diff --git a/packages/account/src/Components/forms/idv-form.tsx b/packages/account/src/Components/forms/idv-form.tsx index 7a977046bbba..5c5e56fa2a33 100644 --- a/packages/account/src/Components/forms/idv-form.tsx +++ b/packages/account/src/Components/forms/idv-form.tsx @@ -4,8 +4,13 @@ import { Field, FieldProps } from 'formik'; import { localize } from '@deriv/translations'; import { formatInput, getIDVNotApplicableOption } from '@deriv/shared'; import { Autocomplete, DesktopWrapper, Input, MobileWrapper, SelectNative, Text } from '@deriv/components'; -import { getDocumentData, preventEmptyClipboardPaste, generatePlaceholderText, getExampleFormat } from 'Helpers/utils'; -import { TDocumentList, TIDVForm } from 'Types'; +import { + getDocumentData, + preventEmptyClipboardPaste, + generatePlaceholderText, + getExampleFormat, +} from '../../Helpers/utils'; +import { TDocument, TIDVForm } from 'Types'; const IDVForm = ({ errors, @@ -19,7 +24,7 @@ const IDVForm = ({ hide_hint, can_skip_document_verification = false, }: TIDVForm) => { - const [document_list, setDocumentList] = React.useState([]); + const [document_list, setDocumentList] = React.useState([]); const [document_image, setDocumentImage] = React.useState(null); const [selected_doc, setSelectedDoc] = React.useState(''); @@ -45,10 +50,8 @@ const IDVForm = ({ const new_document_list = filtered_documents.map(key => { const { display_name, format } = document_data[key]; - const { new_display_name, example_format, sample_image } = getDocumentData( - selected_country.value ?? '', - key - ); + const { new_display_name, example_format, sample_image, additional_document_example_format } = + getDocumentData(selected_country.value ?? '', key); const needs_additional_document = !!document_data[key].additional; if (needs_additional_document) { @@ -58,6 +61,7 @@ const IDVForm = ({ additional: { display_name: document_data[key].additional?.display_name, format: document_data[key].additional?.format, + example_format: additional_document_example_format, }, value: format, sample_image, @@ -98,7 +102,7 @@ const IDVForm = ({ setFieldValue(document_name, current_input, true); }; - const bindDocumentData = (item: TDocumentList) => { + const bindDocumentData = (item: TDocument) => { setFieldValue('document_type', item, true); setSelectedDoc(item?.id); if (item?.id === IDV_NOT_APPLICABLE_OPTION.id) { @@ -221,6 +225,7 @@ const IDVForm = ({ onKeyUp={(e: { target: HTMLInputElement }) => onKeyUp(e, 'document_number') } + className='additional-field' required label={generatePlaceholderText(selected_doc)} /> diff --git a/packages/account/src/Components/forms/personal-details-form.jsx b/packages/account/src/Components/forms/personal-details-form.jsx index 4be847596d3e..492b6032fa59 100644 --- a/packages/account/src/Components/forms/personal-details-form.jsx +++ b/packages/account/src/Components/forms/personal-details-form.jsx @@ -1,7 +1,7 @@ import React from 'react'; -import { Link } from 'react-router-dom'; import classNames from 'classnames'; import { Field, useFormikContext } from 'formik'; +import { Link } from 'react-router-dom'; import { Autocomplete, Checkbox, @@ -15,13 +15,13 @@ import { } from '@deriv/components'; import { getLegalEntityName, isDesktop, isMobile, routes, validPhone } from '@deriv/shared'; import { Localize, localize } from '@deriv/translations'; -import InlineNoteWithIcon from '../inline-note-with-icon'; -import { DateOfBirthField, FormInputField } from './form-fields.jsx'; -import FormBodySection from '../form-body-section'; import FormSubHeader from '../form-sub-header'; import PoiNameDobExample from '../../Assets/ic-poi-name-dob-example.svg'; -import { isFieldImmutable } from '../../Helpers/utils'; +import InlineNoteWithIcon from '../inline-note-with-icon'; +import FormBodySection from '../form-body-section'; +import { DateOfBirthField, FormInputField } from './form-fields.jsx'; import { getEmploymentStatusList } from '../../Sections/Assessment/FinancialAssessment/financial-information-list'; +import { isFieldImmutable } from '../../Helpers/utils'; const PersonalDetailsForm = props => { const { diff --git a/packages/account/src/Components/poa/poa-button/__tests__/poa-button.spec.tsx b/packages/account/src/Components/poa/poa-button/__tests__/poa-button.spec.tsx index 420e0e6306fb..0b0b6914511e 100644 --- a/packages/account/src/Components/poa/poa-button/__tests__/poa-button.spec.tsx +++ b/packages/account/src/Components/poa/poa-button/__tests__/poa-button.spec.tsx @@ -3,7 +3,7 @@ import { createBrowserHistory } from 'history'; import { Router } from 'react-router'; import { fireEvent, render, screen } from '@testing-library/react'; import { routes } from '@deriv/shared'; -import { PoaButton } from '../poa-button'; +import PoaButton from '../poa-button'; describe('', () => { const history = createBrowserHistory(); diff --git a/packages/account/src/Components/poa/poa-button/index.ts b/packages/account/src/Components/poa/poa-button/index.ts index f9bb29c01e75..d1b09a40a0cd 100644 --- a/packages/account/src/Components/poa/poa-button/index.ts +++ b/packages/account/src/Components/poa/poa-button/index.ts @@ -1,3 +1,3 @@ -import { PoaButton } from './poa-button'; +import PoaButton from './poa-button'; export default PoaButton; diff --git a/packages/account/src/Components/poa/poa-button/poa-button.tsx b/packages/account/src/Components/poa/poa-button/poa-button.tsx index f4774cb9abd6..0a3780dc0f46 100644 --- a/packages/account/src/Components/poa/poa-button/poa-button.tsx +++ b/packages/account/src/Components/poa/poa-button/poa-button.tsx @@ -7,10 +7,12 @@ type TPoaButton = { custom_text?: string; }; -export const PoaButton = ({ custom_text = localize('Submit proof of address') }: TPoaButton) => ( +const PoaButton = ({ custom_text = localize('Submit proof of address') }: TPoaButton) => ( {custom_text} ); + +export default PoaButton; diff --git a/packages/account/src/Components/poi-unsupported-failed/unsupported-failed.tsx b/packages/account/src/Components/poi-unsupported-failed/unsupported-failed.tsx index ce27efc522fd..1be1b4b562dc 100644 --- a/packages/account/src/Components/poi-unsupported-failed/unsupported-failed.tsx +++ b/packages/account/src/Components/poi-unsupported-failed/unsupported-failed.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Icon } from '@deriv/components'; import { localize } from '@deriv/translations'; -import IconMessageContent from 'Components/icon-message-content'; +import IconMessageContent from '../icon-message-content'; type TUnsupportedFailed = { error?: string; 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 a97203a25183..dbc20942cb72 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 @@ -12,14 +12,21 @@ import { removeEmptyPropertiesFromObject, formatIDVFormValues, } from '@deriv/shared'; -import { documentAdditionalError, getRegex, validate, makeSettingsRequest, validateName } from 'Helpers/utils'; +import { + documentAdditionalError, + getRegex, + validate, + makeSettingsRequest, + validateName, + getExampleFormat, +} from 'Helpers/utils'; import FormFooter from 'Components/form-footer'; import BackButtonIcon from 'Assets/ic-poi-back-btn.svg'; import IDVForm from 'Components/forms/idv-form'; import PersonalDetailsForm from 'Components/forms/personal-details-form'; import FormSubHeader from 'Components/form-sub-header'; -import { GetSettings, IdentityVerificationAddDocumentResponse, ResidenceList } from '@deriv/api-types'; -import { TIDVFormValues, TInputFieldValues, TDocumentList } from 'Types'; +import { GetSettings, ResidenceList, IdentityVerificationAddDocumentResponse } from '@deriv/api-types'; +import { TDocument, TInputFieldValues, TIDVFormValues } from 'Types'; type TIDVDocumentSubmitProps = { account_settings: GetSettings; @@ -58,21 +65,18 @@ const IdvDocumentSubmit = ({ ...form_initial_values, }; - const getExampleFormat = (example_format: string) => { - return example_format ? localize('Example: ') + example_format : ''; - }; const IDV_NOT_APPLICABLE_OPTION = React.useMemo(() => getIDVNotApplicableOption(), []); const shouldHideHelperImage = (document_id: string) => document_id === IDV_NOT_APPLICABLE_OPTION.id; - const isDocumentTypeValid = (document_type: TDocumentList) => { + const isDocumentTypeValid = (document_type: TDocument) => { if (!document_type?.text) { return localize('Please select a document type.'); } return undefined; }; - const isAdditionalDocumentValid = (document_type: TDocumentList, document_additional: string) => { + const isAdditionalDocumentValid = (document_type: TDocument, document_additional: string) => { const error_message = documentAdditionalError(document_additional, document_type.additional?.format); if (error_message) { return localize(error_message) + getExampleFormat(document_type.additional?.example_format); @@ -80,7 +84,7 @@ const IdvDocumentSubmit = ({ return undefined; }; - const isDocumentNumberValid = (document_number: string, document_type: Required) => { + const isDocumentNumberValid = (document_number: string, document_type: Required) => { const is_document_number_invalid = document_number === document_type.example_format; if (!document_number) { return localize('Please enter your document number. ') + getExampleFormat(document_type.example_format); diff --git a/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx b/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx index c07eae73190a..86212d2144de 100644 --- a/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-submit-complete/__tests__/idv-submit-complete.spec.tsx @@ -3,7 +3,7 @@ import { BrowserRouter } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; import IdvSubmitComplete from '../idv-submit-complete'; -jest.mock('Assets/ic-idv-document-pending.svg', () => jest.fn(() => 'IdvDocumentPending')); +jest.mock('../../../../../Assets/ic-idv-document-pending.svg', () => jest.fn(() => 'IdvDocumentPending')); describe('', () => { const mock_props = { diff --git a/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx b/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx index 0b99c7bc0ef3..bd0f526e110d 100644 --- a/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-submit-complete/idv-submit-complete.tsx @@ -1,5 +1,5 @@ -import IdvDocumentPending from 'Assets/ic-idv-document-pending.svg'; -import PoaButton from 'Components/poa/poa-button'; +import IdvDocumentPending from '../../../../Assets/ic-idv-document-pending.svg'; +import PoaButton from '../../../poa/poa-button/poa-button'; import React from 'react'; import { Text } from '@deriv/components'; import { localize } from '@deriv/translations'; diff --git a/packages/account/src/Components/poi/idv-status/idv-verified/__tests__/idv-verified.spec.tsx b/packages/account/src/Components/poi/idv-status/idv-verified/__tests__/idv-verified.spec.tsx index fe61dca8ee94..753ac9226f7f 100644 --- a/packages/account/src/Components/poi/idv-status/idv-verified/__tests__/idv-verified.spec.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-verified/__tests__/idv-verified.spec.tsx @@ -10,7 +10,7 @@ jest.mock('@deriv/shared', () => ({ isMobile: jest.fn(() => false), })); -jest.mock('Assets/ic-idv-verified.svg', () => jest.fn(() => 'mockedSVGIcon')); +jest.mock('../../../../../Assets/ic-idv-verified.svg', () => jest.fn(() => 'mockedSVGIcon')); describe('', () => { const needs_poa_header = /your id is verified\. you will also need to submit proof of your address\./i; diff --git a/packages/account/src/Components/poi/idv-status/idv-verified/idv-verified.tsx b/packages/account/src/Components/poi/idv-status/idv-verified/idv-verified.tsx index 91cfe3c5af21..387c0ca346d7 100644 --- a/packages/account/src/Components/poi/idv-status/idv-verified/idv-verified.tsx +++ b/packages/account/src/Components/poi/idv-status/idv-verified/idv-verified.tsx @@ -1,5 +1,5 @@ -import IdvDocumentVerified from 'Assets/ic-idv-verified.svg'; -import PoaButton from 'Components/poa/poa-button'; +import IdvDocumentVerified from '../../../../Assets/ic-idv-verified.svg'; +import PoaButton from '../../../poa/poa-button/poa-button'; import React from 'react'; import { Text } from '@deriv/components'; import { isMobile } from '@deriv/shared'; diff --git a/packages/account/src/Components/poi/missing-personal-details/missing-personal-details.tsx b/packages/account/src/Components/poi/missing-personal-details/missing-personal-details.tsx index ba61a41d4b41..743fe7cc318d 100644 --- a/packages/account/src/Components/poi/missing-personal-details/missing-personal-details.tsx +++ b/packages/account/src/Components/poi/missing-personal-details/missing-personal-details.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { PlatformContext } from '@deriv/shared'; import { ButtonLink, Icon, Text } from '@deriv/components'; import { localize } from '@deriv/translations'; -import IconMessageContent from 'Components/icon-message-content'; +import IconMessageContent from '../../icon-message-content'; type TGoToPersonalDetailsButton = { has_invalid_postal_code?: boolean; 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 2958df7a8385..9214d28fd839 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 @@ -12,10 +12,10 @@ import { toMoment, WS, } from '@deriv/shared'; -import FormBody from 'Components/form-body'; -import LoadErrorMessage from 'Components/load-error-message'; -import PersonalDetailsForm from 'Components/forms/personal-details-form'; -import { makeSettingsRequest, validate, validateName } from 'Helpers/utils'; +import FormBody from '../../form-body'; +import LoadErrorMessage from '../../load-error-message'; +import PersonalDetailsForm from '../../forms/personal-details-form.jsx'; +import { makeSettingsRequest, validate, validateName } from '../../../Helpers/utils'; import { TInputFieldValues } from 'Types'; type TRestState = { diff --git a/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx b/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx index 79e8de7583b4..daef89e70eb5 100644 --- a/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx +++ b/packages/account/src/Components/poi/poi-country-selector/__tests__/poi-country-selector.spec.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { fireEvent, render, screen, waitFor } from '@testing-library/react'; import { isDesktop, isMobile } from '@deriv/shared'; -import CountrySelector from '../poi-country-selector'; +import PoiCountrySelector from '../poi-country-selector'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -9,7 +9,7 @@ jest.mock('@deriv/shared', () => ({ isMobile: jest.fn(() => false), })); -describe('', () => { +describe('', () => { let mock_props = { handleSelectionNext: jest.fn(), is_from_external: false, @@ -32,10 +32,10 @@ describe('', () => { }; }); - it('should render CountrySelector component external', () => { + it('should render PoiCountrySelector component external', () => { mock_props.is_from_external = true; - render(); + render(); expect(screen.getByText('Proof of identity')).toBeInTheDocument(); expect(screen.getByText('In which country was your document issued?')).toBeInTheDocument(); @@ -45,7 +45,7 @@ describe('', () => { }); it('should show error message after clicking the input without choosing the country', async () => { - render(); + render(); const field = screen.getByLabelText('Country'); const next_btn = screen.getByRole('button'); @@ -68,7 +68,7 @@ describe('', () => { (isMobile as jest.Mock).mockReturnValue(true); mock_props.selected_country = 'Country 2'; - render(); + render(); const field = screen.getByRole('combobox'); diff --git a/packages/account/src/Components/poi/poi-country-selector/index.js b/packages/account/src/Components/poi/poi-country-selector/index.js index 479c572a67d1..27629049f726 100644 --- a/packages/account/src/Components/poi/poi-country-selector/index.js +++ b/packages/account/src/Components/poi/poi-country-selector/index.js @@ -1,3 +1,3 @@ -import CountrySelector from './poi-country-selector'; +import PoiCountrySelector from './poi-country-selector'; -export default CountrySelector; +export default PoiCountrySelector; diff --git a/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx b/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx index d28514bb74f3..b84e2d8b971c 100644 --- a/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx +++ b/packages/account/src/Components/poi/poi-country-selector/poi-country-selector.tsx @@ -7,23 +7,21 @@ import FormFooter from 'Components/form-footer'; type TCountry = Record; -type TCountrySelector = { +type TPoiCountrySelector = { handleSelectionNext: () => void; is_from_external: boolean; - residence_list: TCountry[]; + residence_list: Array; selected_country: string; setSelectedCountry: (value: TCountry) => void; }; -const CountrySelector = ({ +const PoiCountrySelector = ({ handleSelectionNext, is_from_external, residence_list, selected_country, setSelectedCountry, -}: TCountrySelector) => { - const [country_list, setCountryList] = React.useState([]); - +}: TPoiCountrySelector) => { const initial_form_values: FormikValues = { country_input: '', }; @@ -34,7 +32,7 @@ const CountrySelector = ({ if (!country_input) { errors.country_input = localize('Please select the country of document issuance.'); - } else if (!country_list.find((c: FormikValues) => c.text === country_input)) { + } else if (!residence_list.find((c: FormikValues) => c.text === country_input)) { errors.country_input = localize('Please select a valid country of document issuance.'); } @@ -42,7 +40,9 @@ const CountrySelector = ({ }; const updateSelectedCountry = (country_name: string) => { - const matching_country: TCountry | undefined = country_list.find((c: FormikValues) => c.text === country_name); + const matching_country: TCountry | undefined = residence_list.find( + (c: FormikValues) => c.text === country_name + ); if (matching_country) { setSelectedCountry?.(matching_country); } @@ -54,10 +54,6 @@ const CountrySelector = ({ handleSelectionNext?.(); }; - React.useEffect(() => { - setCountryList(residence_list); - }, [residence_list]); - return ( {({ @@ -99,13 +95,13 @@ const CountrySelector = ({ autoComplete='off' type='text' label={localize('Country')} - list_items={country_list} + list_items={residence_list} value={values.country_input} onBlur={(e: FormikValues) => { handleBlur(e); const current_input = e.target.value; if ( - !country_list.find( + !residence_list.find( (c: FormikValues) => c.text === current_input ) ) { @@ -131,7 +127,7 @@ const CountrySelector = ({ error={touched.country_input && errors.country_input} label={localize('Country')} placeholder={localize('Please select')} - list_items={country_list} + list_items={residence_list} value={values.country_input} onChange={(e: React.ChangeEvent) => { handleChange(e); @@ -168,4 +164,4 @@ const CountrySelector = ({ ); }; -export default CountrySelector; +export default PoiCountrySelector; diff --git a/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx b/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx index 6e56197532ff..27324d09cab9 100644 --- a/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx +++ b/packages/account/src/Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { Formik, FormikValues, FormikHelpers, FormikErrors, Form } from 'formik'; import { localize } from '@deriv/translations'; import classNames from 'classnames'; +import { GetSettings, ResidenceList } from '@deriv/api-types'; import { Button } from '@deriv/components'; import { filterObjProperties, toMoment, removeEmptyPropertiesFromObject } from '@deriv/shared'; import { @@ -11,12 +12,11 @@ import { isAdditionalDocumentValid, isDocumentNumberValid, shouldHideHelperImage, -} from 'Helpers/utils'; -import FormSubHeader from 'Components/form-sub-header'; -import IDVForm from 'Components/forms/idv-form'; -import PersonalDetailsForm from 'Components/forms/personal-details-form'; -import FormFooter from 'Components/form-footer'; -import { GetSettings } from '@deriv/api-types'; +} from '../../../../Helpers/utils'; +import FormSubHeader from '../../../form-sub-header'; +import IDVForm from '../../../forms/idv-form'; +import PersonalDetailsForm from '../../../forms/personal-details-form.jsx'; +import FormFooter from '../../../form-footer'; type TIdvDocSubmitOnSignup = { citizen_data: FormikValues; @@ -26,6 +26,7 @@ type TIdvDocSubmitOnSignup = { has_idv_error?: boolean; account_settings: GetSettings; getChangeableFields: () => string[]; + residence_list: ResidenceList; }; export const IdvDocSubmitOnSignup = ({ @@ -33,6 +34,7 @@ export const IdvDocSubmitOnSignup = ({ onNext, account_settings, getChangeableFields, + residence_list, }: TIdvDocSubmitOnSignup) => { const validateFields = (values: FormikValues) => { const errors: FormikErrors = {}; @@ -118,6 +120,7 @@ export const IdvDocSubmitOnSignup = ({ is_appstore should_hide_helper_image={shouldHideHelperImage(values?.document_type?.id)} editable_fields={changeable_fields} + residence_list={residence_list} /> diff --git a/packages/account/src/Components/poi/status/limited/limited.tsx b/packages/account/src/Components/poi/status/limited/limited.tsx index c782f068ea11..e6391537c2a8 100644 --- a/packages/account/src/Components/poi/status/limited/limited.tsx +++ b/packages/account/src/Components/poi/status/limited/limited.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { Icon } from '@deriv/components'; import { localize, Localize } from '@deriv/translations'; -import IconMessageContent from 'Components/icon-message-content'; +import IconMessageContent from '../../../icon-message-content'; export const POILimited = () => ( { let has_nimc = false; diff --git a/packages/account/src/Components/poi/status/upload-complete/__tests__/upload-complete.spec.tsx b/packages/account/src/Components/poi/status/upload-complete/__tests__/upload-complete.spec.tsx index 04d91784dfe2..d6225d9e7c56 100644 --- a/packages/account/src/Components/poi/status/upload-complete/__tests__/upload-complete.spec.tsx +++ b/packages/account/src/Components/poi/status/upload-complete/__tests__/upload-complete.spec.tsx @@ -5,7 +5,7 @@ import { PlatformContext } from '@deriv/shared'; import { UploadComplete } from '../upload-complete'; import { BrowserRouter } from 'react-router-dom'; -jest.mock('Components/poa/poa-button', () => jest.fn(() =>
)); +jest.mock('../../../../poa/poa-button', () => jest.fn(() =>
)); jest.mock('@deriv/components', () => { const original_module = jest.requireActual('@deriv/components'); diff --git a/packages/account/src/Components/poi/status/upload-complete/upload-complete.tsx b/packages/account/src/Components/poi/status/upload-complete/upload-complete.tsx index 6194dae82642..a483e233eb70 100644 --- a/packages/account/src/Components/poi/status/upload-complete/upload-complete.tsx +++ b/packages/account/src/Components/poi/status/upload-complete/upload-complete.tsx @@ -2,9 +2,9 @@ import React from 'react'; import { Icon, Text } from '@deriv/components'; import { PlatformContext, isNavigationFromP2P, isNavigationFromDerivGO } from '@deriv/shared'; import { localize } from '@deriv/translations'; -import PoaButton from 'Components/poa/poa-button'; -import { ContinueTradingButton } from 'Components/poa/continue-trading-button/continue-trading-button'; -import IconMessageContent from 'Components/icon-message-content'; +import PoaButton from '../../../poa/poa-button'; +import { ContinueTradingButton } from '../../../poa/continue-trading-button/continue-trading-button'; +import IconMessageContent from '../../../icon-message-content/icon-message-content'; import { TPlatformContext, TPOIStatus } from 'Types'; import classNames from 'classnames'; diff --git a/packages/account/src/Constants/idv-document-config.ts b/packages/account/src/Constants/idv-document-config.ts index 2202ab910848..59fdbc749838 100644 --- a/packages/account/src/Constants/idv-document-config.ts +++ b/packages/account/src/Constants/idv-document-config.ts @@ -5,7 +5,7 @@ const getImageLocation = (image_name: string) => getUrlBase(`/public/images/comm // Note: Ensure that the object keys matches BE API's keys. This is simply a mapping for FE templates -export const getIDVDocumentConfig = () => ({ +const getIDVDocumentConfig = () => ({ ke: { alien_card: { new_display_name: '', @@ -125,4 +125,58 @@ export const getIDVDocumentConfig = () => ({ sample_image: getImageLocation('zw_national_identity_card.png'), }, }, + cl: { + national_id: { + example_format: '123456789', + }, + }, + ar: { + dni: { + example_format: '12345678', + }, + }, + mx: { + curp: { + example_format: 'ABCD123456HEFGIJ00', + }, + }, + id: { + nik: { + example_format: '1234567890123456', + }, + }, + in: { + aadhaar: { + example_format: '123456789012', + additional_document_example_format: 'ABCDE1234F', + }, + drivers_license: { + example_format: 'AB1234567890123', + }, + epic: { + example_format: 'ABC1234567', + }, + pan: { + example_format: 'ABCDE1234F', + }, + passport: { + example_format: 'A1234567', + additional_document_example_format: 'AB1234567890123', + }, + }, + pe: { + national_id: { + example_format: '12345678', + }, + }, + vn: { + national_id: { + example_format: '12345678901', + }, + }, }); + +export const getIDVDocuments = (country_code: string) => { + const IDV_DOCUMENT_DATA: { [key: string]: object } = getIDVDocumentConfig(); + return IDV_DOCUMENT_DATA[country_code]; +}; diff --git a/packages/account/src/Helpers/__tests__/utils.spec.ts b/packages/account/src/Helpers/__tests__/utils.spec.ts index d76047f5d32d..d62876bf0c80 100644 --- a/packages/account/src/Helpers/__tests__/utils.spec.ts +++ b/packages/account/src/Helpers/__tests__/utils.spec.ts @@ -107,10 +107,8 @@ describe('shouldShowIdentityInformation', () => { describe('getDocumentData', () => { it('should return the empty document data', () => { - expect(getDocumentData('test', 'test')).toEqual({ - new_display_name: '', - example_format: '', - sample_image: '', + expect(getDocumentData('pe', 'national_id')).toEqual({ + example_format: '12345678', }); }); diff --git a/packages/account/src/Helpers/utils.ts b/packages/account/src/Helpers/utils.ts index 1ac810c83dea..d4f1ac185fe8 100644 --- a/packages/account/src/Helpers/utils.ts +++ b/packages/account/src/Helpers/utils.ts @@ -3,7 +3,7 @@ import { filterObjProperties, toMoment, validLength, validName, getIDVNotApplica import { localize } from '@deriv/translations'; import { ResidenceList, GetSettings, GetAccountStatus } from '@deriv/api-types'; import { FormikValues } from 'formik'; -import { getIDVDocumentConfig } from '../Constants/idv-document-config'; +import { getIDVDocuments } from '../Constants/idv-document-config'; import { TServerError } from '../Types'; export const documentAdditionalError = (document_additional: string, document_additional_format: string) => { @@ -56,15 +56,13 @@ export const shouldShowIdentityInformation = ({ }; export const getDocumentData = (country_code: string, document_type: string) => { - const IDV_DOCUMENT_DATA = getIDVDocumentConfig(); - return ( - (Object.keys(IDV_DOCUMENT_DATA).includes(country_code) && - (IDV_DOCUMENT_DATA as any)[country_code][document_type]) || { - new_display_name: '', - example_format: '', - sample_image: '', - } - ); + const DEFAULT_CONFIG = { + new_display_name: '', + example_format: '', + sample_image: '', + }; + const IDV_DOCUMENT_DATA: any = getIDVDocuments(country_code); + return IDV_DOCUMENT_DATA[document_type] ?? DEFAULT_CONFIG; }; export const preventEmptyClipboardPaste = (e: React.ClipboardEvent) => { @@ -135,7 +133,7 @@ export const validateName = (name: string) => { return ''; }; -export const getExampleFormat = (example_format: string | undefined) => +export const getExampleFormat = (example_format?: string) => example_format ? localize('Example: ') + example_format : ''; export const isDocumentTypeValid = (document_type: FormikValues) => { diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/__tests__/proof-of-identity-container.spec.js b/packages/account/src/Sections/Verification/ProofOfIdentity/__tests__/proof-of-identity-container.spec.js index 8603b4187173..eb2e0087aec4 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/__tests__/proof-of-identity-container.spec.js +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/__tests__/proof-of-identity-container.spec.js @@ -3,6 +3,7 @@ import { render, screen, act, waitFor } from '@testing-library/react'; import ProofOfIdentityContainer from '../proof-of-identity-container'; import { populateVerificationStatus } from '../../Helpers/verification.js'; import { identity_status_codes, service_code } from '../proof-of-identity-utils'; +import { StoreProvider, mockStore } from '@deriv/stores'; jest.mock('@deriv/shared', () => ({ ...jest.requireActual('@deriv/shared'), @@ -43,154 +44,182 @@ jest.mock('Components/poi/status/limited', () => jest.fn(() => 'mockedLimited')) jest.mock('Components/poi/status/expired', () => jest.fn(() => 'mockedExpired')); const mock_props = { - account_settings: {}, - account_status: { - authentication: { - attempts: { - count: 1, - history: [ - { - country_code: 'id', - id: '8919', - service: 'manual', + onStateChange: jest.fn(), + setIsCfdPoiCompleted: jest.fn(), + is_from_external: false, + height: '200', +}; + +describe('ProofOfIdentityContainer', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + let store = mockStore({ + client: { + account_status: { + authentication: { + attempts: { + count: 1, + history: [ + { + country_code: 'id', + id: '8919', + service: 'manual', + status: 'verified', + timestamp: 1674633681, + }, + ], + latest: { + country_code: 'id', + id: '8919', + service: 'manual', + status: 'verified', + timestamp: 1674633681, + }, + }, + document: { status: 'verified', - timestamp: 1674633681, }, - ], - latest: { - country_code: 'id', - id: '8919', - service: 'manual', - status: 'verified', - timestamp: 1674633681, - }, - }, - document: { - status: 'verified', - }, - identity: { - services: { - idv: { - last_rejected: [], - reported_properties: {}, - status: 'none', - submissions_left: 3, + identity: { + services: { + idv: { + last_rejected: [], + reported_properties: {}, + status: 'none', + submissions_left: 3, + }, + manual: { + status: 'none', + }, + onfido: { + country_code: 'IDN', + documents_supported: [ + 'Driving Licence', + 'National Identity Card', + 'Passport', + 'Residence Permit', + ], + is_country_supported: 1, + last_rejected: [], + reported_properties: {}, + status: 'none', + submissions_left: 3, + }, + }, + status: 'verified', }, - manual: { + income: { status: 'none', }, - onfido: { - country_code: 'IDN', - documents_supported: [ - 'Driving Licence', - 'National Identity Card', - 'Passport', - 'Residence Permit', - ], - is_country_supported: 1, - last_rejected: [], - reported_properties: {}, + needs_verification: [], + ownership: { + requests: [], status: 'none', - submissions_left: 3, }, }, - status: 'verified', - }, - income: { - status: 'none', - }, - needs_verification: [], - ownership: { - requests: [], - status: 'none', - }, - }, - currency_config: { - USD: { - is_deposit_suspended: 0, - is_withdrawal_suspended: 0, + currency_config: { + USD: { + is_deposit_suspended: 0, + is_withdrawal_suspended: 0, + }, + }, + p2p_status: 'none', + prompt_client_to_authenticate: 0, + risk_classification: 'low', + status: [ + 'age_verification', + 'allow_document_upload', + 'authenticated', + 'dxtrade_password_not_set', + 'financial_information_not_complete', + 'idv_disallowed', + 'mt5_password_not_set', + 'trading_experience_not_complete', + ], }, + fetchResidenceList: jest.fn().mockResolvedValue({ + residence_list: [ + { + identity: { + services: { + idv: { + documents_supported: {}, + has_visual_sample: 0, + is_country_supported: 0, + }, + onfido: { + documents_supported: { + passport: { + display_name: 'Passport', + }, + }, + is_country_supported: 1, + }, + }, + }, + phone_idd: '93', + text: 'Afghanistan', + value: 'af', + }, + ], + }), }, - p2p_status: 'none', - prompt_client_to_authenticate: 0, - risk_classification: 'low', - status: [ - 'age_verification', - 'allow_document_upload', - 'authenticated', - 'dxtrade_password_not_set', - 'financial_information_not_complete', - 'idv_disallowed', - 'mt5_password_not_set', - 'trading_experience_not_complete', - ], - }, - app_routing_history: [ - { - pathname: '/account/proof-of-identity', + common: { + app_routing_history: [ + { + pathname: '/account/proof-of-identity', + }, + ], }, - ], - fetchResidenceList: jest.fn().mockResolvedValue({ - residence_list: [], - }), - getChangeableFields: [], - is_from_external: false, - is_switching: false, - is_virtual: false, - is_high_risk: false, - is_withdrawal_lock: false, - onStateChange: jest.fn(), - refreshNotifications: jest.fn(), - routeBackInApp: jest.fn(), - should_allow_authentication: false, - setIsCfdPoiCompleted: jest.fn(), - updateAccountStatus: jest.fn(), -}; - -describe('ProofOfIdentityContainer', () => { - beforeEach(() => { - jest.clearAllMocks(); }); + const renderComponent = ({ props = mock_props, store_config = store }) => { + return render( + + + + ); + }; + it('should render proof of identity container with loader', async () => { - const new_props = { - ...mock_props, - is_switching: true, - }; + const new_store = { ...store, client: { ...store.client, is_switching: true } }; + + renderComponent({ store_config: new_store }); - render(); await waitFor(() => {}); expect(screen.getByText('mockedLoading')).toBeInTheDocument(); }); it('should render message when account is virtual', async () => { - const new_props = { - ...mock_props, - is_virtual: true, - }; + const new_store = { ...store, client: { ...store.client, is_virtual: true } }; - render(); + renderComponent({ store_config: new_store }); await waitFor(() => {}); expect(screen.getByText('mockedDemoMessage')).toBeInTheDocument(); }); it('should render API error message returned in response', async () => { - const new_props = { - ...mock_props, - fetchResidenceList: jest.fn().mockResolvedValue({ - error: { - message: 'API error', - }, - }), + const new_store = { + ...store, + client: { + ...store.client, + fetchResidenceList: jest.fn().mockResolvedValue({ + error: { + message: 'API error', + }, + }), + }, }; - render(); + + renderComponent({ store_config: new_store }); await waitFor(() => {}); expect(screen.getByText('mockedErrorMessage')).toBeInTheDocument(); }); it('should render messages that POA is not required', async () => { - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedNotRequired')).toBeInTheDocument(); }); @@ -201,7 +230,7 @@ describe('ProofOfIdentityContainer', () => { is_age_verified: true, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedProofOfIdentitySubmission')).toBeInTheDocument(); }); @@ -213,7 +242,7 @@ describe('ProofOfIdentityContainer', () => { identity_status: identity_status_codes.verified, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedProofOfIdentitySubmission')).toBeInTheDocument(); }); @@ -224,8 +253,7 @@ describe('ProofOfIdentityContainer', () => { is_age_verified: true, identity_status: identity_status_codes.pending, }); - - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedUploadComplete')).toBeInTheDocument(); }); @@ -237,7 +265,7 @@ describe('ProofOfIdentityContainer', () => { identity_status: identity_status_codes.verified, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedVerified')).toBeInTheDocument(); }); @@ -248,8 +276,7 @@ describe('ProofOfIdentityContainer', () => { is_age_verified: true, identity_status: identity_status_codes.expired, }); - - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedExpired')).toBeInTheDocument(); }); @@ -261,7 +288,7 @@ describe('ProofOfIdentityContainer', () => { identity_status: identity_status_codes.rejected, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedLimited')).toBeInTheDocument(); }); @@ -273,7 +300,7 @@ describe('ProofOfIdentityContainer', () => { identity_status: identity_status_codes.rejected, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedOnfido')).toBeInTheDocument(); }); @@ -285,7 +312,7 @@ describe('ProofOfIdentityContainer', () => { identity_status: identity_status_codes.rejected, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedIDV')).toBeInTheDocument(); }); @@ -297,7 +324,7 @@ describe('ProofOfIdentityContainer', () => { identity_status: identity_status_codes.rejected, }); - render(); + renderComponent({}); await waitFor(() => {}); expect(screen.getByText('mockedUnsupported')).toBeInTheDocument(); }); diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/onfido-sdk-view-container.tsx b/packages/account/src/Sections/Verification/ProofOfIdentity/onfido-sdk-view-container.tsx index 4efe217507a3..85d71526459a 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/onfido-sdk-view-container.tsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/onfido-sdk-view-container.tsx @@ -7,11 +7,11 @@ import { GetSettings, ResidenceList } from '@deriv/api-types'; import { Loading, ThemedScrollbars } from '@deriv/components'; import { cryptoMathRandom, isMobile, WS } from '@deriv/shared'; import { getLanguage } from '@deriv/translations'; -import ErrorMessage from 'Components/error-component'; -import getOnfidoPhrases from 'Constants/onfido-phrases'; -import MissingPersonalDetails from 'Components/poi/missing-personal-details'; -import PoiConfirmWithExampleFormContainer from 'Components/poi/poi-confirm-with-example-form-container'; -import OnfidoSdkView from 'Sections/Verification/ProofOfIdentity/onfido-sdk-view'; +import ErrorMessage from '../../../Components/error-component'; +import getOnfidoPhrases from '../../../Constants/onfido-phrases'; +import MissingPersonalDetails from '../../../Components/poi/missing-personal-details'; +import PoiConfirmWithExampleFormContainer from '../../../Components/poi/poi-confirm-with-example-form-container'; +import OnfidoSdkView from './onfido-sdk-view'; type TAPIError = { code?: string; diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5.jsx index a931df72d4cc..9463ef65c488 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5.jsx @@ -2,30 +2,21 @@ import React from 'react'; import { Loading } from '@deriv/components'; import { WS } from '@deriv/shared'; import { localize } from '@deriv/translations'; -import ErrorMessage from 'Components/error-component'; -import IconWithMessage from 'Components/icon-with-message'; +import { useStore, observer } from '@deriv/stores'; +import ErrorMessage from '../../../Components/error-component'; +import IconWithMessage from '../../../Components/icon-with-message'; import POISubmissionForMT5 from './proof-of-identity-submission-for-mt5.jsx'; import { service_code } from './proof-of-identity-utils'; import { populateVerificationStatus } from '../Helpers/verification'; -const ProofOfIdentityContainerForMt5 = ({ - account_settings, - account_status, - fetchResidenceList, - getChangeableFields, - height, - is_from_external, - is_switching, - is_virtual, - onStateChange, - refreshNotifications, - citizen_data, - is_eu_user, -}) => { +const ProofOfIdentityContainerForMt5 = observer(({ onStateChange, citizen_data }) => { const [api_error, setAPIError] = React.useState(); const [residence_list, setResidenceList] = React.useState(); const [is_status_loading, setStatusLoading] = React.useState(true); + const { client } = useStore(); + const { account_status, fetchResidenceList, is_switching, is_virtual } = client; + React.useEffect(() => { // only re-mount logic when switching is done if (!is_switching) { @@ -62,8 +53,7 @@ const ProofOfIdentityContainerForMt5 = ({ } const verification_status = populateVerificationStatus(account_status); - const { idv, has_attempted_idv, identity_last_attempt, is_idv_disallowed, manual, needs_poa, onfido } = - verification_status; + const { idv, identity_last_attempt, is_idv_disallowed, onfido } = verification_status; const poi_resubmission_cases = ['rejected', 'suspected', 'expired']; @@ -72,25 +62,15 @@ const ProofOfIdentityContainerForMt5 = ({ return ( ); -}; +}); export default ProofOfIdentityContainerForMt5; diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container.jsx index f9fc84bb6fe4..d80bbd23f432 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container.jsx @@ -1,5 +1,6 @@ import { Button, Loading } from '@deriv/components'; -import { WS, getPlatformRedirect, platforms } from '@deriv/shared'; +import { isEmptyObject, WS, getPlatformRedirect, platforms } from '@deriv/shared'; +import { observer, useStore } from '@deriv/stores'; import { identity_status_codes, service_code } from './proof-of-identity-utils'; import DemoMessage from 'Components/demo-message'; import ErrorMessage from 'Components/error-component'; @@ -17,31 +18,28 @@ import Verified from 'Components/poi/status/verified'; import { populateVerificationStatus } from '../Helpers/verification'; import { useHistory } from 'react-router'; -const ProofOfIdentityContainer = ({ - account_settings, - account_status, - app_routing_history, - fetchResidenceList, - getChangeableFields, - height, - is_from_external, - is_switching, - is_virtual, - is_high_risk, - is_withdrawal_lock, - onStateChange, - refreshNotifications, - routeBackInApp, - should_allow_authentication, - setIsCfdPoiCompleted, - updateAccountStatus, -}) => { +const ProofOfIdentityContainer = observer(({ height, is_from_external, onStateChange, setIsCfdPoiCompleted }) => { const history = useHistory(); const [api_error, setAPIError] = React.useState(); const [has_require_submission, setHasRequireSubmission] = React.useState(false); - const [residence_list, setResidenceList] = React.useState(); + const [residence_list, setResidenceList] = React.useState([]); const [is_status_loading, setStatusLoading] = React.useState(true); + const { client, common, notifications } = useStore(); + + const { + account_settings, + account_status, + fetchResidenceList, + is_switching, + is_high_risk, + is_withdrawal_lock, + should_allow_authentication, + is_virtual, + } = client; + const { app_routing_history, is_language_changing, routeBackInApp } = common; + const { refreshNotifications } = notifications; + const from_platform = getPlatformRedirect(app_routing_history); const should_show_redirect_btn = Object.keys(platforms).includes(from_platform?.ref); @@ -56,6 +54,24 @@ const ProofOfIdentityContainer = ({ }); }; + const loadResidenceList = React.useCallback(() => { + setStatusLoading(true); + fetchResidenceList().then(response_residence_list => { + if (response_residence_list.error) { + setAPIError(response_residence_list.error); + } else { + setResidenceList(response_residence_list.residence_list); + } + }); + setStatusLoading(false); + }, [fetchResidenceList]); + + React.useEffect(() => { + if (is_language_changing) { + loadResidenceList(); + } + }, [is_language_changing, loadResidenceList]); + React.useEffect(() => { // only re-mount logic when switching is done if (!is_switching) { @@ -65,25 +81,21 @@ const ProofOfIdentityContainer = ({ setStatusLoading(false); return; } - - fetchResidenceList().then(response_residence_list => { - if (response_residence_list.error) { - setAPIError(response_residence_list.error); - } else { - setResidenceList(response_residence_list.residence_list); - } - setStatusLoading(false); - }); + loadResidenceList(); }); } - }, [fetchResidenceList, is_switching]); + }, [is_switching, loadResidenceList]); - if (is_status_loading || is_switching) { + if (api_error) { + return ; + } + /** + * Display loader while waiting for the account status and residence list to be populated + */ + if (is_status_loading || is_switching || isEmptyObject(account_status) || residence_list.length === 0) { return ; } else if (is_virtual) { return ; - } else if (api_error) { - return ; } const verification_status = populateVerificationStatus(account_status); @@ -124,24 +136,19 @@ const ProofOfIdentityContainer = ({ if (identity_status === identity_status_codes.none || has_require_submission || allow_poi_resubmission) { return ( ); } else if ( @@ -225,6 +232,6 @@ const ProofOfIdentityContainer = ({ default: return null; } -}; +}); export default ProofOfIdentityContainer; diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx index 9f9b79aa062e..371aafc87833 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission-for-mt5.jsx @@ -1,137 +1,134 @@ import React from 'react'; +import { AutoHeightWrapper } from '@deriv/components'; import { WS, isVerificationServiceSupported, formatIDVFormValues } from '@deriv/shared'; -import Unsupported from 'Components/poi/status/unsupported'; +import { useStore, observer } from '@deriv/stores'; +import Unsupported from '../../../Components/poi/status/unsupported'; import OnfidoUpload from './onfido-sdk-view-container'; import { identity_status_codes, submission_status_code, service_code } from './proof-of-identity-utils'; -import { IdvDocSubmitOnSignup } from 'Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup'; -import { AutoHeightWrapper } from '@deriv/components'; -import { makeSettingsRequest } from 'Helpers/utils'; +import { IdvDocSubmitOnSignup } from '../../../Components/poi/poi-form-on-signup/idv-doc-submit-on-signup/idv-doc-submit-on-signup'; +import { makeSettingsRequest } from '../../../Helpers/utils'; -const POISubmissionForMT5 = ({ - account_settings, - getChangeableFields, - idv, - is_idv_disallowed, - onfido, - onStateChange, - refreshNotifications, - citizen_data, - has_idv_error, - residence_list, - is_eu_user, -}) => { - const [submission_status, setSubmissionStatus] = React.useState(); // submitting - const [submission_service, setSubmissionService] = React.useState(); - React.useEffect(() => { - if (citizen_data) { - const { submissions_left: idv_submissions_left } = idv; - const { submissions_left: onfido_submissions_left } = onfido; - const is_idv_supported = isVerificationServiceSupported(residence_list, account_settings, 'idv'); - const is_onfido_supported = isVerificationServiceSupported(residence_list, account_settings, 'onfido'); - if (is_idv_supported && Number(idv_submissions_left) > 0 && !is_idv_disallowed && !is_eu_user) { - setSubmissionService(service_code.idv); - } else if (onfido_submissions_left > 0 && is_onfido_supported) { - setSubmissionService(service_code.onfido); - } else { - setSubmissionService(service_code.manual); - } - setSubmissionStatus(submission_status_code.submitting); - } - }, [citizen_data]); +const POISubmissionForMT5 = observer( + ({ idv, is_idv_disallowed, onfido, onStateChange, citizen_data, has_idv_error, residence_list }) => { + const [submission_status, setSubmissionStatus] = React.useState(); // submitting + const [submission_service, setSubmissionService] = React.useState(); - const handlePOIComplete = () => { - if (onStateChange && typeof onStateChange === 'function') { - onStateChange(identity_status_codes.pending); - } - WS.authorized.getAccountStatus().then(() => { - refreshNotifications(); - }); - }; + const { client, notifications, traders_hub } = useStore(); + const { account_settings, getChangeableFields } = client; + const { refreshNotifications } = notifications; + const { is_eu_user } = traders_hub; - const handleIdvSubmit = async (values, { setSubmitting, setErrors }) => { - setSubmitting(true); + React.useEffect(() => { + if (citizen_data) { + const { submissions_left: idv_submissions_left } = idv; + const { submissions_left: onfido_submissions_left } = onfido; + const is_idv_supported = isVerificationServiceSupported(residence_list, account_settings, 'idv'); + const is_onfido_supported = isVerificationServiceSupported(residence_list, account_settings, 'onfido'); + if (is_idv_supported && Number(idv_submissions_left) > 0 && !is_idv_disallowed && !is_eu_user) { + setSubmissionService(service_code.idv); + } else if (onfido_submissions_left && is_onfido_supported) { + setSubmissionService(service_code.onfido); + } else { + setSubmissionService(service_code.manual); + } + setSubmissionStatus(submission_status_code.submitting); + } + }, [citizen_data]); - const request = makeSettingsRequest(values, [...getChangeableFields()]); + const handlePOIComplete = () => { + if (onStateChange && typeof onStateChange === 'function') { + onStateChange(identity_status_codes.pending); + } + WS.authorized.getAccountStatus().then(() => { + refreshNotifications(); + }); + }; - const data = await WS.setSettings(request); + const handleIdvSubmit = async (values, { setSubmitting, setErrors }) => { + setSubmitting(true); - if (data.error) { - setErrors({ error_message: data.error.message }); - setSubmitting(false); - return; - } - const get_settings = WS.authorized.storage.getSettings(); + const request = makeSettingsRequest(values, [...getChangeableFields()]); - if (get_settings.error) { - setErrors({ error_message: get_settings.error.message }); - setSubmitting(false); - return; - } + const data = await WS.setSettings(request); - const submit_data = { - identity_verification_document_add: 1, - ...formatIDVFormValues(values, citizen_data.value), - }; + if (data.error) { + setErrors({ error_message: data.error.message }); + setSubmitting(false); + return; + } + const get_settings = WS.authorized.storage.getSettings(); - WS.send(submit_data).then(response => { - setSubmitting(false); - if (response.error) { - setErrors({ error_message: response.error.message }); + if (get_settings.error) { + setErrors({ error_message: get_settings.error.message }); + setSubmitting(false); return; } - handlePOIComplete(); - }); - }; - if (submission_status === submission_status_code.submitting) { - switch (submission_service) { - case service_code.idv: - return ( - - ); - case service_code.onfido: { - const country_code = citizen_data.value; - const doc_obj = citizen_data.identity.services.onfido.documents_supported; - const documents_supported = Object.keys(doc_obj).map(d => doc_obj[d].display_name); + const submit_data = { + identity_verification_document_add: 1, + ...formatIDVFormValues(values, citizen_data.value), + }; + + WS.send(submit_data).then(response => { + setSubmitting(false); + if (response.error) { + setErrors({ error_message: response.error.message }); + return; + } + handlePOIComplete(); + }); + }; + + if (submission_status === submission_status_code.submitting) { + switch (submission_service) { + case service_code.idv: + return ( + + ); + case service_code.onfido: { + const country_code = citizen_data.value; + const doc_obj = citizen_data.identity.services.onfido.documents_supported; + const documents_supported = Object.keys(doc_obj).map(d => doc_obj[d].display_name); - return ( - - {({ setRef, height }) => ( -
- -
- )} -
- ); + return ( + + {({ setRef, height }) => ( +
+ +
+ )} +
+ ); + } + case service_code.manual: + return ( + + ); + default: + return null; } - case service_code.manual: - return ( - - ); - default: - return null; + } else { + return null; } - } else { - return null; } -}; +); export default POISubmissionForMT5; diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx index 3f4efcae812f..06c5653d8a04 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-submission.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { WS } from '@deriv/shared'; -import CountrySelector from 'Components/poi/poi-country-selector'; +import { observer, useStore } from '@deriv/stores'; +import PoiCountrySelector from 'Components/poi/poi-country-selector'; import IdvDocumentSubmit from 'Components/poi/idv-document-submit'; import IdvUploadComplete from 'Components/poi/idv-status/idv-submit-complete'; import Unsupported from 'Components/poi/status/unsupported'; @@ -8,199 +9,205 @@ import UploadComplete from 'Components/poi/status/upload-complete'; import OnfidoUpload from './onfido-sdk-view-container'; import { identity_status_codes, submission_status_code, service_code } from './proof-of-identity-utils'; -const POISubmission = ({ - account_settings, - allow_poi_resubmission, - has_require_submission, - height, - getChangeableFields, - identity_last_attempt, - idv, - is_from_external, - is_idv_disallowed, - needs_poa, - onfido, - onStateChange, - redirect_button, - refreshNotifications, - residence_list, - setIsCfdPoiCompleted, -}) => { - const [submission_status, setSubmissionStatus] = React.useState(); // selecting, submitting, complete - const [submission_service, setSubmissionService] = React.useState(); - const [selected_country, setSelectedCountry] = React.useState({}); +const POISubmission = observer( + ({ + allow_poi_resubmission, + has_require_submission, + height, + identity_last_attempt, + idv, + is_from_external, + is_idv_disallowed, + needs_poa, + onfido, + onStateChange, + redirect_button, + residence_list, + setIsCfdPoiCompleted, + }) => { + const [submission_status, setSubmissionStatus] = React.useState(); // selecting, submitting, complete + const [submission_service, setSubmissionService] = React.useState(); + const [selected_country, setSelectedCountry] = React.useState({}); + + const { client, common, notifications } = useStore(); - const handleSelectionNext = () => { - if (Object.keys(selected_country).length) { - const { submissions_left: idv_submissions_left } = idv; - const { submissions_left: onfido_submissions_left } = onfido; - const is_idv_supported = selected_country.identity.services.idv.is_country_supported; - const is_onfido_supported = - selected_country.identity.services.onfido.is_country_supported && selected_country.value !== 'ng'; + const { account_settings, getChangeableFields } = client; + const { current_language } = common; + const { refreshNotifications } = notifications; - if (is_idv_supported && Number(idv_submissions_left) > 0 && !is_idv_disallowed) { - setSubmissionService(service_code.idv); - } else if (onfido_submissions_left && is_onfido_supported) { - setSubmissionService(service_code.onfido); - } else { - setSubmissionService(service_code.manual); + const handleSelectionNext = () => { + if (Object.keys(selected_country).length) { + const { submissions_left: idv_submissions_left } = idv; + const { submissions_left: onfido_submissions_left } = onfido; + const is_idv_supported = selected_country.identity.services.idv.is_country_supported; + const is_onfido_supported = + selected_country.identity.services.onfido.is_country_supported && selected_country.value !== 'ng'; + + if (is_idv_supported && Number(idv_submissions_left) > 0 && !is_idv_disallowed) { + setSubmissionService(service_code.idv); + } else if (onfido_submissions_left && is_onfido_supported) { + setSubmissionService(service_code.onfido); + } else { + setSubmissionService(service_code.manual); + } + setSubmissionStatus(submission_status_code.submitting); } - setSubmissionStatus(submission_status_code.submitting); - } - }; + }; - const handleViewComplete = () => { - if (onStateChange && typeof onStateChange === 'function') { - onStateChange(identity_status_codes.pending); - } - setSubmissionStatus(submission_status_code.complete); + const handleViewComplete = () => { + if (onStateChange && typeof onStateChange === 'function') { + onStateChange(identity_status_codes.pending); + } + setSubmissionStatus(submission_status_code.complete); - WS.authorized.getAccountStatus().then(() => { - refreshNotifications(); - }); - }; + WS.authorized.getAccountStatus().then(() => { + refreshNotifications(); + }); + }; - const handleBack = () => setSubmissionStatus(submission_status_code.selecting); + const handleBack = () => setSubmissionStatus(submission_status_code.selecting); - const getCountryFromResidence = React.useCallback( - country_code => residence_list.find(residence => residence.value === country_code), - [residence_list] - ); + const getCountryFromResidence = React.useCallback( + country_code => residence_list.find(residence => residence.value === country_code), + [residence_list] + ); - React.useEffect(() => { - if (submission_status !== submission_status_code.complete) { - if ((has_require_submission || allow_poi_resubmission) && identity_last_attempt) { - switch (identity_last_attempt.service) { - case service_code.idv: { - if (Number(idv.submissions_left) > 0 || Number(onfido.submissions_left) > 0) { - setSubmissionStatus(submission_status_code.selecting); - } else { - setSubmissionService(service_code.manual); - setSubmissionStatus(submission_status_code.submitting); + React.useEffect(() => { + if (submission_status !== submission_status_code.complete) { + if ((has_require_submission || allow_poi_resubmission) && identity_last_attempt) { + switch (identity_last_attempt.service) { + case service_code.idv: { + if (Number(idv.submissions_left) > 0 || Number(onfido.submissions_left) > 0) { + setSubmissionStatus(submission_status_code.selecting); + } else { + setSubmissionService(service_code.manual); + setSubmissionStatus(submission_status_code.submitting); + } + break; } - break; - } - case service_code.onfido: { - if (Number(onfido.submissions_left) > 0) { - setSubmissionStatus(submission_status_code.selecting); - } else { - setSubmissionService(service_code.manual); + case service_code.onfido: { + if (Number(onfido.submissions_left) > 0) { + setSubmissionStatus(submission_status_code.selecting); + } else { + setSubmissionService(service_code.manual); + setSubmissionStatus(submission_status_code.submitting); + } + break; + } + case service_code.manual: { + setSelectedCountry(getCountryFromResidence(identity_last_attempt.country_code)); setSubmissionStatus(submission_status_code.submitting); + setSubmissionService(service_code.manual); + break; } - break; - } - case service_code.manual: { - setSelectedCountry(getCountryFromResidence(identity_last_attempt.country_code)); - setSubmissionStatus(submission_status_code.submitting); - setSubmissionService(service_code.manual); - break; + default: + break; } - default: - break; + } else { + setSubmissionStatus(submission_status_code.selecting); } - } else { - setSubmissionStatus(submission_status_code.selecting); } - } - }, [ - allow_poi_resubmission, - getCountryFromResidence, - has_require_submission, - identity_last_attempt, - idv.submissions_left, - onfido.submissions_left, - ]); + }, [ + allow_poi_resubmission, + getCountryFromResidence, + has_require_submission, + identity_last_attempt, + idv.submissions_left, + onfido.submissions_left, + ]); - switch (submission_status) { - case submission_status_code.selecting: { - return ( - - ); - } - case submission_status_code.submitting: { - switch (submission_service) { - case service_code.idv: - return ( - - ); - case service_code.onfido: { - const country_code = selected_country.value; - const doc_obj = selected_country.identity.services.onfido.documents_supported; - const documents_supported = Object.keys(doc_obj).map(d => doc_obj[d].display_name); + switch (submission_status) { + case submission_status_code.selecting: { + return ( + + ); + } + case submission_status_code.submitting: { + switch (submission_service) { + case service_code.idv: + return ( + + ); + case service_code.onfido: { + const country_code = selected_country.value; + const doc_obj = selected_country.identity.services.onfido.documents_supported; + const documents_supported = Object.keys(doc_obj).map(d => doc_obj[d].display_name); - return ( - - ); + return ( + + ); + } + case service_code.manual: + return ( + + ); + default: + return null; } - case service_code.manual: - return ( - - ); - default: - return null; } - } - case submission_status_code.complete: { - switch (submission_service) { - case service_code.idv: - return ( - - ); - // This will be replaced in the next Manual Upload Project - case service_code.manual: - return ( - - ); - case service_code.onfido: - return ( - - ); - default: - return null; + case submission_status_code.complete: { + switch (submission_service) { + case service_code.idv: + return ( + + ); + // This will be replaced in the next Manual Upload Project + case service_code.manual: + return ( + + ); + case service_code.onfido: + return ( + + ); + default: + return null; + } } + default: + return null; } - default: - return null; } -}; +); export default POISubmission; diff --git a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx index ebcbd6f37dcf..5cb47dcf0905 100644 --- a/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx +++ b/packages/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity.jsx @@ -2,25 +2,9 @@ import { AutoHeightWrapper } from '@deriv/components'; import ProofOfIdentityContainer from './proof-of-identity-container.jsx'; import React from 'react'; import { changeMetaTagWithOG } from '@deriv/shared'; -import { observer, useStore } from '@deriv/stores'; import { withRouter } from 'react-router-dom'; -const ProofOfIdentity = observer(({ is_from_external, onStateChange }) => { - const { client, common, notifications } = useStore(); - const { - account_status, - account_settings, - fetchResidenceList, - getChangeableFields, - is_switching, - is_high_risk, - is_withdrawal_lock, - should_allow_authentication, - is_virtual, - updateAccountStatus, - } = client; - const { refreshNotifications } = notifications; - const { app_routing_history, routeBackInApp } = common; +const ProofOfIdentity = ({ is_from_external, onStateChange }) => { // next useEffect implements seo requirements React.useEffect(() => { const description_content = 'Submit your proof of identity documents to verify your account and start trading'; @@ -42,28 +26,14 @@ const ProofOfIdentity = observer(({ is_from_external, onStateChange }) => {
)} ); -}); +}; export default withRouter(ProofOfIdentity); diff --git a/packages/account/src/Types/common.type.ts b/packages/account/src/Types/common.type.ts index f591db338894..c36c0ac8c715 100644 --- a/packages/account/src/Types/common.type.ts +++ b/packages/account/src/Types/common.type.ts @@ -153,7 +153,11 @@ export type TInputFieldValues = Record; export type TIDVVerificationResponse = IdentityVerificationAddDocumentResponse & { error: { message: string } }; -export type TDocumentList = { +export type TVerificationStatus = Readonly< + Record<'none' | 'pending' | 'rejected' | 'verified' | 'expired' | 'suspected', string> +>; + +export type TDocument = { id: string; text: string; value?: string; @@ -163,10 +167,10 @@ export type TDocumentList = { display_name?: string; example_format?: string; }; -}[]; +}; -type TFormProps = { - document_type: TDocumentList[0]; +export type TIDVFormValues = { + document_type: TDocument; document_number: string; document_additional?: string; error_message?: string; @@ -178,18 +182,7 @@ export type TIDVForm = { class_name?: string; can_skip_document_verification: boolean; } & Partial & - FormikProps; - -export type TVerificationStatus = Readonly< - Record<'none' | 'pending' | 'rejected' | 'verified' | 'expired' | 'suspected', string> ->; - -export type TIDVFormValues = { - document_type: TDocumentList[0]; - document_number: string; - document_additional?: string; - error_message?: string; -}; + FormikProps; export type TServerError = { code: string; diff --git a/packages/cfd/src/Components/__tests__/cfd-poi.spec.js b/packages/cfd/src/Components/__tests__/cfd-poi.spec.js index c9a2f2620cbd..cb081f2cb04a 100644 --- a/packages/cfd/src/Components/__tests__/cfd-poi.spec.js +++ b/packages/cfd/src/Components/__tests__/cfd-poi.spec.js @@ -4,17 +4,14 @@ import CFDPOI from '../cfd-poi'; import CFDProviders from '../../cfd-providers'; import { mockStore } from '@deriv/stores'; -jest.mock('@deriv/account', () => ({ - ...jest.requireActual('@deriv/account'), - ProofOfIdentityContainerForMt5: () =>
ProofOfIdentityContainerForMt5
, -})); +jest.mock('@deriv/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5.jsx', () => + jest.fn(() =>
ProofOfIdentityContainerForMt5
) +); describe('', () => { let props; let mockRootStore; - const ProofOfIdentityContainerForMt5 = 'ProofOfIdentityContainerForMt5'; - beforeEach(() => { mockRootStore = { client: { @@ -104,6 +101,6 @@ describe('', () => { render(, { wrapper: ({ children }) => {children}, }); - expect(screen.getByText(ProofOfIdentityContainerForMt5)).toBeInTheDocument(); + expect(screen.getByText('ProofOfIdentityContainerForMt5')).toBeInTheDocument(); }); }); diff --git a/packages/cfd/src/Components/cfd-poi.tsx b/packages/cfd/src/Components/cfd-poi.tsx index 6e6bc04981c1..67ced113919b 100644 --- a/packages/cfd/src/Components/cfd-poi.tsx +++ b/packages/cfd/src/Components/cfd-poi.tsx @@ -1,4 +1,5 @@ -import { ProofOfIdentityContainerForMt5 } from '@deriv/account'; +// @ts-expect-error remove this line when ProofOfIdentityContainerForMt5 is converted to TS +import ProofOfIdentityContainerForMt5 from '@deriv/account/src/Sections/Verification/ProofOfIdentity/proof-of-identity-container-for-mt5.jsx'; import React from 'react'; import { useStore, observer } from '@deriv/stores'; import type { TCoreStores } from '@deriv/stores/types'; @@ -23,44 +24,9 @@ export type TCFDPOIProps = { jurisdiction_selected_shortcode: string; }; -const CFDPOI = observer(({ index, onSave, onSubmit, height, ...props }: TCFDPOIProps) => { - const { client, common, notifications, traders_hub } = useStore(); - - const { - account_status, - fetchResidenceList, - is_switching, - is_virtual, - is_high_risk, - is_withdrawal_lock, - should_allow_authentication, - account_settings, - residence_list, - getChangeableFields, - updateAccountStatus, - } = client; - const { routeBackInApp, app_routing_history } = common; - const { refreshNotifications } = notifications; - const { is_eu_user } = traders_hub; - - const poi_props = { - account_status, - fetchResidenceList, - is_switching, - is_virtual, - is_high_risk, - is_withdrawal_lock, - should_allow_authentication, - account_settings, - residence_list, - routeBackInApp, - app_routing_history, - refreshNotifications, - getChangeableFields, - updateAccountStatus, - is_eu_user, - ...props, - }; +const CFDPOI = observer(({ index, onSave, onSubmit, ...props }: TCFDPOIProps) => { + const { client } = useStore(); + const { account_settings, residence_list } = client; const [poi_state, setPOIState] = React.useState('none'); const citizen = account_settings?.citizen || account_settings?.country_code; @@ -73,9 +39,7 @@ const CFDPOI = observer(({ index, onSave, onSubmit, height, ...props }: TCFDPOIP }; return ( onStateChange(status)} citizen_data={citizen_data} /> diff --git a/packages/components/src/components/autocomplete/autocomplete.tsx b/packages/components/src/components/autocomplete/autocomplete.tsx index 8f15ecf253ac..232a26c0ad1c 100644 --- a/packages/components/src/components/autocomplete/autocomplete.tsx +++ b/packages/components/src/components/autocomplete/autocomplete.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; import React from 'react'; -import { matchStringByChar, getPosition } from '@deriv/shared'; +import classNames from 'classnames'; +import { matchStringByChar, getPosition, getSearchNotFoundOption } from '@deriv/shared'; import Icon from '../icon'; import Input from '../input'; import DropdownList, { TItem } from '../dropdown-list'; @@ -22,7 +22,7 @@ type TAutocompleteProps = { list_height: string; list_items: TItem[]; list_portal_id: string; - not_found_text?: string; + not_found_text: string; onBlur?: (e: React.FocusEvent) => void; onHideDropdownList: () => void; onItemSelection: (item: TItem) => void; @@ -59,20 +59,22 @@ const getFilteredItems = (val: string, list: TItem[], should_filter_by_char = fa ); }; const Autocomplete = React.memo((props: TAutocompleteProps) => { + const NO_SEARCH_RESULT = getSearchNotFoundOption(); const { autoComplete, - data_testid, className, + data_testid, dropdown_offset, - historyValue, error, has_updating_list = true, hide_list = false, + historyValue, input_id, is_alignment_top, is_list_visible = false, list_items, list_portal_id, + not_found_text = NO_SEARCH_RESULT, onHideDropdownList, onItemSelection, onScrollStop, @@ -80,7 +82,6 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { should_filter_by_char, show_list = false, value, - not_found_text = 'No results found', ...other_props } = props; @@ -104,8 +105,8 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { let new_filtered_items = []; if (is_list_visible) { - if (typeof props.onSearch === 'function') { - new_filtered_items = props.onSearch(value.toLowerCase(), list_items); + if (typeof other_props.onSearch === 'function') { + new_filtered_items = other_props.onSearch(value.toLowerCase(), list_items); } else { new_filtered_items = getFilteredItems(value.toLowerCase(), list_items); } @@ -156,11 +157,11 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { setStyle(position_style.style); } - }, [should_show_list, is_alignment_top, list_portal_id, filtered_items.length]); + }, [should_show_list, is_alignment_top, list_portal_id, filtered_items?.length]); const handleScrollStop = (e: React.UIEvent) => { // pass onScrollStop func callback when scrolling stops - if (!props.onScrollStop) return; + if (!onScrollStop) return; const element = e.currentTarget; scroll_top_position = element.scrollTop; @@ -168,7 +169,7 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { clearTimeout(scroll_timeout); } scroll_timeout = setTimeout(() => { - props.onScrollStop?.(); + onScrollStop?.(); }, 150); }; @@ -267,13 +268,13 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { if (!is_list_visible) setFilteredItems(list_items); if (input_value === '') { - props.onItemSelection?.({ - text: props.not_found_text, + onItemSelection?.({ + text: not_found_text, value: '', }); } - if (typeof props.onBlur === 'function') { - props.onBlur(e); + if (typeof other_props.onBlur === 'function') { + other_props.onBlur(e); } }; @@ -282,29 +283,29 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { setInputValue((typeof item === 'object' ? item.text : item) || ''); - props.onItemSelection?.(item); + onItemSelection?.(item); }; const showDropdownList = () => { setShouldShowList(true); - props.onShowDropdownList?.(); + onShowDropdownList?.(); }; const hideDropdownList = () => { setShouldShowList(false); - props.onHideDropdownList?.(); + onHideDropdownList?.(); }; const filterList = (e: React.FormEvent) => { const val = (e.target as HTMLInputElement).value.toLowerCase(); let new_filtered_items = []; - if (typeof props.onSearch === 'function') { - new_filtered_items = props.onSearch(val, props.list_items); + if (typeof other_props.onSearch === 'function') { + new_filtered_items = other_props.onSearch(val, list_items); } else { - new_filtered_items = getFilteredItems(val, props.list_items, should_filter_by_char); + new_filtered_items = getFilteredItems(val, list_items, should_filter_by_char); } if (!new_filtered_items.length) { @@ -367,12 +368,12 @@ const Autocomplete = React.memo((props: TAutocompleteProps) => { }} is_visible={should_show_list || is_list_visible} list_items={filtered_items} - list_height={props.list_height} + list_height={other_props.list_height} // Autocomplete must use the `text` property and not the `value`, however DropdownList provides access to both onItemSelection={onSelectItem} setActiveIndex={setActiveIndex} onScrollStop={handleScrollStop} - not_found_text={props.not_found_text} + not_found_text={not_found_text} portal_id={list_portal_id} />
diff --git a/packages/components/src/components/dropdown-list/dropdown-list.tsx b/packages/components/src/components/dropdown-list/dropdown-list.tsx index 1187bf2c23b4..2f4adb4df2c1 100644 --- a/packages/components/src/components/dropdown-list/dropdown-list.tsx +++ b/packages/components/src/components/dropdown-list/dropdown-list.tsx @@ -76,7 +76,7 @@ const ListItem = ({ is_active, is_disabled, index, item, child_ref, onItemSelect const ListItems = React.forwardRef((props, ref) => { const { active_index, list_items, is_object_list, onItemSelection, setActiveIndex, not_found_text } = props; - const is_grouped_list = list_items.some(list_item => typeof list_item === 'object' && !!list_item.group); + const is_grouped_list = list_items?.some(list_item => typeof list_item === 'object' && !!list_item.group); if (is_grouped_list) { const groups: { [key: string]: TItem[] } = {}; @@ -126,7 +126,7 @@ const ListItems = React.forwardRef((props, ref) => { return ( <> - {list_items.length ? ( + {list_items?.length ? ( list_items.map((item, item_idx) => ( { portal_id, } = props; - if (list_items.length && typeof list_items[0] !== 'string' && typeof list_items[0] !== 'object') { + if (list_items?.length && typeof list_items[0] !== 'string' && typeof list_items[0] !== 'object') { throw Error('Dropdown received wrong data structure'); } const is_object = !Array.isArray(list_items) && typeof list_items === 'object'; - const is_string_array = list_items.length && typeof list_items[0] === 'string'; + const is_string_array = list_items?.length && typeof list_items[0] === 'string'; const el_dropdown_list = ( ({ + id: 'none', + text: localize('I want to do this later'), + value: 'none', +}); + +/** + * Returns default value for the text to render when there are no matching results. + */ +export const getSearchNotFoundOption = () => localize('No results found'); diff --git a/packages/shared/src/utils/constants/idv-options.ts b/packages/shared/src/utils/constants/idv-options.ts deleted file mode 100644 index 8edf847066a9..000000000000 --- a/packages/shared/src/utils/constants/idv-options.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { localize } from '@deriv/translations'; - -export const getIDVNotApplicableOption = () => ({ - id: 'none', - text: localize('I want to do this later'), - value: 'none', -}); diff --git a/packages/shared/src/utils/constants/index.ts b/packages/shared/src/utils/constants/index.ts index 9ba271478d6d..d9817697f166 100644 --- a/packages/shared/src/utils/constants/index.ts +++ b/packages/shared/src/utils/constants/index.ts @@ -1,7 +1,7 @@ export * from './barriers'; export * from './contract'; export * from './content_flags'; -export * from './idv-options'; +export * from './default-options'; export * from './jurisdictions-config'; export * from './signup_fields'; export * from './error';