From a097aa01e619fd07d15bf0aa6c1bc683d04d079b Mon Sep 17 00:00:00 2001 From: Joe Karow <58997957+JoeKarow@users.noreply.github.com> Date: Fri, 8 Sep 2023 16:09:21 -0400 Subject: [PATCH] error state handling WIP --- .../data-portal/PhoneNumberEntry.stories.tsx | 5 +- .../data-portal/PhoneNumberEntryHookForm.tsx | 134 +++++++++++++----- 2 files changed, 101 insertions(+), 38 deletions(-) diff --git a/packages/ui/components/data-portal/PhoneNumberEntry.stories.tsx b/packages/ui/components/data-portal/PhoneNumberEntry.stories.tsx index 5d3643c2ed..c239280b00 100644 --- a/packages/ui/components/data-portal/PhoneNumberEntry.stories.tsx +++ b/packages/ui/components/data-portal/PhoneNumberEntry.stories.tsx @@ -24,7 +24,9 @@ const HookFormContextDecorator = (Story: StoryFn) => { const form = useHookForm() return ( - +
+ +
) } @@ -33,6 +35,7 @@ export default { component: PhoneNumberEntry, parameters: { msw: [fieldOpt.countries], + rqDevtools: true, }, } satisfies Meta diff --git a/packages/ui/components/data-portal/PhoneNumberEntryHookForm.tsx b/packages/ui/components/data-portal/PhoneNumberEntryHookForm.tsx index 1870c5ec4c..3a295e655e 100644 --- a/packages/ui/components/data-portal/PhoneNumberEntryHookForm.tsx +++ b/packages/ui/components/data-portal/PhoneNumberEntryHookForm.tsx @@ -1,10 +1,19 @@ -import { createStyles, Group, rem, Text } from '@mantine/core' -import { AsYouType, type CountryCode } from 'libphonenumber-js' -import { type ComponentPropsWithoutRef, forwardRef, useEffect, useState } from 'react' -import { type FieldValues, useFormContext, useWatch } from 'react-hook-form' -import { Select, type SelectProps, TextInput, type TextInputProps } from 'react-hook-form-mantine' -import PhoneInput, { parsePhoneNumber, type Props as PhoneInputProps } from 'react-phone-number-input/input' -import { type SetOptional } from 'type-fest' +import { DevTool } from '@hookform/devtools' +import { ErrorMessage } from '@hookform/error-message' +import { createStyles, Group, rem, Text, TextInput, type TextInputProps } from '@mantine/core' +import { AsYouType } from 'libphonenumber-js' +import { type ComponentPropsWithoutRef, forwardRef, useEffect, useMemo } from 'react' +import { type FieldValues, type Path, useFormContext, useFormState } from 'react-hook-form' +import { + Select, + type SelectProps, + // TextInput, + // type TextInputProps, +} from 'react-hook-form-mantine' +import PhoneInput, { + // parsePhoneNumber, + type Props as PhoneInputProps, +} from 'react-phone-number-input/react-hook-form-input' import { type ApiOutput } from '@weareinreach/api' import { isCountryCode } from '~ui/hooks/usePhoneNumber' @@ -16,8 +25,10 @@ const PhoneCountrySelectItem = forwardRef { const { name } = data return ( - - {`${label} ${name}`} + + {label} + {name} + {/* {`${label} ${name}`} */} ) } @@ -26,7 +37,7 @@ PhoneCountrySelectItem.displayName = 'PhoneCountrySelectItem' const useCountrySelectStyles = createStyles((theme) => ({ dropdown: { - width: 'fit-content !important', + width: 'max-content !important', left: 'unset !important', // right: 0, }, @@ -57,16 +68,16 @@ export const PhoneNumberEntry = ({ const form = useFormContext() // const selectedCountry = useWatch({control: countrySelectProps.control, name: countrySelectProps.name}) const selectedCountry = form.watch(countrySelectProps.name) + const phoneNumber = form.watch(allPhoneEntryProps.name) // const [selectedCountry, setSelectedCountry] = useState() const { classes: countrySelectClasses } = useCountrySelectStyles() const { classes: phoneEntryClasses } = usePhoneEntryStyles() const { setError: setPhoneError, - value: phoneValue, - onChange: onPhoneChange, + // value: phoneValue, + // onChange: onPhoneChange, ...phoneEntryProps } = allPhoneEntryProps - const topCountries = ['US', 'CA', 'MX'] const { data: countryList } = api.fieldOpt.countries.useQuery( @@ -90,7 +101,13 @@ export const PhoneNumberEntry = ({ }), } ) - const phoneFormatter = new AsYouType(selectedCountry) + const activeCountry = useMemo(() => { + const result = countryList?.find(({ value }) => value === selectedCountry)?.data.cca2 + if (result && isCountryCode(result)) return result + return undefined + }, [selectedCountry, countryList]) + + const phoneFormatter = new AsYouType(activeCountry) // useEffect(() => { // const { data } = countryList.find(({ value }) => value === countrySelectProps.value) ?? {} // if (data?.cca2 && isCountryCode(data.cca2)) setSelectedCountry(data.cca2) @@ -99,23 +116,45 @@ export const PhoneNumberEntry = ({ // }, [countrySelectProps.value]) useEffect(() => { - if (phoneValue) { - phoneFormatter.input(phoneValue) + if (phoneNumber) { + phoneFormatter.input(phoneNumber) const phoneCountry = phoneFormatter.getNumber()?.country - if (phoneCountry && phoneCountry !== selectedCountry) { + + console.log( + `🚀 ~ file: PhoneNumberEntryHookForm.tsx:124 ~ useEffect ~ phoneCountry/selectedCountry:`, + phoneCountry, + selectedCountry + ) + const formError = form.formState.errors + console.log(formError) + if ((!phoneCountry && !selectedCountry) || phoneCountry !== selectedCountry) { const countryId = countryList.find(({ data }) => data.cca2 === phoneCountry)?.value + if (countryId) { + console.log('setting countryId') + // @ts-expect-error -> string is okay. form.setValue(countrySelectProps.name, countryId) if (countrySelectProps.onChange && typeof countrySelectProps.onChange === 'function') { countrySelectProps.onChange(countryId) } - } else if (typeof setPhoneError === 'function') { - setPhoneError(`Country not active: ${phoneCountry}`) + } else if (!phoneCountry && formError[phoneEntryProps.name]) { + console.log('clearing error') + form.resetField(countrySelectProps.name) + form.clearErrors(phoneEntryProps.name) + // // @ts-expect-error -> null is okay. + // form.setValue(countrySelectProps.name, null) + } else if (phoneCountry && !countryId) { + console.log('setting error') + form.setError( + phoneEntryProps.name, + { message: `Country not active: ${phoneCountry}` }, + { shouldFocus: true } + ) } } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [phoneValue]) + }, [phoneNumber]) const countrySelection = (