Skip to content

Commit

Permalink
fix: amount input field to properly interpret BTC (#800)
Browse files Browse the repository at this point in the history
  • Loading branch information
amitx13 authored Aug 17, 2024
1 parent ead874a commit 072a419
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 65 deletions.
21 changes: 21 additions & 0 deletions src/components/BitcoinAmountInput.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,25 @@ describe('<BitcoinAmountInput />', () => {
expect(inputElement.dataset.displayUnit).toBe(undefined)
expect(inputElement.dataset.displayValue).toBe('')
})

it('amount 1.0 should be interpreted as 1 BTC', async () => {
setup({
label: 'test-label',
})
const inputElement = screen.getByLabelText('test-label')

await user.type(inputElement, '1.0')

expect(inputElement).toHaveFocus()
expect(inputElement.dataset.value).toBe('100000000')
expect(inputElement.dataset.displayUnit).toBe('BTC')
expect(inputElement.dataset.displayValue).toBe(`1.0`)

await user.tab()

expect(inputElement).not.toHaveFocus()
expect(inputElement.dataset.value).toBe('100000000')
expect(inputElement.dataset.displayUnit).toBe('BTC')
expect(inputElement.dataset.displayValue).toBe(`1.00 000 000`)
})
})
139 changes: 74 additions & 65 deletions src/components/BitcoinAmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,78 @@ const BitcoinAmountInput = forwardRef(
: undefined
}, [field, inputType])

const handleBlur = (e: React.FocusEvent<HTMLInputElement>) => {
setInputType({
type: 'text',
inputMode: 'decimal',
})

let displayValue = String(field.value?.value || '')
if (isValidNumber(field.value?.value)) {
displayValue = formatBtcDisplayValue(field.value!.value!)
}

form.setFieldValue(
field.name,
{
...field.value,
displayValue,
},
false,
)
field.onBlur(e)
}

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const rawUserInputOrEmpty = e.target.value ?? ''
const validNumberRegex = /^-?\d*\.?\d*$/
if (!validNumberRegex.test(rawUserInputOrEmpty)) {
return
}
const floatValueOrNan = parseFloat(rawUserInputOrEmpty)
if (!isValidNumber(floatValueOrNan)) {
form.setFieldValue(
field.name,
{
...field.value,
value: null,
userRawInputValue: e.target.value,
displayValue: e.target.value,
},
true,
)
return
} else {
const value: number = floatValueOrNan
let numberValues: string | undefined
const unit =
rawUserInputOrEmpty.includes('.') && parseFloat(rawUserInputOrEmpty)
? unitFromValue(String(rawUserInputOrEmpty))
: unitFromValue(String(value))
if (unit === 'BTC') {
const splitted = String(value).split('.')
const [integerPart, fractionalPart = ''] = splitted
const paddedFractionalPart = fractionalPart.padEnd(8, '0').substring(0, 8)
numberValues = `${integerPart}${paddedFractionalPart}`
} else {
numberValues = value.toLocaleString('en-US', {
maximumFractionDigits: 0,
useGrouping: false,
})
}

form.setFieldValue(
field.name,
{
value: parseInt(numberValues, 10),
userRawInputValue: e.target.value,
displayValue: e.target.value,
},
true,
)
}
}

return (
<>
<rb.InputGroup hasValidation={true}>
Expand All @@ -77,7 +149,6 @@ const BitcoinAmountInput = forwardRef(
data-display-value={field.value?.displayValue}
name={field.name}
autoComplete="off"
type={inputType.type}
inputMode={inputType.inputMode}
className={classNames('slashed-zeroes', className)}
value={
Expand All @@ -92,70 +163,8 @@ const BitcoinAmountInput = forwardRef(
onFocus={() => {
setInputType({ type: 'number' })
}}
onBlur={(e) => {
setInputType({
type: 'text',
inputMode: 'decimal',
})

let displayValue = String(field.value?.value || '')
if (isValidNumber(field.value?.value)) {
displayValue = formatBtcDisplayValue(field.value!.value!)
}

form.setFieldValue(
field.name,
{
...field.value,
displayValue,
},
false,
)
field.onBlur(e)
}}
onChange={(e) => {
const valueOrNan = parseFloat(e.target.value ?? '')

if (!isValidNumber(valueOrNan)) {
form.setFieldValue(
field.name,
{
...field.value,
value: null,
userRawInputValue: e.target.value,
displayValue: e.target.value,
},
true,
)
return
} else {
const value: number = valueOrNan

let numberValues: string | undefined
const unit = unitFromValue(String(value))
if (unit === 'BTC') {
const splitted = String(value).split('.')
const [integerPart, fractionalPart = ''] = splitted
const paddedFractionalPart = fractionalPart.padEnd(8, '0').substring(0, 8)
numberValues = `${integerPart}${paddedFractionalPart}`
} else {
numberValues = value.toLocaleString('en-US', {
maximumFractionDigits: 0,
useGrouping: false,
})
}

form.setFieldValue(
field.name,
{
value: parseInt(numberValues, 10),
userRawInputValue: e.target.value,
displayValue: e.target.value,
},
true,
)
}
}}
onBlur={handleBlur}
onChange={handleChange}
/>
{children}
<rb.Form.Control.Feedback type="invalid">
Expand Down

0 comments on commit 072a419

Please sign in to comment.