Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kyc / wall-1551: CTA 'Next' button enhancement #10010

Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c9bcfc6
feat: scroll to field with error component
Sep 8, 2023
9dd4cf0
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 11, 2023
6f5a0ae
chore: account signup next behaviour improve
Sep 12, 2023
b795fb8
fix: resolve conflicts
Sep 12, 2023
43ba253
chore: personal details on signup field order chnage
Sep 12, 2023
402c229
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 13, 2023
f7796ad
chore: improving behavior for address details page in account signup …
Sep 13, 2023
9941a2f
chore: errors fields order
Sep 13, 2023
55fd157
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 14, 2023
689dc1e
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 15, 2023
45208b5
chore: should_scroll_to_error_field flag added
Sep 15, 2023
ce5d97d
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 15, 2023
e2a8962
chore: custom hook for getting error field and scroll to it
Sep 15, 2023
08f2e9f
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 15, 2023
6a460ae
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 18, 2023
4c90919
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 18, 2023
c3a6105
chore: remove one field error and hool
Sep 18, 2023
19b5b6a
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Sep 19, 2023
890d2da
chore: review comments
Sep 19, 2023
aa639e6
fix: resolve conflicts
Oct 3, 2023
e88f22b
chore: remove shaking, added error message
Oct 4, 2023
a3d5989
fix: resolve conflicts
Oct 4, 2023
addaabc
chore: KYC-586/ client cannot add personal details after switching fr…
Oct 6, 2023
68f5048
fix: resolve conflicts, update from master
Oct 6, 2023
a8c3442
fix: resolve conflicts, update from master
Oct 6, 2023
c9a7ba1
fix: checkbox handling field
Oct 6, 2023
16b1785
chore: add chechbox to formik value
Oct 6, 2023
f86042c
fix: checkbox field name
Oct 7, 2023
f362df7
chore: eu account confirmation fix
Oct 8, 2023
db941a6
chore: confirmation checkbox for non eu scroll
Oct 9, 2023
8ffdbcf
chore: description for scrolling component
Oct 9, 2023
c4fb3e3
chore: mobile scroll to errors
Oct 9, 2023
096e489
chore: handling status for checkbox
Oct 9, 2023
9b61dec
chore: review comments
Oct 10, 2023
d892d5b
chore: confirmation refactoring
Oct 11, 2023
cdf5093
chore: error message, scroll for checkboxes
Oct 12, 2023
6a1f1d0
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Oct 12, 2023
0eee70a
trigger build
Oct 12, 2023
c944666
Merge branch 'master' into kyc/wall-1551/cta_next_button_enhancement
likhith-deriv Oct 13, 2023
d4e63f0
chore: review comments
Oct 13, 2023
1e10a18
fix: failing tests
Oct 13, 2023
8228817
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Oct 19, 2023
2751e53
fix: unnecessary field removed from data to BE
Oct 19, 2023
2ee51b7
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Oct 20, 2023
b257680
fix: placeholders color with error
Oct 20, 2023
82048e0
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Oct 23, 2023
a6d11ae
fix: red error placegolder for input
Oct 23, 2023
198ed2e
Merge branch 'master' of github.com:binary-com/deriv-app into kyc/wal…
Oct 23, 2023
f3a9c72
fix: failing tests
Oct 23, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { getLocation } from '@deriv/shared';
import { observer, useStore } from '@deriv/stores';
import { localize, Localize } from '@deriv/translations';
import { FormInputField } from '../forms/form-fields';
import ScrollToFieldWithError from '../forms/scroll-to-field-with-error';
import { splitValidationResultTypes } from '../real-account-signup/helpers/utils';

export type TAddressDetailFormProps = {
Expand Down Expand Up @@ -58,6 +59,7 @@ type TAddressDetails = {
selected_step_ref?: React.RefObject<FormikProps<TAddressDetailFormProps>>;
value: TAddressDetailFormProps;
has_real_account: boolean;
should_scroll_to_error_field?: boolean;
};

type TAutoComplete = {
Expand Down Expand Up @@ -96,6 +98,7 @@ const AddressDetails = observer(
selected_step_ref,
disabled_items,
has_real_account,
should_scroll_to_error_field = false,
...props
}: TAddressDetails) => {
const [address_state_to_display, setAddressStateToDisplay] = React.useState('');
Expand Down Expand Up @@ -144,6 +147,7 @@ const AddressDetails = observer(
{({
handleSubmit,
errors,
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
isSubmitting,
values,
setFieldValue,
handleChange,
Expand All @@ -157,12 +161,13 @@ const AddressDetails = observer(
setRef: (instance: HTMLFormElement) => void;
height: number | string;
}) => (
<form ref={setRef} onSubmit={handleSubmit}>
<form ref={setRef} onSubmit={handleSubmit} noValidate>
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
<Div100vhContainer
className='details-form'
height_offset='90px'
is_disabled={is_desktop}
>
{should_scroll_to_error_field && <ScrollToFieldWithError />}
<Text
as='p'
align='left'
Expand Down Expand Up @@ -304,7 +309,9 @@ const AddressDetails = observer(
</Div100vhContainer>
<Modal.Footer has_separator is_bypassed={is_mobile}>
<FormSubmitButton
is_disabled={isSubmitDisabled(errors)}
is_disabled={
should_scroll_to_error_field ? isSubmitting : isSubmitDisabled(errors)
}
label={localize('Next')}
is_absolute={is_mobile}
has_cancel
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { isDesktop, isMobile } from '@deriv/shared';
import { Localize, localize } from '@deriv/translations';
import FinancialInformation from './financial-details-partials';
import { splitValidationResultTypes } from '../real-account-signup/helpers/utils';
import ScrollToFieldWithError from '../forms/scroll-to-field-with-error';

type TFinancialDetailsFormValues = {
income_source: string;
Expand Down Expand Up @@ -78,7 +79,11 @@ const FinancialDetails = (props: TFinancialDetails) => {
setRef: (instance: HTMLFormElement) => void;
height?: number | string;
}) => (
<form ref={setRef} onSubmit={handleSubmit}>
<form ref={setRef} onSubmit={handleSubmit} noValidate>
<ScrollToFieldWithError
fields_to_scroll_top={['income_source', isMobile() ? 'account_turnover' : '']}
fields_to_scroll_end={isMobile() ? undefined : ['account_turnover']}
/>
<Div100vhContainer
className={classNames('details-form', 'financial-assessment')}
height_offset='110px'
Expand All @@ -100,7 +105,7 @@ const FinancialDetails = (props: TFinancialDetails) => {
</Div100vhContainer>
<Modal.Footer has_separator is_bypassed={isMobile()}>
<FormSubmitButton
is_disabled={isSubmitting || Object.keys(errors).length > 0}
is_disabled={isSubmitting}
is_absolute={isMobile()}
label={localize('Next')}
has_cancel
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { useFormikContext } from 'formik';

import { Checkbox, Text } from '@deriv/components';
import { FormikValues, useFormikContext } from 'formik';
import { Checkbox } from '@deriv/components';
import { isMobile } from '@deriv/shared';

/**
Expand All @@ -11,11 +10,11 @@ type TConfirmationCheckboxProps = {
/**
* The label of the checkbox.
*/
label: React.ReactNode;
label: string | React.ReactElement;
/**
* The size of the checkbox label.
*/
label_size?: 'xxxxs' | 'xxxs' | 'xxs' | 'xs' | 's' | 'sm' | 'm' | 'l' | 'xl' | 'xxl';
label_font_size?: 'xxxxs' | 'xxxs' | 'xxs' | 'xs' | 's' | 'sm' | 'm' | 'l' | 'xl' | 'xxl';
disabled?: boolean;
};

Expand All @@ -31,30 +30,30 @@ type TConfirmationCheckboxProps = {
* @name ConfirmationCheckbox
* @returns {JSX.Element} React component that renders a checkbox with a label
*/
export const ConfirmationCheckbox = ({
label,
label_size,
disabled = false,
}: TConfirmationCheckboxProps): JSX.Element => {
export const ConfirmationCheckbox = ({ label, label_font_size, disabled = false }: TConfirmationCheckboxProps) => {
/**
* The formik context for the current form.
*
* This context provides information about the form's state and helps in managing form behavior.
*/
const { setStatus, status } = useFormikContext();
const { setFieldValue, setStatus, status, values, touched, errors } = useFormikContext<FormikValues>();

const handleChange = () => {
// check if status is an object to avoid overwriting the status if it is a string
if (typeof status === 'object') setStatus({ ...status, is_confirmed: !status?.is_confirmed });
setFieldValue('confirmation_checkbox', !values.confirmation_checkbox);
};

return (
<Checkbox
name='confirmation_checkbox'
className='formik__confirmation-checkbox'
value={status?.is_confirmed ?? false}
label={<Text size={label_size ?? (isMobile() ? 'xxs' : 'xs')}>{label}</Text>}
label={label}
label_font_size={label_font_size ?? (isMobile() ? 'xxs' : 'xs')}
disabled={disabled}
onChange={handleChange}
has_error={!!(touched.confirmation_checkbox && errors.confirmation_checkbox)}
/>
);
};
21 changes: 14 additions & 7 deletions packages/account/src/Components/forms/personal-details-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@ import {
} from '@deriv/components';
import { getLegalEntityName, isDesktop, isMobile, routes, validPhone } from '@deriv/shared';
import { Localize, localize } from '@deriv/translations';

import PoiNameDobExample from '../../Assets/ic-poi-name-dob-example.svg';
import ConfirmationCheckbox from './confirmation-checkbox';
import { isFieldImmutable } from '../../Helpers/utils';
import { getEmploymentStatusList } from '../../Sections/Assessment/FinancialAssessment/financial-information-list';
import FormBodySection from '../form-body-section';
import { DateOfBirthField, FormInputField } from './form-fields';
import FormSubHeader from '../form-sub-header';
import InlineNoteWithIcon from '../inline-note-with-icon';

import ConfirmationCheckbox from './confirmation-checkbox';
import { DateOfBirthField, FormInputField } from './form-fields';
import { getEmploymentStatusList } from '../../Sections/Assessment/FinancialAssessment/financial-information-list';

const PersonalDetailsForm = props => {
const {
Expand Down Expand Up @@ -66,10 +64,15 @@ const PersonalDetailsForm = props => {
}, [should_close_tooltip, handleToolTipStatus, setShouldCloseTooltip]);

React.useEffect(() => {
if (no_confirmation_needed && typeof status === 'object' && !status.is_confirmed) {
if (
!no_confirmation_needed &&
typeof status === 'object' &&
!status.is_confirmed &&
values.confirmation_checkbox
) {
setStatus({ ...status, is_confirmed: true });
}
}, [no_confirmation_needed, setStatus, status]);
}, [no_confirmation_needed, setStatus, status, values.confirmation_checkbox]);

const getNameAndDobLabels = () => {
const is_asterisk_needed = is_svg || is_mf || is_rendered_for_onfido || is_qualified_for_idv;
Expand Down Expand Up @@ -206,6 +209,7 @@ const PersonalDetailsForm = props => {
disabled={
!!values.salutation && isFieldImmutable('salutation', editable_fields)
}
has_error={touched.salutation && errors.salutation}
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
/>
))}
</RadioGroup>
Expand Down Expand Up @@ -529,6 +533,9 @@ const PersonalDetailsForm = props => {
)}
withTabIndex={0}
data-testid='tax_identification_confirm'
has_error={
!!(touched.tax_identification_confirm && errors.tax_identification_confirm)
}
/>
)}
</React.Fragment>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import { useFormikContext } from 'formik';

type TScrollToFieldWithError = {
/**
* The fields_to_scroll_top is to scroll to top for exact fields.
*/
fields_to_scroll_top?: string[];
/**
* The fields_to_scroll_end is to scroll to end for exact fields.
*/
fields_to_scroll_end?: string[];
/**
* The should_recollect_inputs_names is to recollect all inputs on the page .
*/
should_recollect_inputs_names?: boolean;
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
};

/**
* A ScrollToFieldWithError for scrolling to field with error.
*
* **Note**: This component is supposed to be used with Formik.
* To use scrolling to field with error, you have to use this component within Formik.
*
* @name ScrollToFieldWithError
* @returns {null} React component that renders nothing
*/
const ScrollToFieldWithError = ({
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
fields_to_scroll_top,
fields_to_scroll_end,
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
should_recollect_inputs_names = false,
}: TScrollToFieldWithError) => {
const [all_page_inputs_names, setAllPageInputsNames] = React.useState<string[]>([]);
const formik = useFormikContext();
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
const is_submitting = formik.isSubmitting;
const scrollToElement = (element_name: string, block: ScrollLogicalPosition = 'center') => {
const el = document.querySelector(`[name="${element_name}"]`) as HTMLInputElement;
(el?.parentElement ?? el)?.scrollIntoView({ behavior: 'smooth', block });
el?.focus();
};

React.useEffect(() => {
const inputs = [...document.querySelectorAll('input, select')] as HTMLInputElement[];
setAllPageInputsNames(inputs.map(input => input.name));
}, [should_recollect_inputs_names]);
React.useEffect(() => {
let current_error_field_name = '';

for (let i = 0; i <= all_page_inputs_names.length; i++) {
if (Object.hasOwn(formik.errors, all_page_inputs_names[i])) {
current_error_field_name = all_page_inputs_names[i];
break;
}
}
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved

if (fields_to_scroll_top?.includes(current_error_field_name)) {
scrollToElement(current_error_field_name, 'start');
} else if (fields_to_scroll_end?.includes(current_error_field_name)) {
scrollToElement(current_error_field_name, 'end');
} else {
scrollToElement(current_error_field_name);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
}, [is_submitting]);

return null;
};

export default ScrollToFieldWithError;
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ import {
shouldShowIdentityInformation,
} from 'Helpers/utils';

import FormSubHeader from '../form-sub-header';
import IDVForm from '../forms/idv-form';
import PersonalDetailsForm from '../forms/personal-details-form';
import { splitValidationResultTypes } from '../real-account-signup/helpers/utils';
import FormSubHeader from '../form-sub-header';
import ScrollToFieldWithError from '../forms/scroll-to-field-with-error';

const PersonalDetails = ({
getCurrentStep,
Expand All @@ -45,10 +46,12 @@ const PersonalDetails = ({
selected_step_ref,
closeRealAccountSignup,
has_real_account,
should_scroll_to_error_field = false,
...props
}) => {
const { account_status, account_settings, residence, real_account_signup_target } = props;
const [should_close_tooltip, setShouldCloseTooltip] = React.useState(false);
const [no_confirmation_needed, setNoConfirmationNeeded] = React.useState(false);

const isSubmitDisabled = errors => {
return selected_step_ref?.current?.isSubmitting || Object.keys(errors).length > 0;
Expand Down Expand Up @@ -83,10 +86,19 @@ const PersonalDetails = ({
}

errors.document_number = isDocumentNumberValid(document_number, document_type);

if (document_type.id !== IDV_NOT_APPLICABLE_OPTION.id && !values.confirmation_checkbox) {
errors.confirmation_checkbox = 'error';
}
return removeEmptyPropertiesFromObject(errors);
};

const handleValidate = values => {
if (values?.document_type?.id === IDV_NOT_APPLICABLE_OPTION.id) {
setNoConfirmationNeeded(true);
} else {
setNoConfirmationNeeded(false);
}
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
let idv_error = {};
if (is_qualified_for_idv) {
idv_error = validateIDV(values);
Expand Down Expand Up @@ -126,21 +138,41 @@ const PersonalDetails = ({
validate={handleValidate}
validateOnMount
enableReinitialize
initialStatus={{ is_confirmed: !is_qualified_for_idv }}
initialStatus={{ is_confirmed: no_confirmation_needed || !is_qualified_for_idv }}
onSubmit={(values, actions) => {
onSubmit(getCurrentStep() - 1, values, actions.setSubmitting, goToNextStep);
}}
>
{({ handleSubmit, errors, setFieldValue, touched, values, handleChange, handleBlur, status }) => (
{({
handleSubmit,
errors,
isSubmitting,
setFieldValue,
touched,
values,
handleChange,
handleBlur,
status,
}) => (
<AutoHeightWrapper default_height={380} height_offset={isDesktop() ? 81 : null}>
{({ setRef, height }) => (
<Form
noValidate
ref={setRef}
onSubmit={handleSubmit}
autoComplete='off'
onClick={closeToolTip}
data-testid='personal_details_form'
>
{should_scroll_to_error_field && (
<ScrollToFieldWithError
fields_to_scroll_end={isMobile() ? '' : ['account_opening_reason']}
fields_to_scroll_top={isMobile() ? ['account_opening_reason'] : ''}
yauheni-deriv marked this conversation as resolved.
Show resolved Hide resolved
should_recollect_inputs_names={
values?.document_type?.id === IDV_NOT_APPLICABLE_OPTION.id
}
/>
)}
<Div100vhContainer className='details-form' height_offset='100px' is_disabled={isDesktop()}>
{!is_qualified_for_idv && (
<Text as='p' size='xxxs' align='center' className='details-form__description'>
Expand Down Expand Up @@ -200,9 +232,7 @@ const PersonalDetails = ({
should_close_tooltip={should_close_tooltip}
setShouldCloseTooltip={setShouldCloseTooltip}
should_hide_helper_image={shouldHideHelperImage(values?.document_type?.id)}
no_confirmation_needed={
values?.document_type?.id === IDV_NOT_APPLICABLE_OPTION.id
}
no_confirmation_needed={no_confirmation_needed}
/>
</div>
</ThemedScrollbars>
Expand All @@ -211,7 +241,11 @@ const PersonalDetails = ({
<FormSubmitButton
cancel_label={localize('Previous')}
has_cancel
is_disabled={!status?.is_confirmed || isSubmitDisabled(errors)}
is_disabled={
should_scroll_to_error_field
? isSubmitting
: !status?.is_confirmed || isSubmitDisabled(errors)
}
is_absolute={isMobile()}
label={localize('Next')}
onCancel={() => handleCancel(values)}
Expand Down
Loading
Loading