From f4991ba4bdf13418dfa03d24d2dd3537b3414645 Mon Sep 17 00:00:00 2001 From: MrMuzyk Date: Fri, 26 Jan 2024 15:51:03 +0100 Subject: [PATCH] feat: Change business type selector from dropdown to list (#13) * feat: Change business type selector from dropdown to list * fix: typos * fix: remove unnecessary draft * fix: cr fixes --- .../BusinessInfo/BusinessInfo.tsx | 2 +- .../BusinessTypeSelectorModal.tsx | 78 +++++++++++++++++ .../TypeBusiness/BusinessTypePicker/index.tsx | 86 +++++++++++++++++++ .../TypeBusiness/BusinessTypePicker/types.ts | 12 +++ .../{ => TypeBusiness}/TypeBusiness.tsx | 22 ++--- 5 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/BusinessTypeSelectorModal.tsx create mode 100644 src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/index.tsx create mode 100644 src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/types.ts rename src/pages/ReimbursementAccount/BusinessInfo/substeps/{ => TypeBusiness}/TypeBusiness.tsx (77%) diff --git a/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx b/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx index e4d2e326d148..c7b0f42c330d 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/BusinessInfo.tsx @@ -24,7 +24,7 @@ import IncorporationStateBusiness from './substeps/IncorporationStateBusiness'; import NameBusiness from './substeps/NameBusiness'; import PhoneNumberBusiness from './substeps/PhoneNumberBusiness'; import TaxIdBusiness from './substeps/TaxIdBusiness'; -import TypeBusiness from './substeps/TypeBusiness'; +import TypeBusiness from './substeps/TypeBusiness/TypeBusiness'; import WebsiteBusiness from './substeps/WebsiteBusiness'; type BusinessInfoOnyxProps = { diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/BusinessTypeSelectorModal.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/BusinessTypeSelectorModal.tsx new file mode 100644 index 000000000000..f163ec52fee0 --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/BusinessTypeSelectorModal.tsx @@ -0,0 +1,78 @@ +import React, {useMemo} from 'react'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import Modal from '@components/Modal'; +import ScreenWrapper from '@components/ScreenWrapper'; +import SelectionList from '@components/SelectionList'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import CONST from '@src/CONST'; +import type {BusinessTypeItemType, IncorporationType} from './types'; + +type BusinessTypeSelectorModalProps = { + /** Whether the modal is visible */ + isVisible: boolean; + + /** Business type value selected */ + currentBusinessType: string; + + /** Function to call when the user selects a business type */ + onBusinessTypeSelected: (value: BusinessTypeItemType) => void; + + /** Function to call when the user closes the business type selector modal */ + onClose: () => void; + + /** Label to display on field */ + label: string; +}; + +function BusinessTypeSelectorModal({isVisible, currentBusinessType, onBusinessTypeSelected, onClose, label}: BusinessTypeSelectorModalProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const incorporationTypes = useMemo( + () => + Object.keys(CONST.INCORPORATION_TYPES).map((key) => ({ + value: key, + text: translate(`businessInfoStep.incorporationType.${key as IncorporationType}`), + keyForList: key, + isSelected: key === currentBusinessType, + })), + [currentBusinessType, translate], + ); + + return ( + + + + + + + ); +} + +BusinessTypeSelectorModal.displayName = 'BusinessTypeSelectorModal'; + +export default BusinessTypeSelectorModal; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/index.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/index.tsx new file mode 100644 index 000000000000..3e41afbc324e --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/index.tsx @@ -0,0 +1,86 @@ +import React, {useState} from 'react'; +import type {StyleProp, ViewStyle} from 'react-native'; +import {View} from 'react-native'; +import FormHelpMessage from '@components/FormHelpMessage'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import BusinessTypeSelectorModal from './BusinessTypeSelectorModal'; +import type {BusinessTypeItemType, IncorporationType} from './types'; + +type BusinessTypePickerProps = { + /** Error text to display */ + errorText?: string; + + /** Business type to display */ + value: string; + + /** Callback to call when the input changes */ + onInputChange: (value: string) => void; + + /** Label to display on field */ + label: string; + + /** Any additional styles to apply */ + wrapperStyle: StyleProp; + + /** Callback to call when the picker modal is dismissed */ + onBlur: () => void; +}; + +function BusinessTypePicker({errorText = '', value, wrapperStyle, onInputChange, label, onBlur}: BusinessTypePickerProps) { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + + const [isPickerVisible, setIsPickerVisible] = useState(false); + + const showPickerModal = () => { + setIsPickerVisible(true); + }; + + const hidePickerModal = (shouldBlur = true) => { + if (shouldBlur) { + onBlur(); + } + setIsPickerVisible(false); + }; + + const updateBusinessTypeInput = (businessTypeItem: BusinessTypeItemType) => { + if (businessTypeItem.value !== value) { + onInputChange(businessTypeItem.value); + } + // If the user selects any business type, call the hidePickerModal function with shouldBlur = false + // to prevent the onBlur function from being called. + hidePickerModal(false); + }; + + const title = value ? translate(`businessInfoStep.incorporationType.${value as IncorporationType}`) : ''; + const descStyle = title.length === 0 ? styles.textNormal : null; + + return ( + + + + + + + + ); +} + +BusinessTypePicker.displayName = 'BusinessTypePicker'; + +export default BusinessTypePicker; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/types.ts b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/types.ts new file mode 100644 index 000000000000..82f2ccc129ac --- /dev/null +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/BusinessTypePicker/types.ts @@ -0,0 +1,12 @@ +import type CONST from '@src/CONST'; + +type IncorporationType = keyof typeof CONST.INCORPORATION_TYPES; + +type BusinessTypeItemType = { + value: string; + text: string; + keyForList: string; + isSelected: boolean; +}; + +export type {IncorporationType, BusinessTypeItemType}; diff --git a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness.tsx b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/TypeBusiness.tsx similarity index 77% rename from src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness.tsx rename to src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/TypeBusiness.tsx index 5c2f23ea27b4..9ac0582aacad 100644 --- a/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness.tsx +++ b/src/pages/ReimbursementAccount/BusinessInfo/substeps/TypeBusiness/TypeBusiness.tsx @@ -3,7 +3,6 @@ import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import FormProvider from '@components/Form/FormProvider'; import InputWrapper from '@components/Form/InputWrapper'; -import Picker from '@components/Picker'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useReimbursementAccountStepFormSubmit from '@hooks/useReimbursementAccountStepFormSubmit'; @@ -15,6 +14,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {ReimbursementAccount} from '@src/types/onyx'; import type {FormValues} from '@src/types/onyx/Form'; import type * as OnyxCommon from '@src/types/onyx/OnyxCommon'; +import BusinessTypePicker from './BusinessTypePicker'; type TypeBusinessOnyxProps = { /** Reimbursement account from ONYX */ @@ -23,16 +23,14 @@ type TypeBusinessOnyxProps = { type TypeBusinessProps = TypeBusinessOnyxProps & SubStepProps; -type IncorporationType = keyof typeof CONST.INCORPORATION_TYPES; - const COMPANY_INCORPORATION_TYPE_KEY = CONST.BANK_ACCOUNT.BUSINESS_INFO_STEP.INPUT_KEY.INCORPORATION_TYPE; const STEP_FIELDS = [COMPANY_INCORPORATION_TYPE_KEY]; -const validate = (values: FormValues): OnyxCommon.Errors => ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); - function TypeBusiness({reimbursementAccount, onNext, isEditing}: TypeBusinessProps) { const {translate} = useLocalize(); const styles = useThemeStyles(); + const validate = (values: FormValues): OnyxCommon.Errors => ValidationUtils.getFieldRequiredErrors(values, STEP_FIELDS); + const defaultIncorporationType = reimbursementAccount?.achData?.incorporationType ?? ''; const handleSubmit = useReimbursementAccountStepFormSubmit({ @@ -48,23 +46,19 @@ function TypeBusiness({reimbursementAccount, onNext, isEditing}: TypeBusinessPro submitButtonText={translate(isEditing ? 'common.confirm' : 'common.next')} validate={validate} onSubmit={handleSubmit} - style={[styles.mh5, styles.flexGrow1]} - submitButtonStyles={[styles.pb5, styles.mb0]} + style={[styles.flexGrow1]} + submitButtonStyles={[styles.p5, styles.mb0]} > - {translate('businessInfoStep.selectYourCompanysType')} + {translate('businessInfoStep.selectYourCompanysType')} ({ - value: key, - label: translate(`businessInfoStep.incorporationType.${key as IncorporationType}`), - }))} - placeholder={{value: '', label: '-'}} defaultValue={defaultIncorporationType} shouldSaveDraft={!isEditing} + wrapperStyle={[styles.ph5, styles.mt4]} /> );