Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
e058da3
interactive step wrapper extended to match all use cases
burczu Sep 25, 2024
5ed587c
refactor: using InteractiveStepWrapper wherever applies
burczu Sep 25, 2024
ee229c8
missing personal details refactor: using separate form providers
burczu Oct 2, 2024
21a39de
missing personal details page final refactor
burczu Oct 3, 2024
d0b68c9
styling fixed
burczu Oct 7, 2024
6c9be43
missing personal details adjusted to use interactive step wrapper
burczu Oct 7, 2024
2b3b92b
handling draft and form correctly
burczu Oct 7, 2024
f9f8d2c
Merge branch 'main' into feature/20459-sub-steps-refactor
burczu Oct 8, 2024
26cd6ae
common full name step component created
burczu Oct 14, 2024
7e1c89e
switched to use common full name step component in reimbursement account
burczu Oct 15, 2024
1f849db
switched to use common full name step component in enable payments page
burczu Oct 15, 2024
b47e29a
switched to use common full name step component in missing personal d…
burczu Oct 15, 2024
b262e8e
switched to use common full name step component in beneficial owners …
burczu Oct 15, 2024
ac3fdbe
common full name step moved to dedicated folder
burczu Oct 16, 2024
6377b87
common date of birth step component added
burczu Oct 16, 2024
bae4112
common date of birth step used in the enable payments page
burczu Oct 16, 2024
544754c
common date of birth step component used in missing personal details …
burczu Oct 16, 2024
77fe445
common date of birth step component added to reimbursement account
burczu Oct 16, 2024
23e579f
common date of birth component used in beneficial owner page
burczu Oct 16, 2024
bba1405
common address step component created
burczu Oct 18, 2024
9ecd3e1
common address step component typings fixed and improved
burczu Oct 18, 2024
29e2b09
common address step component used in beneficial owner page
burczu Oct 20, 2024
f3aa42c
common address step component used in enable payments page
burczu Oct 20, 2024
d4cece1
common address step component used in personal info page
burczu Oct 20, 2024
3c283d5
switched to generic form id typing for common full name comp usages
burczu Oct 20, 2024
3d1e3ba
prettier fix
burczu Oct 20, 2024
09494cc
switched to generic form id typing for common date of birth comp usages
burczu Oct 20, 2024
7c04b0e
common single field step component created
burczu Oct 21, 2024
ea46862
common single field step used for personal info page
burczu Oct 21, 2024
1e99139
common single step component used in the beneficial owner page
burczu Oct 21, 2024
c8401d4
fix: defaultName changed to displayName
burczu Oct 21, 2024
edbf6a2
common single field step component used wherever possible
burczu Oct 21, 2024
0977917
missing input mode added
burczu Oct 21, 2024
8033f10
missing should show help links prop in date of birth comp fixed
burczu Oct 21, 2024
d141c3e
some cleanup
burczu Oct 21, 2024
6085301
common confirmation step component created
burczu Oct 22, 2024
a3b2b18
enable payments page confirmation step component refactored
burczu Oct 22, 2024
90311ef
common confirmation component used in missing pers details page
burczu Oct 22, 2024
d14c626
onfido links title fixed
burczu Oct 22, 2024
88446cb
common confirmation step component used in beneficial owner page
burczu Oct 22, 2024
436493c
common confirmation step component used in personal info page
burczu Oct 22, 2024
8e66633
prettier cleanup
burczu Oct 22, 2024
460a617
Merge branch 'main' into feature/20459-sub-steps-refactor
burczu Oct 22, 2024
7033339
changes in interactive step sub-header reverted
burczu Oct 22, 2024
401fa5f
prettier fix
burczu Oct 23, 2024
3f27c15
unused hook removed
burczu Oct 23, 2024
0136b74
unused form action methods removed
burczu Oct 23, 2024
a623536
unnecessary eslint comment removed
burczu Oct 23, 2024
ad2e71f
eslint errors fixed
burczu Oct 23, 2024
f7fa8f3
switched from withOnyx to useOnyx wherever necessary in changed files
burczu Oct 23, 2024
7b6c991
prettier fixes
burczu Oct 23, 2024
9eb3121
missing max length property handled in single field step component
burczu Oct 23, 2024
0245768
missing key property added
burczu Oct 28, 2024
d9d1060
validation issue in the address common substep fixed
burczu Oct 28, 2024
4b63495
Merge branch 'main' into feature/20459-sub-steps-refactor
burczu Oct 28, 2024
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
45 changes: 40 additions & 5 deletions src/components/InteractiveStepWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import React, {forwardRef} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
import {View} from 'react-native';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
Expand All @@ -24,21 +25,55 @@ type InteractiveStepWrapperProps = {

// Array of step names
stepNames?: readonly string[];

// Should enable max height
shouldEnableMaxHeight?: boolean;

// Should show offline indicator
shouldShowOfflineIndicator?: boolean;

// Should enable picker avoiding
shouldEnablePickerAvoiding?: boolean;

// Call task ID for the guides
guidesCallTaskID?: string;

// Offline indicator style
offlineIndicatorStyle?: StyleProp<ViewStyle>;
};

function InteractiveStepWrapper({children, wrapperID, handleBackButtonPress, headerTitle, startStepIndex, stepNames}: InteractiveStepWrapperProps) {
function InteractiveStepWrapper(
{
children,
wrapperID,
handleBackButtonPress,
headerTitle,
startStepIndex,
stepNames,
shouldEnableMaxHeight,
shouldShowOfflineIndicator,
shouldEnablePickerAvoiding = false,
guidesCallTaskID,
offlineIndicatorStyle,
}: InteractiveStepWrapperProps,
ref: React.ForwardedRef<View>,
) {
const styles = useThemeStyles();

return (
<ScreenWrapper
ref={ref}
testID={wrapperID}
includeSafeAreaPaddingBottom={false}
shouldEnablePickerAvoiding={false}
shouldEnableMaxHeight
shouldEnablePickerAvoiding={shouldEnablePickerAvoiding}
shouldEnableMaxHeight={shouldEnableMaxHeight}
shouldShowOfflineIndicator={shouldShowOfflineIndicator}
offlineIndicatorStyle={offlineIndicatorStyle}
>
<HeaderWithBackButton
title={headerTitle}
onBackButtonPress={handleBackButtonPress}
guidesCallTaskID={guidesCallTaskID}
/>
{stepNames && (
<View style={[styles.ph5, styles.mb5, styles.mt3, {height: CONST.BANK_ACCOUNT.STEPS_HEADER_HEIGHT}]}>
Expand All @@ -55,4 +90,4 @@ function InteractiveStepWrapper({children, wrapperID, handleBackButtonPress, hea

InteractiveStepWrapper.displayName = 'InteractiveStepWrapper';

export default InteractiveStepWrapper;
export default forwardRef(InteractiveStepWrapper);
111 changes: 111 additions & 0 deletions src/components/SubStepForms/AddressStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, {useCallback} from 'react';
import {View} from 'react-native';
import FormProvider from '@components/Form/FormProvider';
import type {FormInputErrors, FormOnyxKeys, FormOnyxValues, FormValue} from '@components/Form/types';
import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
import * as ValidationUtils from '@libs/ValidationUtils';
import AddressFormFields from '@pages/ReimbursementAccount/AddressFormFields';
import HelpLinks from '@pages/ReimbursementAccount/PersonalInfo/HelpLinks';
import type {OnyxFormValuesMapping} from '@src/ONYXKEYS';

type AddressValues = {
street: string;
city: string;
state: string;
zipCode: string;
};

type AddressStepProps<TFormID extends keyof OnyxFormValuesMapping> = SubStepProps & {
/** The ID of the form */
formID: TFormID;

/** The title of the form */
formTitle: string;

/** The disclaimer informing that PO box is not allowed */
formPOBoxDisclaimer?: string;

/** The validation function to call when the form is submitted */
customValidate?: (values: FormOnyxValues<TFormID>) => FormInputErrors<TFormID>;

/** A function to call when the form is submitted */
onSubmit: (values: FormOnyxValues<TFormID>) => void;

/** Fields list of the form */
stepFields: Array<FormOnyxKeys<TFormID>>;

/* The IDs of the input fields */
inputFieldsIDs: AddressValues;

/** The default values for the form */
defaultValues: AddressValues;

/** Should show help links */
shouldShowHelpLinks?: boolean;
};

function AddressStep<TFormID extends keyof OnyxFormValuesMapping>({
formID,
formTitle,
formPOBoxDisclaimer,
customValidate,
onSubmit,
stepFields,
inputFieldsIDs,
defaultValues,
shouldShowHelpLinks,
isEditing,
}: AddressStepProps<TFormID>) {
const {translate} = useLocalize();
const styles = useThemeStyles();

const validate = useCallback(
(values: FormOnyxValues<TFormID>): FormInputErrors<TFormID> => {
const errors = ValidationUtils.getFieldRequiredErrors(values, stepFields);

const street = values[inputFieldsIDs.street as keyof typeof values];
if (street && !ValidationUtils.isValidAddress(street as FormValue)) {
// @ts-expect-error type mismatch to be fixed
errors[inputFieldsIDs.street] = translate('bankAccount.error.addressStreet');
}

const zipCode = values[inputFieldsIDs.zipCode as keyof typeof values];
if (zipCode && !ValidationUtils.isValidZipCode(zipCode as string)) {
// @ts-expect-error type mismatch to be fixed
errors[inputFieldsIDs.zipCode] = translate('bankAccount.error.zipCode');
}

return errors;
},
[inputFieldsIDs.street, inputFieldsIDs.zipCode, stepFields, translate],
);

return (
<FormProvider
formID={formID}
submitButtonText={translate(isEditing ? 'common.confirm' : 'common.next')}
validate={customValidate ?? validate}
onSubmit={onSubmit}
style={[styles.mh5, styles.flexGrow1]}
Copy link
Contributor

@alitoshmatov alitoshmatov Apr 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personal information substeps should work offline, #57385

>
<View>
<Text style={[styles.textHeadlineLineHeightXXL, styles.mb3]}>{formTitle}</Text>
{formPOBoxDisclaimer && <Text style={[styles.textSupporting]}>{formPOBoxDisclaimer}</Text>}
<AddressFormFields
inputKeys={inputFieldsIDs}
streetTranslationKey="common.streetAddress"
defaultValues={defaultValues}
shouldSaveDraft={!isEditing}
/>
{shouldShowHelpLinks && <HelpLinks containerStyles={[styles.mt6]} />}
</View>
</FormProvider>
);
}

AddressStep.displayName = 'AddressStep';

export default AddressStep;
118 changes: 118 additions & 0 deletions src/components/SubStepForms/ConfirmationStep.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React from 'react';
import {View} from 'react-native';
import Button from '@components/Button';
import DotIndicatorMessage from '@components/DotIndicatorMessage';
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
import SafeAreaConsumer from '@components/SafeAreaConsumer';
import ScrollView from '@components/ScrollView';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import useLocalize from '@hooks/useLocalize';
import useNetwork from '@hooks/useNetwork';
import type {SubStepProps} from '@hooks/useSubStep/types';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';

type SummaryItem = {
description: string;
title: string;
shouldShowRightIcon: boolean;
onPress: () => void;
};

type ConfirmationStepProps = SubStepProps & {
/** The title of the step */
pageTitle: string;

/** The summary items to display */
summaryItems: SummaryItem[];

/** Whether show additional section with Onfido terms etc. */
showOnfidoLinks: boolean;

/** The title of the Onfido section */
onfidoLinksTitle?: string;

/** Whether the data is loading */
isLoading?: boolean;

/** The error message to display */
error?: string;
};

function ConfirmationStep({pageTitle, summaryItems, showOnfidoLinks, onfidoLinksTitle, isLoading, error, onNext}: ConfirmationStepProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {isOffline} = useNetwork();

return (
<SafeAreaConsumer>
{({safeAreaPaddingBottomStyle}) => (
<ScrollView
style={styles.pt0}
contentContainerStyle={[styles.flexGrow1, safeAreaPaddingBottomStyle]}
>
<Text style={[styles.textHeadlineLineHeightXXL, styles.ph5, styles.mb3]}>{pageTitle}</Text>
{summaryItems.map(({description, title, shouldShowRightIcon, onPress}) => (
<MenuItemWithTopDescription
key={`${title}_${description}`}
description={description}
title={title}
shouldShowRightIcon={shouldShowRightIcon}
onPress={onPress}
/>
))}

{showOnfidoLinks && (
<Text style={[styles.mt3, styles.ph5, styles.textMicroSupporting]}>
{onfidoLinksTitle}
<TextLink
href={CONST.ONFIDO_FACIAL_SCAN_POLICY_URL}
style={[styles.textMicro]}
>
{translate('onfidoStep.facialScan')}
</TextLink>
{', '}
<TextLink
href={CONST.ONFIDO_PRIVACY_POLICY_URL}
style={[styles.textMicro]}
>
{translate('common.privacy')}
</TextLink>
{` ${translate('common.and')} `}
<TextLink
href={CONST.ONFIDO_TERMS_OF_SERVICE_URL}
style={[styles.textMicro]}
>
{translate('common.termsOfService')}
</TextLink>
</Text>
)}

<View style={[styles.ph5, styles.pb5, styles.flexGrow1, styles.justifyContentEnd]}>
{error && error.length > 0 && (
<DotIndicatorMessage
textStyles={[styles.formError]}
type="error"
messages={{error}}
/>
)}
<Button
isDisabled={isOffline}
success
large
isLoading={isLoading}
style={[styles.w100]}
onPress={onNext}
text={translate('common.confirm')}
/>
</View>
</ScrollView>
)}
</SafeAreaConsumer>
);
}

ConfirmationStep.displayName = 'ConfirmationStep';

export default ConfirmationStep;
Loading