From 64b7fe92d6a184b16ae0310589a3c8a6ef047234 Mon Sep 17 00:00:00 2001 From: Pedro Ladaria Date: Thu, 3 Oct 2024 17:16:56 +0200 Subject: [PATCH] =?UTF-8?q?Revert=20"feat(PhoneNumberField):=20Custom=20fo?= =?UTF-8?q?rmatter=20support=20+=20lazy=20load=20libphone=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2ee88e9643998c84ca724469948f256802a5209e. --- src/__tests__/phone-number-field-test.tsx | 81 +------------------- src/phone-number-field.tsx | 93 +++++------------------ src/test-utils/ssr.tsx | 5 -- src/text-field-base.tsx | 2 +- 4 files changed, 21 insertions(+), 160 deletions(-) diff --git a/src/__tests__/phone-number-field-test.tsx b/src/__tests__/phone-number-field-test.tsx index 9406312a91..c7d7bbe02c 100644 --- a/src/__tests__/phone-number-field-test.tsx +++ b/src/__tests__/phone-number-field-test.tsx @@ -1,7 +1,8 @@ import * as React from 'react'; -import {Form, PhoneNumberField, ThemeContextProvider} from '..'; -import {render, screen, waitFor} from '@testing-library/react'; +import {PhoneNumberField} from '..'; +import {render, screen} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import ThemeContextProvider from '../theme-context-provider'; import {makeTheme} from './test-utils'; test.each` @@ -46,79 +47,3 @@ test.each` expect(onChangeValueSpy).toHaveBeenLastCalledWith(expectedValue, expectedValueRaw); } ); - -test.each` - contextRegionCode | prefix | typed | expectedValue | expectedValueRaw - ${'ES'} | ${undefined} | ${'+123123123'} | ${'123123123'} | ${'1-2-3-1-2-3-1-2-3'} - ${'ES'} | ${'+49'} | ${'69654321'} | ${'69654321'} | ${'6-9-6-5-4-3-2-1'} - ${'DE'} | ${undefined} | ${'069654321'} | ${'069654321'} | ${'0-6-9-6-5-4-3-2-1'} -`( - `PhoneNumberField with custom formatter ($contextRegionCode, $prefix, $typed, $expectedValue, $expectedValueRaw)`, - async ({contextRegionCode, prefix, typed, expectedValue, expectedValueRaw}) => { - const onChangeValueSpy = jest.fn(); - const theme = makeTheme(); - - render( - - { - // dumb formatter that just adds a dash between each digit - return number.replace(/[^\d]/g, '').split('').join('-'); - }} - /> - - ); - - await userEvent.type(screen.getByLabelText('Enter Phone'), typed); - - expect(onChangeValueSpy).toHaveBeenLastCalledWith(expectedValue, expectedValueRaw); - } -); - -test('PhoneNumberField gets formatted when libphonenumber loads', async () => { - const onChangeValueSpy = jest.fn(); - const theme = makeTheme(); - - const TestComponent = () => { - const [isLib, setIsLib] = React.useState(false); - return ( - -
{}}> - number} - /> - - -
- ); - }; - - render(); - - await userEvent.type(screen.getByLabelText('Enter Phone'), '654834455'); - - expect(onChangeValueSpy).toHaveBeenLastCalledWith('654834455', '654834455'); - - await userEvent.click(screen.getByText('enable libphonenumber')); - - await waitFor(() => { - expect(onChangeValueSpy).toHaveBeenLastCalledWith('654834455', '654 83 44 55'); - }); -}); diff --git a/src/phone-number-field.tsx b/src/phone-number-field.tsx index 63c9de4215..fa2a04be73 100644 --- a/src/phone-number-field.tsx +++ b/src/phone-number-field.tsx @@ -1,6 +1,7 @@ 'use client'; import * as React from 'react'; import {useRifm} from 'rifm'; +import {formatAsYouType, formatToE164, parse, getRegionCodeForCountryCode} from '@telefonica/libphonenumber'; import {useFieldProps} from './form-context'; import {TextFieldBaseAutosuggest} from './text-field-base'; import {useTheme} from './hooks'; @@ -10,14 +11,8 @@ import {combineRefs} from './utils/common'; import type {CommonFormFieldProps} from './text-field-base'; import type {RegionCode} from './utils/region-code'; -let libphonenumber: typeof import('@telefonica/libphonenumber'); - -type NumberFormatter = (number: string, regionCode: RegionCode) => string; - -const formatPhoneDummy: NumberFormatter = (number) => number; - -const formatPhoneUsingLibphonenumber: NumberFormatter = (number, regionCode) => - libphonenumber.formatAsYouType(number.replace(/[^\d+*#]/g, ''), regionCode); +const formatPhone = (regionCode: RegionCode, number: string): string => + formatAsYouType(number.replace(/[^\d+*#]/g, ''), regionCode); type InputProps = Omit, 'value' | 'onInput'> & { inputRef?: React.Ref; @@ -26,47 +21,18 @@ type InputProps = Omit, 'value' | 'o onInput?: (event: React.FormEvent) => void; prefix?: string; e164?: boolean; - format?: NumberFormatter; }; const isValidPrefix = (prefix: string): boolean => !!prefix.match(/^\+\d+$/); -const PhoneInput = ({ - inputRef, - value, - defaultValue, - onChange: onChangeFromProps, - prefix, - e164, - format: formatFromProps, - ...other -}: InputProps) => { +const PhoneInput = ({inputRef, value, defaultValue, onChange, prefix, e164, ...other}: InputProps) => { const [selfValue, setSelfValue] = React.useState(defaultValue ?? ''); const ref = React.useRef(null); const {i18n} = useTheme(); - const formatRef = React.useRef(formatFromProps || formatPhoneDummy); - /** this state is used to force a re-render when libphonenumber is loaded */ - const [isLibphonenumberLoaded, setIsLibphonenumberloaded] = React.useState(false); + const regionCode = i18n.phoneNumberFormattingRegionCode; const isControlledByParent = typeof value !== 'undefined'; const controlledValue = (isControlledByParent ? value : selfValue) as string; - const onChangeRef = React.useRef(onChangeFromProps); - - React.useEffect(() => { - onChangeRef.current = onChangeFromProps; - }, [onChangeFromProps]); - - React.useEffect(() => { - if (formatFromProps) { - formatRef.current = formatFromProps; - } else { - import('@telefonica/libphonenumber' /* webpackChunkName: "libphonenumber" */).then((lib) => { - libphonenumber = lib; - formatRef.current = formatPhoneUsingLibphonenumber; - setIsLibphonenumberloaded(true); - }); - } - }, [formatFromProps]); const handleChangeValue = React.useCallback( (newFormattedValue: string) => { @@ -74,10 +40,10 @@ const PhoneInput = ({ setSelfValue(newFormattedValue); } if (ref.current) { - onChangeRef.current?.(createChangeEvent(ref.current, newFormattedValue)); + onChange?.(createChangeEvent(ref.current, newFormattedValue)); } }, - [isControlledByParent] + [isControlledByParent, onChange] ); const format = React.useCallback( @@ -91,15 +57,15 @@ const PhoneInput = ({ // then remove the prefix from the result if (prefix && isValidPrefix(prefix)) { const prefixedValue = prefix + value; - result = formatRef.current(prefixedValue, regionCode); + result = formatPhone(regionCode, prefixedValue); if (result.startsWith(prefix)) { result = result.slice(prefix.length).trim(); } else { // fallback to regular formatting - result = formatRef.current(value, regionCode); + result = formatPhone(regionCode, value); } } else { - result = formatRef.current(value, regionCode); + result = formatPhone(regionCode, value); } return result.replace(/-/g, '@'); }, @@ -109,18 +75,11 @@ const PhoneInput = ({ const rifm = useRifm({ format, value: controlledValue, - // Instead of calling `handleChangeValue` here, we call it in `useEffect` below. - // When the formatter changes (libphonenumber is lazy loaded), rifm should call `onChange` - // with the new formatted value but it doesn't, so we need to call it manually. - onChange: () => {}, + onChange: handleChangeValue, accept: /[\d\-+#*]+/g, replace: (s) => s.replace(/@/g, '-'), }); - React.useEffect(() => { - handleChangeValue(rifm.value); - }, [rifm.value, handleChangeValue]); - return ( ); }; @@ -138,7 +96,6 @@ export interface PhoneNumberFieldProps extends CommonFormFieldProps { prefix?: string; getSuggestions?: (value: string) => Array; e164?: boolean; - format?: NumberFormatter; } const PhoneNumberField = ({ @@ -154,34 +111,21 @@ const PhoneNumberField = ({ onBlur, value, defaultValue, - dataAttributes, - /** - * By default this component will use google's libphonenumber library to format numbers. - * The component will load libphonenumber on demand, so it won't impact the initial load time. - * You can opt-out of using libphonenumber by providing a custom formatter. - */ - format, - /** enabling e164 is incompatible with custom formatters because this requires libphonenumber */ e164, + dataAttributes, ...rest }: PhoneNumberFieldProps): JSX.Element => { const {i18n} = useTheme(); - if (process.env.NODE_ENV !== 'production') { - if (e164 && format) { - console.error('[PhoneNumberField] enabling e164 is incompatible with custom formatters'); - } - } - const processValue = (value: string) => { - if (e164 && libphonenumber && !format) { + if (e164) { try { const numericPrefix = (rest.prefix ?? '').replace(/[^\d]/g, ''); - let regionCode = libphonenumber.getRegionCodeForCountryCode(numericPrefix); + let regionCode = getRegionCodeForCountryCode(numericPrefix); if (!regionCode || regionCode === 'ZZ') { regionCode = i18n.phoneNumberFormattingRegionCode; } - return libphonenumber.formatToE164(libphonenumber.parse(value, regionCode)); + return formatToE164(parse(value, regionCode)); } catch (e) { return ''; } @@ -212,12 +156,9 @@ const PhoneNumberField = ({ {...rest} {...fieldProps} type="phone" - inputProps={{prefix: rest.prefix, format}} + inputProps={{prefix: rest.prefix}} inputComponent={PhoneInput} - dataAttributes={{ - 'component-name': 'PhoneNumberField', - ...dataAttributes, - }} + dataAttributes={{'component-name': 'PhoneNumberField', ...dataAttributes}} /> ); }; diff --git a/src/test-utils/ssr.tsx b/src/test-utils/ssr.tsx index 4528919850..2ec3dac30c 100644 --- a/src/test-utils/ssr.tsx +++ b/src/test-utils/ssr.tsx @@ -144,11 +144,6 @@ export const createServer = (): http.Server => { return; } - if (moduleName.includes('telefonica_libphonenumber')) { - serveFileInPath(path.join(__dirname, '..', '..', 'public', 'ssr', `${moduleName}`)); - return; - } - let Component; try { // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/src/text-field-base.tsx b/src/text-field-base.tsx index f5858688b4..6b07dc623a 100644 --- a/src/text-field-base.tsx +++ b/src/text-field-base.tsx @@ -166,7 +166,7 @@ interface TextFieldBaseProps { onBlur?: React.FocusEventHandler; onFocus?: React.FocusEventHandler; onKeyDown?: (event: React.KeyboardEvent) => void; - inputProps?: {[name: string]: unknown}; + inputProps?: {[name: string]: string | number | undefined}; inputComponent?: React.ComponentType; shrinkLabel?: boolean; focus?: boolean;