mirror of
https://github.com/joinmarket-webui/jam.git
synced 2026-08-19 13:18:30 +02:00
fix: amount input field to properly interpret BTC (#800)
This commit is contained in:
parent
ead874a366
commit
072a41950d
2 changed files with 95 additions and 65 deletions
|
|
@ -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`)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
@ -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={
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue