Skip to content

Commit

Permalink
Suisin/sign up country code dropdown (#18058)
Browse files Browse the repository at this point in the history
* chore: implement country code dropdown for real account sign up

* chore: fix test case failing issue

* chore: revert async await from personal-details-form test case

* chore: fix test case failing for poi-confirm-with-example-form-container

* chore: make is_country_code_dropdown_enabled boolean to false first

* chore: insert growthbook feature value for country code dropdown

* chore: put calling country code as default

* chore: empty commit

* chore: fix unable to submit issue

* chore: remove default country code values
  • Loading branch information
suisin-deriv authored Feb 20, 2025
1 parent a53b824 commit c9afb54
Show file tree
Hide file tree
Showing 11 changed files with 289 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ import userEvent from '@testing-library/user-event';

import PersonalDetailsForm from '../personal-details-form';
import { APIProvider } from '@deriv/api';
import { useGetPhoneNumberList } from '@deriv/hooks';

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
Link: () => <div>Mocked Link Component</div>,
}));

jest.mock('@deriv/hooks', () => ({
...jest.requireActual('@deriv/hooks'),
useGetPhoneNumberList: jest.fn(),
}));

describe('PersonalDetailsForm', () => {
const mock_props = {
editable_fields: ['salutation'],
Expand All @@ -20,6 +26,14 @@ describe('PersonalDetailsForm', () => {
],
};

beforeEach(() => {
(useGetPhoneNumberList as jest.Mock).mockReturnValue({
legacy_core_countries_list: [
{ text: 'United States (+1)', value: '+1', id: '1_US', carriers: [], disabled: false },
],
});
});

const renderComponent = () => {
render(
<APIProvider>
Expand All @@ -37,7 +51,7 @@ describe('PersonalDetailsForm', () => {
expect(screen.getByRole('radio', { name: 'Ms' })).toBeInTheDocument();
});

it('should select the respective salutation when radio button is clicked', () => {
it('should select the respective salutation when radio button is clicked', async () => {
renderComponent();

const mr_radio_input = screen.getByRole('radio', { name: 'Mr' });
Expand All @@ -46,11 +60,11 @@ describe('PersonalDetailsForm', () => {
expect(mr_radio_input).not.toBeChecked();
expect(ms_radio_input).not.toBeChecked();

userEvent.click(mr_radio_input);
await userEvent.click(mr_radio_input);
expect(mr_radio_input).toBeChecked();
expect(ms_radio_input).not.toBeChecked();

userEvent.click(ms_radio_input);
await userEvent.click(ms_radio_input);
expect(mr_radio_input).not.toBeChecked();
expect(ms_radio_input).toBeChecked();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ const FormInputField = ({ name, warn, ...rest }: FormInputFieldProps) => (
autoComplete='off'
error={touched[field.name] && errors[field.name] ? errors[field.name] : undefined}
warn={warn}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
!touched[field.name] && setFieldTouched(field.name);
field.onChange(e);
}}
onChange={
rest.onChange ||
((e: React.ChangeEvent<HTMLInputElement>) => {
!touched[field.name] && setFieldTouched(field.name);
field.onChange(e);
})
}
/>
);
}}
Expand Down
128 changes: 113 additions & 15 deletions packages/account/src/Components/forms/personal-details-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@ import React from 'react';
import { Link } from 'react-router-dom';
import clsx from 'clsx';
import { Field, useFormikContext } from 'formik';

import { Autocomplete, Checkbox, InlineMessage, RadioGroup, SelectNative, Text } from '@deriv/components';
import { useGetPhoneNumberList, useGrowthbookGetFeatureValue, useResidenceList } from '@deriv/hooks';
import { routes, validPhone } from '@deriv/shared';
import { Localize, localize } from '@deriv/translations';
import { useDevice } from '@deriv-com/ui';

import { isFieldImmutable, verifyFields } from '../../Helpers/utils';
import FormBodySection from '../form-body-section';
import { DateOfBirthField, FormInputField } from './form-fields';
import FormSubHeader from '../form-sub-header';
import InlineNoteWithIcon from '../inline-note-with-icon';
import { useResidenceList } from '@deriv/hooks';
import { useDevice } from '@deriv-com/ui';

import AccountOpeningReasonField from './form-fields/account-opening-reason';
import { DateOfBirthField, FormInputField } from './form-fields';

const PersonalDetailsForm = props => {
const { isDesktop } = useDevice();
Expand Down Expand Up @@ -40,10 +43,16 @@ const PersonalDetailsForm = props => {
// need to put this check related to DIEL clients
const is_svg_only = is_svg && !is_eu_user;

const [isCountryCodeDropdownEnabled, isCountryCodeLoaded] = useGrowthbookGetFeatureValue({
featureFlag: 'enable_country_code_dropdown',
});

const { errors, touched, values, setFieldValue, handleChange, handleBlur } = useFormikContext();

const { data: residence_list } = useResidenceList();

const { legacy_core_countries_list } = useGetPhoneNumberList();

const getNameAndDobLabels = () => {
const is_asterisk_needed = is_svg || is_eu_user || is_rendered_for_onfido || is_rendered_for_idv;
const first_name_label = is_asterisk_needed ? localize('First name*') : localize('First name');
Expand Down Expand Up @@ -381,6 +390,10 @@ const PersonalDetailsForm = props => {
)}
{!is_svg_only && 'phone' in values && (
<PhoneField
is_country_code_dropdown_enabled={isCountryCodeLoaded && isCountryCodeDropdownEnabled}
handleChange={handleChange}
setFieldValue={setFieldValue}
country_code_list={legacy_core_countries_list}
value={values.phone}
editable_fields={editable_fields}
has_real_account={has_real_account}
Expand Down Expand Up @@ -421,6 +434,10 @@ const PersonalDetailsForm = props => {
<FormSubHeader title={localize('Additional information')} />
{'phone' in values && (
<PhoneField
is_country_code_dropdown_enabled={isCountryCodeLoaded && isCountryCodeDropdownEnabled}
handleChange={handleChange}
setFieldValue={setFieldValue}
country_code_list={legacy_core_countries_list}
value={values.phone}
editable_fields={editable_fields}
has_real_account={has_real_account}
Expand Down Expand Up @@ -458,20 +475,101 @@ const PersonalDetailsForm = props => {

export default PersonalDetailsForm;

const PhoneField = ({ value, editable_fields, has_real_account, required }) => (
<FormInputField
name='phone'
label={required ? localize('Phone number*') : localize('Phone number')}
placeholder={required ? localize('Phone number*') : localize('Phone number')}
disabled={
isFieldImmutable('phone', editable_fields) ||
(value && has_real_account && validPhone(value) && value?.length >= 9 && value?.length <= 35)
}
maxLength={50}
data-testid='phone'
/>
const PhoneField = ({
handleChange,
setFieldValue,
country_code_list,
value,
editable_fields,
has_real_account,
required,
is_country_code_dropdown_enabled,
}) => (
<React.Fragment>
<div className='account-form__phone-container'>
{is_country_code_dropdown_enabled && (
<CountryCodeDropdown
handleChange={handleChange}
setFieldValue={setFieldValue}
disabled={
isFieldImmutable('phone', editable_fields) ||
(value && has_real_account && validPhone(value) && value?.length >= 9 && value?.length <= 35)
}
country_code_list={country_code_list}
required
/>
)}
<FormInputField
className='account-form__phone-container--input'
name='phone'
label={required ? localize('Phone number*') : localize('Phone number')}
placeholder={required ? localize('Phone number*') : localize('Phone number')}
disabled={
isFieldImmutable('phone', editable_fields) ||
(value && has_real_account && validPhone(value) && value?.length >= 9 && value?.length <= 35)
}
{...(is_country_code_dropdown_enabled && {
onChange: e => {
const phone_number = e.target.value.replace(/\D/g, '');
setFieldValue('phone', phone_number, true);
},
})}
maxLength={50}
data-testid='phone'
/>
</div>
</React.Fragment>
);

const CountryCodeDropdown = ({ handleChange, setFieldValue, disabled, country_code_list, required }) => {
const { isDesktop } = useDevice();
return (
<Field name='calling_country_code'>
{({ field, meta }) => (
<React.Fragment>
{isDesktop ? (
<Autocomplete
{...field}
disabled={disabled}
data-lpignore='true'
autoComplete='new-password' // prevent chrome autocomplete
label={required ? localize('Code*') : localize('Code')}
error={meta.touched && meta.error}
list_items={country_code_list}
onItemSelection={country_list => {
setFieldValue('calling_country_code', country_list.value, true);
}}
required
data-testid='calling_country_code'
/>
) : (
<SelectNative
placeholder={required ? localize('Code*') : localize('Code')}
name={field.name}
disabled={disabled}
label={required ? localize('Code*') : localize('Code')}
list_items={country_code_list}
value={field.value}
use_text
error={meta.touched && meta.error}
onChange={e => {
handleChange(e);
setFieldValue('calling_country_code', e.target.value, true);
}}
{...field}
list_portal_id='modal_root'
required
is_country_code_dropdown
should_hide_disabled_options={false}
data_testid='calling_country_code_mobile'
/>
)}
</React.Fragment>
)}
</Field>
);
};

const PlaceOfBirthField = ({ handleChange, setFieldValue, disabled, residence_list, required }) => {
const { isDesktop } = useDevice();
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { FormikErrors } from 'formik';
import { getIDVFormValidationSchema } from '../../../Configs/kyc-validation-config';
import { useDevice } from '@deriv-com/ui';
import { APIProvider } from '@deriv/api';
import { useGrowthbookGetFeatureValue } from '@deriv/hooks';

jest.mock('@deriv-com/ui', () => ({
...jest.requireActual('@deriv-com/ui'),
Expand Down Expand Up @@ -44,6 +45,11 @@ jest.mock('@deriv-com/analytics', () => ({
},
}));

jest.mock('@deriv/hooks', () => ({
...jest.requireActual('@deriv/hooks'),
useGrowthbookGetFeatureValue: jest.fn(),
}));

type TPersonalDetailsSectionForm = ComponentProps<typeof PersonalDetails>['value'];

const mock_warnings = {};
Expand Down Expand Up @@ -266,6 +272,10 @@ describe('<PersonalDetails/>', () => {
real_account_signup_target: '',
};

beforeEach(() => {
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValue([false, false]);
});

afterEach(() => {
jest.clearAllMocks();
});
Expand Down Expand Up @@ -440,6 +450,19 @@ describe('<PersonalDetails/>', () => {
expect(mrs_radio_btn).not.toBeChecked();
});

it('should display country code dropdown when isCountryCodeDropdownEnabled is true ', () => {
(useGrowthbookGetFeatureValue as jest.Mock).mockReturnValue([true, true]);
renderwithRouter({});

expect(screen.getByText(/code\*/i)).toBeInTheDocument();
});

it('should not display country code dropdown when isCountryCodeDropdownEnabled is false ', () => {
renderwithRouter({});

expect(screen.queryByText(/code\*/i)).not.toBeInTheDocument();
});

it('should display the correct field details ', () => {
renderwithRouter({});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { TIDVFormValues, TListItem, TPersonalDetailsBaseForm } from '../../Types
import { GetAccountStatus, GetSettings, ResidenceList } from '@deriv/api-types';
import { getPersonalDetailsBaseValidationSchema } from '../../Configs/user-profile-validation-config';
import { getIDVFormValidationSchema } from '../../Configs/kyc-validation-config';
import { useGrowthbookGetFeatureValue } from '@deriv/hooks';

type TPersonalDetailsSectionForm = Partial<TIDVFormValues & TPersonalDetailsBaseForm> & {
confirmation_checkbox?: boolean;
Expand Down Expand Up @@ -79,6 +80,9 @@ const PersonalDetails = observer(
} = useStore();
const { account_status, account_settings, residence, real_account_signup_target } = props;

const [isCountryCodeDropdownEnabled, isCountryCodeLoaded] = useGrowthbookGetFeatureValue({
featureFlag: 'enable_country_code_dropdown',
});
const { isDesktop } = useDevice();
const handleCancel = (values: TPersonalDetailsSectionForm) => {
const current_step = getCurrentStep() - 1;
Expand Down Expand Up @@ -124,11 +128,15 @@ const PersonalDetails = observer(
const schema = useMemo(
() =>
is_rendered_for_idv
? getPersonalDetailsBaseValidationSchema(real_account_signup_target).concat(
getIDVFormValidationSchema()
)
: getPersonalDetailsBaseValidationSchema(real_account_signup_target),
[is_rendered_for_idv, real_account_signup_target]
? getPersonalDetailsBaseValidationSchema(
real_account_signup_target,
isCountryCodeLoaded && isCountryCodeDropdownEnabled
).concat(getIDVFormValidationSchema())
: getPersonalDetailsBaseValidationSchema(
real_account_signup_target,
isCountryCodeLoaded && isCountryCodeDropdownEnabled
),
[is_rendered_for_idv, real_account_signup_target, isCountryCodeLoaded, isCountryCodeDropdownEnabled]
);

/*
Expand Down
Loading

0 comments on commit c9afb54

Please sign in to comment.