From 0915ac505f13eed45c523977927e996c48441a1f Mon Sep 17 00:00:00 2001 From: amitx13 Date: Fri, 19 Jul 2024 10:45:27 +0530 Subject: [PATCH 1/5] fixed amount input field to properly interpret BTC --- src/components/BitcoinAmountInput.tsx | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/components/BitcoinAmountInput.tsx b/src/components/BitcoinAmountInput.tsx index bca70df88..04f1c9daa 100644 --- a/src/components/BitcoinAmountInput.tsx +++ b/src/components/BitcoinAmountInput.tsx @@ -114,9 +114,9 @@ const BitcoinAmountInput = forwardRef( field.onBlur(e) }} onChange={(e) => { - const valueOrNan = parseFloat(e.target.value ?? '') - - if (!isValidNumber(valueOrNan)) { + const valueOrNan = e.target.value ?? '' + const floatValueOrNan = parseFloat(valueOrNan) + if (!isValidNumber(floatValueOrNan)) { form.setFieldValue( field.name, { @@ -129,10 +129,12 @@ const BitcoinAmountInput = forwardRef( ) return } else { - const value: number = valueOrNan - + const value: number = floatValueOrNan let numberValues: string | undefined - const unit = unitFromValue(String(value)) + const unit = + valueOrNan.includes('.') && parseFloat(valueOrNan) + ? unitFromValue(String(valueOrNan)) + : unitFromValue(String(value)) if (unit === 'BTC') { const splitted = String(value).split('.') const [integerPart, fractionalPart = ''] = splitted From 247baefdc263023f5db773f1f8a48b5c89bd9e21 Mon Sep 17 00:00:00 2001 From: amitx13 Date: Mon, 22 Jul 2024 08:55:30 +0530 Subject: [PATCH 2/5] Renamed valueOrNan to rawUserInputOrEmpty for clarity --- src/components/BitcoinAmountInput.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/BitcoinAmountInput.tsx b/src/components/BitcoinAmountInput.tsx index 04f1c9daa..4aa1976c9 100644 --- a/src/components/BitcoinAmountInput.tsx +++ b/src/components/BitcoinAmountInput.tsx @@ -114,8 +114,8 @@ const BitcoinAmountInput = forwardRef( field.onBlur(e) }} onChange={(e) => { - const valueOrNan = e.target.value ?? '' - const floatValueOrNan = parseFloat(valueOrNan) + const rawUserInputOrEmpty = e.target.value ?? '' + const floatValueOrNan = parseFloat(rawUserInputOrEmpty) if (!isValidNumber(floatValueOrNan)) { form.setFieldValue( field.name, @@ -132,8 +132,8 @@ const BitcoinAmountInput = forwardRef( const value: number = floatValueOrNan let numberValues: string | undefined const unit = - valueOrNan.includes('.') && parseFloat(valueOrNan) - ? unitFromValue(String(valueOrNan)) + rawUserInputOrEmpty.includes('.') && parseFloat(rawUserInputOrEmpty) + ? unitFromValue(String(rawUserInputOrEmpty)) : unitFromValue(String(value)) if (unit === 'BTC') { const splitted = String(value).split('.') From 6dca2f23bbd129a2151da2b9b132fedd3cdab625 Mon Sep 17 00:00:00 2001 From: amitx13 Date: Wed, 24 Jul 2024 18:55:22 +0530 Subject: [PATCH 3/5] Added test case for BitcoinAmountInput --- src/components/BitcoinAmountInput.test.tsx | 26 +++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/components/BitcoinAmountInput.test.tsx b/src/components/BitcoinAmountInput.test.tsx index aab58e3ae..2044143ac 100644 --- a/src/components/BitcoinAmountInput.test.tsx +++ b/src/components/BitcoinAmountInput.test.tsx @@ -1,5 +1,5 @@ import user from '@testing-library/user-event' -import { render, screen } from '../testUtils' +import { act, fireEvent, render, screen } from '../testUtils' import { noop } from '../utils' @@ -95,4 +95,28 @@ describe('', () => { 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 act(async () => { + fireEvent.change(inputElement, { target: { value: '1.0' } }) + fireEvent.focus(inputElement) + }) + + expect(inputElement.dataset.value).toBe('100000000') + expect(inputElement.dataset.displayUnit).toBe('BTC') + expect(inputElement.dataset.displayValue).toBe(`1.0`) + + await act(async () => { + fireEvent.blur(inputElement) + }) + + expect(inputElement.dataset.value).toBe('100000000') + expect(inputElement.dataset.displayUnit).toBe('BTC') + expect(inputElement.dataset.displayValue).toBe(`1.00 000 000`) + }) }) From 714014a41de4e03c201957967b4143f2465b81ba Mon Sep 17 00:00:00 2001 From: amitx13 Date: Sat, 3 Aug 2024 22:34:46 +0530 Subject: [PATCH 4/5] Enhanced input handling to accept only '.' as decimal separators --- src/components/BitcoinAmountInput.test.tsx | 11 ++++------- src/components/BitcoinAmountInput.tsx | 5 ++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/components/BitcoinAmountInput.test.tsx b/src/components/BitcoinAmountInput.test.tsx index 2044143ac..2f342bf21 100644 --- a/src/components/BitcoinAmountInput.test.tsx +++ b/src/components/BitcoinAmountInput.test.tsx @@ -102,19 +102,16 @@ describe('', () => { }) const inputElement = screen.getByLabelText('test-label') - await act(async () => { - fireEvent.change(inputElement, { target: { value: '1.0' } }) - fireEvent.focus(inputElement) - }) + 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 act(async () => { - fireEvent.blur(inputElement) - }) + 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`) diff --git a/src/components/BitcoinAmountInput.tsx b/src/components/BitcoinAmountInput.tsx index 4aa1976c9..443993a17 100644 --- a/src/components/BitcoinAmountInput.tsx +++ b/src/components/BitcoinAmountInput.tsx @@ -77,7 +77,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={ @@ -115,6 +114,10 @@ const BitcoinAmountInput = forwardRef( }} onChange={(e) => { const rawUserInputOrEmpty = e.target.value ?? '' + const validNumberRegex = /^-?\d*\.?\d*$/ + if (!validNumberRegex.test(rawUserInputOrEmpty)) { + return + } const floatValueOrNan = parseFloat(rawUserInputOrEmpty) if (!isValidNumber(floatValueOrNan)) { form.setFieldValue( From 8eb8d2db7700d9c9571b9bf62ca44e4d03c00814 Mon Sep 17 00:00:00 2001 From: amitx13 Date: Sat, 3 Aug 2024 22:44:10 +0530 Subject: [PATCH 5/5] Refactored BitcoinAmountInput --- src/components/BitcoinAmountInput.test.tsx | 2 +- src/components/BitcoinAmountInput.tsx | 144 +++++++++++---------- 2 files changed, 75 insertions(+), 71 deletions(-) diff --git a/src/components/BitcoinAmountInput.test.tsx b/src/components/BitcoinAmountInput.test.tsx index 2f342bf21..b46b6b06e 100644 --- a/src/components/BitcoinAmountInput.test.tsx +++ b/src/components/BitcoinAmountInput.test.tsx @@ -1,5 +1,5 @@ import user from '@testing-library/user-event' -import { act, fireEvent, render, screen } from '../testUtils' +import { render, screen } from '../testUtils' import { noop } from '../utils' diff --git a/src/components/BitcoinAmountInput.tsx b/src/components/BitcoinAmountInput.tsx index 443993a17..d4f565b01 100644 --- a/src/components/BitcoinAmountInput.tsx +++ b/src/components/BitcoinAmountInput.tsx @@ -61,6 +61,78 @@ const BitcoinAmountInput = forwardRef( : undefined }, [field, inputType]) + const handleBlur = (e: React.FocusEvent) => { + 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) => { + 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 ( <> @@ -91,76 +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 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, - ) - } - }} + onBlur={handleBlur} + onChange={handleChange} /> {children}