From 6115bf6c0fac8338be1e87396b4946695c2ea96f Mon Sep 17 00:00:00 2001 From: George Weiler Date: Tue, 8 Jul 2025 14:14:01 -0600 Subject: [PATCH 1/2] feat: fixes date input on android. adds autofill ux improvements --- .../Deposit/Views/BasicInfo/BasicInfo.tsx | 43 ++++++++++++++----- .../__snapshots__/BasicInfo.test.tsx.snap | 18 ++++++++ .../DepositDateField/DepositDateField.tsx | 11 ++++- .../DepositPhoneField/DepositPhoneField.tsx | 1 + .../DepositPhoneField.test.tsx.snap | 10 +++++ 5 files changed, 72 insertions(+), 11 deletions(-) diff --git a/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx b/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx index ad00cd30a542..d9aa252fa8c8 100644 --- a/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx +++ b/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx @@ -127,13 +127,27 @@ const BasicInfo = (): JSX.Element => { } }, [navigation, validateFormData, formData, quote, kycUrl]); - const handleSubmitEditing = useCallback( + const focusNextField = useCallback( (nextRef: React.RefObject) => () => { nextRef.current?.focus(); }, [], ); + const handleFieldChange = useCallback( + (field: keyof BasicInfoFormData, nextAction?: () => void) => (value: string) => { + const currentValue = formData[field]; + const isAutofill = value.length - currentValue.length > 1; + + handleFormDataChange(field)(value); + + if (isAutofill && nextAction) { + nextAction(); + } + }, + [formData, handleFormDataChange], + ); + return ( @@ -152,7 +166,7 @@ const BasicInfo = (): JSX.Element => { label={strings('deposit.basic_info.first_name')} placeholder="John" value={formData.firstName} - onChangeText={handleFormDataChange('firstName')} + onChangeText={handleFieldChange('firstName', focusNextField(lastNameInputRef))} error={errors.firstName} returnKeyType="next" testID="first-name-input" @@ -161,14 +175,14 @@ const BasicInfo = (): JSX.Element => { autoComplete="given-name" textContentType="givenName" autoCapitalize="words" - onSubmitEditing={handleSubmitEditing(lastNameInputRef)} + onSubmitEditing={focusNextField(lastNameInputRef)} /> { autoComplete="family-name" textContentType="familyName" autoCapitalize="words" - onSubmitEditing={handleSubmitEditing(phoneInputRef)} + onSubmitEditing={focusNextField(phoneInputRef)} /> { + if (selectedRegion?.isoCode === 'US') { + focusNextField(ssnInputRef)(); + } else { + Keyboard.dismiss(); + } + })} error={errors.dob} onSubmitEditing={() => { if (selectedRegion?.isoCode === 'US') { - handleSubmitEditing(ssnInputRef)(); + focusNextField(ssnInputRef)(); } else { Keyboard.dismiss(); } @@ -212,7 +232,10 @@ const BasicInfo = (): JSX.Element => { label={strings('deposit.basic_info.social_security_number')} placeholder="XXX-XX-XXXX" value={formData.ssn} - onChangeText={handleFormDataChange('ssn')} + onChangeText={handleFieldChange('ssn', () => { + Keyboard.dismiss(); + handleOnPressContinue(); + })} error={errors.ssn} returnKeyType="done" testID="ssn-input" diff --git a/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap b/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap index 59b74a6e84a2..d601198ff3a5 100644 --- a/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap +++ b/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap @@ -908,6 +908,7 @@ exports[`BasicInfo Component navigates to address page when form is valid and co } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="(234) 567-890" /> @@ -1139,6 +1140,21 @@ exports[`BasicInfo Component navigates to address page when form is valid and co /> + + Social security number is required + @@ -2219,6 +2235,7 @@ exports[`BasicInfo Component render matches snapshot 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -3560,6 +3577,7 @@ exports[`BasicInfo Component snapshot matches validation errors when continue is } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> diff --git a/app/components/UI/Ramp/Deposit/components/DepositDateField/DepositDateField.tsx b/app/components/UI/Ramp/Deposit/components/DepositDateField/DepositDateField.tsx index 0619f7c7e46e..7be5f33f1e7d 100644 --- a/app/components/UI/Ramp/Deposit/components/DepositDateField/DepositDateField.tsx +++ b/app/components/UI/Ramp/Deposit/components/DepositDateField/DepositDateField.tsx @@ -35,6 +35,15 @@ const formatDateForValue = (date: Date): string => { }; const getValidDate = (dateString: string): Date => { + const dateRegex = /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/; + const match = dateString.match(dateRegex); + + if (match) { + const [, month, day, year] = match; + const date = new Date(parseInt(year), parseInt(month) - 1, parseInt(day)); + return isNaN(date.getTime()) ? DEFAULT_DATE : date; + } + const date = new Date(dateString); return isNaN(date.getTime()) ? DEFAULT_DATE : date; }; @@ -97,7 +106,6 @@ const DepositDateField = forwardRef( ) => { const { styles, theme } = useStyles(styleSheet, {}); const [showDatePicker, setShowDatePicker] = useState(false); - // staging state for iOS date selection const [pendingDateSelection, setPendingDateSelection] = useState(null); const fieldRef = useRef(null); @@ -120,6 +128,7 @@ const DepositDateField = forwardRef( const preventModalDismissal = () => { // Prevents touch events from bubbling up to the outer TouchableWithoutFeedback + // This is a workaround to prevent the modal from being dismissed when the user taps on the date picker }; return ( diff --git a/app/components/UI/Ramp/Deposit/components/DepositPhoneField/DepositPhoneField.tsx b/app/components/UI/Ramp/Deposit/components/DepositPhoneField/DepositPhoneField.tsx index 231b8e673af5..20dbee42fdda 100644 --- a/app/components/UI/Ramp/Deposit/components/DepositPhoneField/DepositPhoneField.tsx +++ b/app/components/UI/Ramp/Deposit/components/DepositPhoneField/DepositPhoneField.tsx @@ -113,6 +113,7 @@ const DepositPhoneField = forwardRef( strings('deposit.basic_info.enter_phone_number') } keyboardType="phone-pad" + textContentType="telephoneNumber" startAccessory={countryPrefixAccessory} onSubmitEditing={onSubmitEditing} ref={ref} diff --git a/app/components/UI/Ramp/Deposit/components/DepositPhoneField/__snapshots__/DepositPhoneField.test.tsx.snap b/app/components/UI/Ramp/Deposit/components/DepositPhoneField/__snapshots__/DepositPhoneField.test.tsx.snap index 07634c57d05a..0e07e22d3a1d 100644 --- a/app/components/UI/Ramp/Deposit/components/DepositPhoneField/__snapshots__/DepositPhoneField.test.tsx.snap +++ b/app/components/UI/Ramp/Deposit/components/DepositPhoneField/__snapshots__/DepositPhoneField.test.tsx.snap @@ -127,6 +127,7 @@ exports[`DepositPhoneField renders correctly after flag button press 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -261,6 +262,7 @@ exports[`DepositPhoneField renders correctly after input change 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -395,6 +397,7 @@ exports[`DepositPhoneField renders correctly with default props 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -529,6 +532,7 @@ exports[`DepositPhoneField renders correctly with different region 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -663,6 +667,7 @@ exports[`DepositPhoneField renders correctly with error message 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -812,6 +817,7 @@ exports[`DepositPhoneField renders correctly with long phone number 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="155512345678901234" /> @@ -947,6 +953,7 @@ exports[`DepositPhoneField renders correctly with onSubmitEditing callback 1`] = } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -1081,6 +1088,7 @@ exports[`DepositPhoneField renders correctly with special characters in phone nu } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="15551234567" /> @@ -1215,6 +1223,7 @@ exports[`DepositPhoneField renders correctly with unsupported region 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="" /> @@ -1349,6 +1358,7 @@ exports[`DepositPhoneField renders correctly with value 1`] = ` } } testID="deposit-phone-field-test-id" + textContentType="telephoneNumber" value="(555) 123-4567" /> From 4148288558353aaf200184334d7783f24c394ebc Mon Sep 17 00:00:00 2001 From: George Weiler Date: Wed, 9 Jul 2025 07:55:56 -0600 Subject: [PATCH 2/2] feat: fixes ssn submission bug --- .../Views/BasicInfo/BasicInfo.test.tsx | 1 + .../Deposit/Views/BasicInfo/BasicInfo.tsx | 38 +++++++++++-------- .../__snapshots__/BasicInfo.test.tsx.snap | 15 -------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.test.tsx b/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.test.tsx index b8f5aa88d112..9d4fa7f8e1ff 100644 --- a/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.test.tsx +++ b/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.test.tsx @@ -99,6 +99,7 @@ describe('BasicInfo Component', () => { screen.getByPlaceholderText('XXX-XX-XXXX'), '123456789', ); + fireEvent.changeText(screen.getByTestId('ssn-input'), '123456789'); expect(screen.toJSON()).toMatchSnapshot(); fireEvent.press(screen.getByRole('button', { name: 'Continue' })); diff --git a/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx b/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx index d9aa252fa8c8..94c8ad6f808e 100644 --- a/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx +++ b/app/components/UI/Ramp/Deposit/Views/BasicInfo/BasicInfo.tsx @@ -135,16 +135,17 @@ const BasicInfo = (): JSX.Element => { ); const handleFieldChange = useCallback( - (field: keyof BasicInfoFormData, nextAction?: () => void) => (value: string) => { - const currentValue = formData[field]; - const isAutofill = value.length - currentValue.length > 1; + (field: keyof BasicInfoFormData, nextAction?: () => void) => + (value: string) => { + const currentValue = formData[field]; + const isAutofill = value.length - currentValue.length > 1; - handleFormDataChange(field)(value); + handleFormDataChange(field)(value); - if (isAutofill && nextAction) { - nextAction(); - } - }, + if (isAutofill && nextAction) { + nextAction(); + } + }, [formData, handleFormDataChange], ); @@ -166,7 +167,10 @@ const BasicInfo = (): JSX.Element => { label={strings('deposit.basic_info.first_name')} placeholder="John" value={formData.firstName} - onChangeText={handleFieldChange('firstName', focusNextField(lastNameInputRef))} + onChangeText={handleFieldChange( + 'firstName', + focusNextField(lastNameInputRef), + )} error={errors.firstName} returnKeyType="next" testID="first-name-input" @@ -182,7 +186,10 @@ const BasicInfo = (): JSX.Element => { label={strings('deposit.basic_info.last_name')} placeholder="Smith" value={formData.lastName} - onChangeText={handleFieldChange('lastName', focusNextField(phoneInputRef))} + onChangeText={handleFieldChange( + 'lastName', + focusNextField(phoneInputRef), + )} error={errors.lastName} returnKeyType="next" testID="last-name-input" @@ -198,7 +205,10 @@ const BasicInfo = (): JSX.Element => { { label={strings('deposit.basic_info.social_security_number')} placeholder="XXX-XX-XXXX" value={formData.ssn} - onChangeText={handleFieldChange('ssn', () => { - Keyboard.dismiss(); - handleOnPressContinue(); - })} + onChangeText={handleFieldChange('ssn')} error={errors.ssn} returnKeyType="done" testID="ssn-input" @@ -247,7 +254,6 @@ const BasicInfo = (): JSX.Element => { maxLength={11} onSubmitEditing={() => { Keyboard.dismiss(); - handleOnPressContinue(); }} /> )} diff --git a/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap b/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap index d601198ff3a5..b494a8eee356 100644 --- a/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap +++ b/app/components/UI/Ramp/Deposit/Views/BasicInfo/__snapshots__/BasicInfo.test.tsx.snap @@ -1140,21 +1140,6 @@ exports[`BasicInfo Component navigates to address page when form is valid and co /> - - Social security number is required -