diff --git a/.stylelintrc.js b/.stylelintrc.js index 723f092412cc..e610dc6269d2 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -36,7 +36,7 @@ module.exports = { 'shorthand-property-no-redundant-values': true, 'string-no-newline': true, 'time-min-milliseconds': 100, - 'unit-allowed-list': ['fr', 'px', 'em', 'rem', '%', 'vw', 'vh', 'deg', 'ms', 's', 'dpcm'], + 'unit-allowed-list': ['fr', 'px', 'em', 'rem', '%', 'svh', 'vw', 'vh', 'deg', 'ms', 's', 'dpcm'], 'value-keyword-case': 'lower', }, extends: [ diff --git a/packages/account/src/Components/forms/personal-details-form.jsx b/packages/account/src/Components/forms/personal-details-form.jsx index 626fa3aa54d8..00efe40bcfa6 100644 --- a/packages/account/src/Components/forms/personal-details-form.jsx +++ b/packages/account/src/Components/forms/personal-details-form.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Field } from 'formik'; +import { Field, useFormikContext } from 'formik'; import classNames from 'classnames'; import { Autocomplete, @@ -23,17 +23,7 @@ import { Link } from 'react-router-dom'; import { getEmploymentStatusList } from 'Sections/Assessment/FinancialAssessment/financial-information-list'; import { isFieldImmutable } from 'Helpers/utils'; -const PersonalDetailsForm = ({ - errors, - touched, - values, - setFieldValue, - handleChange, - handleBlur, - warning_items, - setFieldTouched, - ...props -}) => { +const PersonalDetailsForm = props => { const { is_virtual, is_mf, @@ -51,6 +41,7 @@ const PersonalDetailsForm = ({ is_rendered_for_onfido, should_close_tooltip, setShouldCloseTooltip, + class_name, } = props; const autocomplete_value = 'none'; const PoiNameDobExampleIcon = PoiNameDobExample; @@ -58,6 +49,8 @@ const PersonalDetailsForm = ({ const [is_tax_residence_popover_open, setIsTaxResidencePopoverOpen] = React.useState(false); const [is_tin_popover_open, setIsTinPopoverOpen] = React.useState(false); + const { errors, touched, values, setFieldValue, handleChange, handleBlur, setFieldTouched } = useFormikContext(); + React.useEffect(() => { if (should_close_tooltip) { handleToolTipStatus(); @@ -108,474 +101,596 @@ const PersonalDetailsForm = ({ /> ); + // need to put this check related to DIEL clients + const is_svg_only = is_svg && !is_mf; + return ( -
- {(is_qualified_for_idv || is_rendered_for_onfido) && !should_hide_helper_image && ( - - )} - } + +
-
- {'salutation' in values && ( -
- - {is_virtual ? ( - localize( - 'Please remember that it is your responsibility to keep your answers accurate and up to date. You can update your personal details at any time in your account settings.' - ) - ) : ( - , - ]} - /> - )} - -
- )} - {!is_qualified_for_idv && !is_appstore && !is_rendered_for_onfido && ( - - )} - {'salutation' in values && ( - { - e.persist(); - setFieldValue('salutation', e.target.value); - }} - required - > - {salutation_list.map(item => ( - - ))} - - )} - {'first_name' in values && ( - - )} - {'last_name' in values && ( - - )} - {!is_appstore && !is_qualified_for_idv && !is_rendered_for_onfido && ( - - )} - {'date_of_birth' in values && ( - - )} - {'place_of_birth' in values && ( - - {({ field }) => ( - - - - setFieldValue('place_of_birth', value ? text : '', true) - } - required - data-testid='place_of_birth' - /> - - - { - handleChange(e); - setFieldValue('place_of_birth', e.target.value, true); - }} - {...field} - list_portal_id='modal_root' - required - should_hide_disabled_options={false} - data_testid='place_of_birth_mobile' - /> - - - )} - - )} - {'citizen' in values && ( - - {({ field }) => ( - - - - setFieldValue('citizen', value ? text : '', true) - } - list_portal_id='modal_root' - required - data-testid='citizenship' - /> - - - { - handleChange(e); - setFieldValue('citizen', e.target.value, true); - }} - {...field} - required - should_hide_disabled_options={false} - data_testid='citizenship_mobile' + {(is_qualified_for_idv || is_rendered_for_onfido) && !should_hide_helper_image && ( + + )} + } + > +
+ {'salutation' in values && ( +
+ + {is_virtual ? ( + localize( + 'Please remember that it is your responsibility to keep your answers accurate and up to date. You can update your personal details at any time in your account settings.' + ) + ) : ( + , + ]} /> - - - )} - - )} - {'phone' in values && ( - = 9 && - values?.phone?.length <= 35) - } - maxLength={50} - data-testid='phone' - /> - )} - {('tax_residence' in values || 'tax_identification_number' in values) && ( - - - {'tax_residence' in values && ( - - {({ field }) => ( -
- - - setFieldValue('tax_residence', value ? text : '', true) - } - list_portal_id='modal_root' - data-testid='tax_residence' - disabled={isFieldImmutable('tax_residence', editable_fields)} - /> - - - { - handleChange(e); - setFieldValue('tax_residence', e.target.value, true); - }} - {...field} - required - data_testid='tax_residence_mobile' - disabled={isFieldImmutable('tax_residence', editable_fields)} - /> - -
{ - setIsTaxResidencePopoverOpen(true); - setIsTinPopoverOpen(false); - e.stopPropagation(); - }} - > - -
-
)} -
- )} - {'tax_identification_number' in values && ( -
- +
+ )} + {!is_qualified_for_idv && !is_appstore && !is_rendered_for_onfido && ( + + )} + {'salutation' in values && ( + { + e.persist(); + setFieldValue('salutation', e.target.value); + }} + required + > + {salutation_list.map(item => ( + -
{ - setIsTaxResidencePopoverOpen(false); - setIsTinPopoverOpen(true); - if (e.target.tagName !== 'A') e.stopPropagation(); - }} - > - here to learn more." - } - components={[ - , - ]} - /> - } - zIndex={9998} - disable_message_icon - /> -
-
- )} - {warning_items?.tax_identification_number && ( -
- )} - {'employment_status' in values && ( -
- - - - - { - setFieldTouched('employment_status', true); - handleChange(e); - }} - disabled={isFieldImmutable('employment_status', editable_fields)} - /> - -
- )} - {'tax_identification_confirm' in values && ( - - setFieldValue( - 'tax_identification_confirm', - !values.tax_identification_confirm, - true - ) - } - value={values.tax_identification_confirm} - label={localize( - 'I hereby confirm that the tax information I provided is true and complete. I will also inform {{legal_entity_name}} about any changes to this information.', - { - legal_entity_name: getLegalEntityName('maltainvest'), - } - )} - renderlabel={title => ( - - {title} - - )} - withTabIndex={0} - data-testid='tax_identification_confirm' - /> - )} - - )} - {'account_opening_reason' in values && ( - - - + ))} + + )} + {'first_name' in values && ( + + )} + {'last_name' in values && ( + + )} + {!is_appstore && !is_qualified_for_idv && !is_rendered_for_onfido && ( + + )} + {'date_of_birth' in values && ( + + )} + {!is_svg_only && 'place_of_birth' in values && ( + + )} + {'citizen' in values && ( + {({ field }) => ( + + + setFieldValue('citizen', value ? text : '', true) + } + list_portal_id='modal_root' + required + data-testid='citizenship' + /> + + + { + handleChange(e); + setFieldValue('citizen', e.target.value, true); + }} + {...field} + required + should_hide_disabled_options={false} + data_testid='citizenship_mobile' + /> + + + )} + + )} + {!is_svg_only && 'phone' in values && ( + + )} + {!is_svg_only && ('tax_residence' in values || 'tax_identification_number' in values) && ( + + + {'tax_residence' in values && ( + + )} + {'tax_identification_number' in values && ( + + )} + {'employment_status' in values && ( +
{ + setFieldTouched('employment_status', true); handleChange(e); - setFieldValue('account_opening_reason', e.target.value, true); }} - {...field} - required - data_testid='account_opening_reason_mobile' - disabled={isFieldImmutable('account_opening_reason', editable_fields)} + disabled={isFieldImmutable('employment_status', editable_fields)} /> - +
)} -
-
+ {'tax_identification_confirm' in values && ( + + setFieldValue( + 'tax_identification_confirm', + !values.tax_identification_confirm, + true + ) + } + value={values.tax_identification_confirm} + label={localize( + 'I hereby confirm that the tax information I provided is true and complete. I will also inform {{legal_entity_name}} about any changes to this information.', + { + legal_entity_name: getLegalEntityName('maltainvest'), + } + )} + renderlabel={title => ( + + {title} + + )} + withTabIndex={0} + data-testid='tax_identification_confirm' + /> + )} + + )} + {!is_svg_only && 'account_opening_reason' in values && ( + + )} +
+
+
+ + {is_svg_only && ( +
+ + {'phone' in values && ( + )} - - -
+ + {'place_of_birth' in values && ( + + )} + {'tax_residence' in values && ( + + )} + {'tax_identification_number' in values && ( + + )} + {'account_opening_reason' in values && ( + + )} + +
+ )} + ); }; export default PersonalDetailsForm; + +const PhoneField = ({ value, editable_fields, has_real_account, required }) => ( + = 9 && value?.length <= 35) + } + maxLength={50} + data-testid='phone' + /> +); + +const PlaceOfBirthField = ({ handleChange, setFieldValue, disabled, residence_list, required }) => ( + + {({ field, meta }) => ( + + + setFieldValue('place_of_birth', value ? text : '', true)} + required + data-testid='place_of_birth' + /> + + + { + handleChange(e); + setFieldValue('place_of_birth', e.target.value, true); + }} + {...field} + list_portal_id='modal_root' + required + should_hide_disabled_options={false} + data_testid='place_of_birth_mobile' + /> + + + )} + +); + +const TaxResidenceField = ({ + setFieldValue, + residence_list, + required, + setIsTaxResidencePopoverOpen, + setIsTinPopoverOpen, + is_tax_residence_popover_open, + disabled, +}) => ( + + {({ field, meta }) => ( +
+ + setFieldValue('tax_residence', value ? text : '', true)} + list_portal_id='modal_root' + data-testid='tax_residence' + disabled={disabled} + /> + + + { + field.onChange(e); + setFieldValue('tax_residence', e.target.value, true); + }} + {...field} + required + data_testid='tax_residence_mobile' + disabled={disabled} + /> + +
{ + setIsTaxResidencePopoverOpen(true); + setIsTinPopoverOpen(false); + e.stopPropagation(); + }} + > + +
+
+ )} +
+); + +const TaxIdentificationNumberField = ({ + is_tin_popover_open, + setIsTinPopoverOpen, + setIsTaxResidencePopoverOpen, + disabled, + required, +}) => ( +
+ +
{ + setIsTaxResidencePopoverOpen(false); + setIsTinPopoverOpen(true); + if (e.target.tagName !== 'A') e.stopPropagation(); + }} + > + here to learn more." + } + components={[ + , + ]} + /> + } + zIndex={9998} + disable_message_icon + /> +
+
+); + +const AccountOpeningReasonField = ({ no_header, required, account_opening_reason_list, setFieldValue, disabled }) => ( + + {!no_header && } + + {({ field, meta }) => ( + + + + + + { + field.onChange(e); + setFieldValue('account_opening_reason', e.target.value, true); + }} + {...field} + required + data_testid='account_opening_reason_mobile' + disabled={disabled} + /> + + + )} + + +); diff --git a/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js b/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js index 648863ac3325..6d2a73239246 100644 --- a/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js +++ b/packages/account/src/Components/personal-details/__tests__/personal-details.spec.js @@ -48,7 +48,7 @@ const tax_residence_pop_over_text = /the country in which you meet the criteria for paying taxes\. usually the country in which you physically reside\./i; const tin_pop_over_text = /don't know your tax identification number\?/i; -const runCommonFormfieldsTests = () => { +const runCommonFormfieldsTests = is_svg => { expect(screen.getByRole('radio', { name: /mr/i })).toBeInTheDocument(); expect(screen.getByRole('radio', { name: /ms/i })).toBeInTheDocument(); expect(screen.getByTestId('first_name')).toBeInTheDocument(); @@ -74,10 +74,6 @@ const runCommonFormfieldsTests = () => { screen.getByText(/Please enter your date of birth as in your official identity documents./i) ).toBeInTheDocument(); - expect(screen.getByText('Place of birth')).toBeInTheDocument(); - expect(screen.getByText('Citizenship')).toBeInTheDocument(); - expect(screen.getByText('Tax residence')).toBeInTheDocument(); - const tax_residence_pop_over = screen.queryByTestId('tax_residence_pop_over'); expect(tax_residence_pop_over).toBeInTheDocument(); @@ -98,7 +94,12 @@ const runCommonFormfieldsTests = () => { 'https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/' ); - expect(screen.getByRole('heading', { name: /account opening reason/i })).toBeInTheDocument(); + if (is_svg) + expect( + screen.getByRole('heading', { + name: /additional information/i, + }) + ).toBeInTheDocument(); expect(screen.queryByTestId('dti_dropdown_display')).toBeInTheDocument(); expect(screen.queryByTestId('account_opening_reason_mobile')).not.toBeInTheDocument(); expect(screen.getByRole('button', { name: /previous/i })).toBeInTheDocument(); @@ -108,6 +109,7 @@ const runCommonFormfieldsTests = () => { describe('', () => { const props = { is_svg: true, + is_high_risk: false, account_opening_reason_list: [ { text: 'Hedging', @@ -239,6 +241,24 @@ describe('', () => { render({component}); }; + it('should autopopulate tax_residence for MF clients', () => { + const new_props = { + ...props, + is_svg: false, + is_mf: true, + value: { + ...props.value, + tax_residence: 'Malta', + }, + }; + renderwithRouter(); + expect( + screen.getByRole('textbox', { + name: /tax residence\*/i, + }) + ).toHaveValue('Malta'); + }); + it('should render PersonalDetails component', () => { renderwithRouter(); expect(screen.getByTestId('personal_details_form')).toBeInTheDocument(); @@ -326,7 +346,7 @@ describe('', () => { it('should display the correct field details when is_appstore is true ', () => { renderwithRouter( - + ); @@ -336,7 +356,7 @@ describe('', () => { expect(screen.getByText(/phone number\*/i)).toBeInTheDocument(); expect(screen.getByLabelText(/phone number\*/i)).toBeInTheDocument(); - runCommonFormfieldsTests(); + runCommonFormfieldsTests(props.is_svg); }); it('should display the correct field details when is_appstore is false and is_svg is true ', () => { @@ -354,7 +374,7 @@ describe('', () => { expect(screen.getByText(/phone number\*/i)).toBeInTheDocument(); expect(screen.getByLabelText(/phone number\*/i)).toBeInTheDocument(); - runCommonFormfieldsTests(); + runCommonFormfieldsTests(props.is_svg); }); it('should display the correct field details when is_appstore is false and is_svg is false ', () => { @@ -370,10 +390,8 @@ describe('', () => { expect(screen.getByText('First name')).toBeInTheDocument(); expect(screen.getByText('Last name')).toBeInTheDocument(); expect(screen.getByText('Date of birth')).toBeInTheDocument(); - expect(screen.getByText('Phone number')).toBeInTheDocument(); - expect(screen.getByLabelText('Phone number')).toBeInTheDocument(); - runCommonFormfieldsTests(); + runCommonFormfieldsTests(false); }); it('should not enable fields which are disabled and empty', () => { @@ -522,27 +540,25 @@ describe('', () => { ).toBeInTheDocument(); }); - it('should show warning', async () => { + it('should show error for invalid TIN', async () => { const newvalidate = { - warnings: { - tax_identification_number: - 'This Tax Identification Number (TIN) is invalid. You may continue using it, but to facilitate future payment processes, valid tax information will be required.', + errors: { + ...mock_errors, + tax_identification_number: 'Tax Identification Number is not properly formatted.', }, - errors: { ...mock_errors }, }; splitValidationResultTypes.mockReturnValue(newvalidate); renderwithRouter( - ( - ); + ); + const tax_identification_number = screen.getByTestId('tax_identification_number'); - expect( - await screen.findByText( - /this tax identification number \(tin\) is invalid\. you may continue using it, but to facilitate future payment processes, valid tax information will be required\./i - ) - ).toBeInTheDocument(); + fireEvent.blur(tax_identification_number); + fireEvent.change(tax_identification_number, { target: { value: '123456789012345678901234567890' } }); + + expect(await screen.findByText(/tax identification number is not properly formatted/i)).toBeInTheDocument(); }); it('should submit the form if there is no validation error on desktop', async () => { @@ -586,6 +602,7 @@ describe('', () => { splitValidationResultTypes.mockReturnValue({ warnings: {}, errors: {} }); const new_props = { ...props, + is_svg: false, value: { account_opening_reason: '', citizen: '', @@ -666,7 +683,7 @@ describe('', () => { }); it('should close tax_residence pop-over when clicked outside', () => { - renderwithRouter(); + renderwithRouter(); const tax_residence_pop_over = screen.getByTestId('tax_residence_pop_over'); expect(tax_residence_pop_over).toBeInTheDocument(); @@ -680,7 +697,7 @@ describe('', () => { }); it('should close tax_identification_number_pop_over when clicked outside', () => { - renderwithRouter(); + renderwithRouter(); const tin_pop_over = screen.getByTestId('tax_identification_number_pop_over'); expect(tin_pop_over).toBeInTheDocument(); @@ -728,20 +745,6 @@ describe('', () => { expect(screen.queryByRole('link', { name: 'here' })).not.toBeInTheDocument(); }); - it('should autopopulate tax_residence for MF clients', () => { - const new_props = { - ...props, - is_mf: true, - value: { - ...props.value, - tax_residence: 'Malta', - }, - }; - renderwithRouter(); - const el_tax_residence = screen.getByTestId('selected_value'); - expect(el_tax_residence).toHaveTextContent('Malta'); - }); - it('should disable tax_residence field if it is immutable from BE', () => { isMobile.mockReturnValue(false); isDesktop.mockReturnValue(true); diff --git a/packages/account/src/Components/personal-details/personal-details.jsx b/packages/account/src/Components/personal-details/personal-details.jsx index 4d2823e1e0b4..599139a3597b 100644 --- a/packages/account/src/Components/personal-details/personal-details.jsx +++ b/packages/account/src/Components/personal-details/personal-details.jsx @@ -54,7 +54,6 @@ const PersonalDetails = ({ const { account_status, account_settings, residence, real_account_signup_target } = props; const { is_appstore } = React.useContext(PlatformContext); const [should_close_tooltip, setShouldCloseTooltip] = React.useState(false); - const [warning_items, setWarningItems] = React.useState({}); const is_submit_disabled_ref = React.useRef(true); const isSubmitDisabled = errors => { @@ -107,9 +106,8 @@ const PersonalDetails = ({ if (is_qualified_for_idv) { idv_error = validateIDV(values); } - const { errors, warnings } = splitValidationResultTypes(validate(values)); + const { errors } = splitValidationResultTypes(validate(values)); const error_data = { ...idv_error, ...errors }; - setWarningItems(warnings); checkSubmitStatus(error_data); return error_data; }; @@ -137,7 +135,7 @@ const PersonalDetails = ({ onSubmit(getCurrentStep() - 1, values, actions.setSubmitting, goToNextStep); }} > - {({ handleSubmit, errors, setFieldValue, setFieldTouched, touched, values, handleChange, handleBlur }) => ( + {({ handleSubmit, errors, setFieldValue, touched, values, handleChange, handleBlur }) => ( {({ setRef, height }) => (
)} - -
- -
-
+ diff --git a/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx b/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx index c81e867290ea..cf49192fab97 100644 --- a/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx +++ b/packages/account/src/Components/poi/idv-document-submit/idv-document-submit.tsx @@ -168,7 +168,6 @@ const IdvDocumentSubmit = ({ isSubmitting, isValid, setFieldValue, - setFieldTouched, touched, values, }) => ( @@ -189,27 +188,17 @@ const IdvDocumentSubmit = ({ /> -
- -
+ is_qualified_for_idv + is_appstore + should_hide_helper_image={shouldHideHelperImage(values?.document_type?.id)} + editable_fields={changeable_fields} + /> {isDesktop() && ( diff --git a/packages/account/src/Components/poi/poi-confirm-with-example-form-container/poi-confirm-with-example-form-container.tsx b/packages/account/src/Components/poi/poi-confirm-with-example-form-container/poi-confirm-with-example-form-container.tsx index 7bb01a575750..6432d20b9894 100644 --- a/packages/account/src/Components/poi/poi-confirm-with-example-form-container/poi-confirm-with-example-form-container.tsx +++ b/packages/account/src/Components/poi/poi-confirm-with-example-form-container/poi-confirm-with-example-form-container.tsx @@ -134,28 +134,10 @@ const PoiConfirmWithExampleFormContainer = ({ return ( - {({ - values, - errors, - touched, - handleChange, - handleBlur, - handleSubmit, - isSubmitting, - setFieldValue, - setFieldTouched, - status, - }) => ( + {({ errors, handleSubmit, isSubmitting, status }) => ( - {({ - errors, - handleBlur, - handleChange, - isSubmitting, - isValid, - setFieldValue, - setFieldTouched, - touched, - dirty, - values, - }) => ( + {({ errors, handleBlur, handleChange, isSubmitting, isValid, setFieldValue, touched, dirty, values }) => (
@@ -122,27 +111,17 @@ export const IdvDocSubmitOnSignup = ({ class_name='idv-layout' /> -
- -
+ is_qualified_for_idv + is_appstore + should_hide_helper_image={shouldHideHelperImage(values?.document_type?.id)} + editable_fields={changeable_fields} + />