diff --git a/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-2145678901-in-vivo-skin-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-2145678901-in-vivo-skin-1-snap.png
new file mode 100644
index 000000000..af28fe5c4
Binary files /dev/null and b/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-2145678901-in-vivo-skin-1-snap.png differ
diff --git a/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-34654834455-in-vivo-skin-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-34654834455-in-vivo-skin-1-snap.png
new file mode 100644
index 000000000..d15bb0ab7
Binary files /dev/null and b/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-34654834455-in-vivo-skin-1-snap.png differ
diff --git a/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-654834455-in-movistar-skin-1-snap.png b/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-654834455-in-movistar-skin-1-snap.png
new file mode 100644
index 000000000..7fa9daf52
Binary files /dev/null and b/src/__screenshot_tests__/__image_snapshots__/input-fields-screenshot-test-tsx-phone-number-field-lite-654834455-in-movistar-skin-1-snap.png differ
diff --git a/src/__screenshot_tests__/input-fields-screenshot-test.tsx b/src/__screenshot_tests__/input-fields-screenshot-test.tsx
index 051e86988..da4bcdcb6 100644
--- a/src/__screenshot_tests__/input-fields-screenshot-test.tsx
+++ b/src/__screenshot_tests__/input-fields-screenshot-test.tsx
@@ -521,6 +521,28 @@ test.each`
expect(await fieldWrapper.screenshot()).toMatchImageSnapshot();
});
+test.each`
+ skin | number
+ ${'Vivo'} | ${'2145678901'}
+ ${'Vivo'} | ${'+34654834455'}
+ ${'Movistar'} | ${'654834455'}
+`('PhoneNumberFieldLite - $number in $skin skin', async ({skin, number}) => {
+ await openStoryPage({
+ id: 'components-input-fields-phonenumberfieldlite--uncontrolled',
+ device: 'MOBILE_IOS',
+ skin,
+ args: {defaultValue: number},
+ });
+
+ const fieldWrapper = await screen.findByTestId('phone-number-field-lite');
+ const field = await screen.findByLabelText('Label');
+
+ await field.click({clickCount: 3});
+ await field.type(number);
+
+ expect(await fieldWrapper.screenshot()).toMatchImageSnapshot();
+});
+
test('CreditCardExpirationField', async () => {
await openStoryPage({
id: 'components-input-fields-creditcardexpirationfield--uncontrolled',
diff --git a/src/__stories__/phone-number-field-lite-story.tsx b/src/__stories__/phone-number-field-lite-story.tsx
new file mode 100644
index 000000000..75717f828
--- /dev/null
+++ b/src/__stories__/phone-number-field-lite-story.tsx
@@ -0,0 +1,170 @@
+import * as React from 'react';
+import {Box, Text1, Stack, ResponsiveLayout, PhoneNumberFieldLite, Boxed} from '..';
+import {inspect} from 'util';
+import {phoneNumbersList} from './helpers';
+
+export default {
+ title: 'Components/Input fields/PhoneNumberFieldLite',
+ parameters: {fullScreen: true},
+};
+
+const getPhoneNumberSuggestions = (value: string) =>
+ phoneNumbersList
+ .filter((s) => String(s).toLocaleLowerCase().startsWith(value.toLocaleLowerCase()))
+ .slice(0, 5);
+
+interface PhoneNumberFieldBaseArgs {
+ label: string;
+ placeholder: string;
+ prefix: string;
+ helperText: string;
+ error: boolean;
+ inverse: boolean;
+ optional: boolean;
+ disabled: boolean;
+ readOnly: boolean;
+ preventCopy: boolean;
+}
+
+const defaultBaseArgs: PhoneNumberFieldBaseArgs = {
+ label: 'Label',
+ placeholder: '',
+ prefix: '',
+ helperText: '',
+ error: false,
+ inverse: false,
+ optional: false,
+ disabled: false,
+ readOnly: false,
+ preventCopy: false,
+};
+
+interface PhoneNumberFieldControlledArgs extends PhoneNumberFieldBaseArgs {
+ initialValue: string;
+ suggestions: boolean;
+}
+
+const Description = () => {
+ return (
+
+
+
+
+ This is a "light" version of the PhoneNumberField component. It does not use google's
+ libphonenumber library to reduce bundle size.
+
+
+ - Only supported countries are formatted
+ - Not all phone number types are formatted
+ - Numbers in E164 are returned unformatted
+
+ - A custom formatter can be provided via props and the formatter used by this
+ component is exported as `formatPhoneLite`
+
+
+
+
+
+ );
+};
+
+export const Controlled: StoryComponent = ({
+ inverse,
+ initialValue,
+ suggestions,
+ ...rest
+}) => {
+ const [rawValue, setRawValue] = React.useState(initialValue);
+ const [value, setValue] = React.useState(undefined);
+
+ return (
+
+
+
+
+ {
+ setValue(value);
+ setRawValue(rawValue);
+ }}
+ name="phoneNumber"
+ autoComplete="off"
+ dataAttributes={{testid: 'phone-number-field-lite'}}
+ getSuggestions={suggestions ? getPhoneNumberSuggestions : undefined}
+ {...rest}
+ />
+
+
+ value: {typeof value === 'undefined' ? '' : `(${typeof value}) ${inspect(value)}`}
+
+
+ rawValue:{' '}
+ {typeof rawValue === 'undefined'
+ ? ''
+ : `(${typeof rawValue}) ${inspect(rawValue)}`}
+
+
+
+
+
+ );
+};
+
+Controlled.storyName = 'controlled';
+Controlled.args = {
+ initialValue: '654834455',
+ ...defaultBaseArgs,
+ suggestions: false,
+};
+
+interface PhoneNumberFieldUncontrolledArgs extends PhoneNumberFieldBaseArgs {
+ defaultValue: string;
+}
+
+export const Uncontrolled: StoryComponent = ({
+ inverse,
+ defaultValue,
+ ...rest
+}) => {
+ const [rawValue, setRawValue] = React.useState(undefined);
+ const [value, setValue] = React.useState(undefined);
+
+ return (
+
+
+
+
+ {
+ setValue(value);
+ setRawValue(rawValue);
+ }}
+ name="phoneNumber"
+ autoComplete="off"
+ dataAttributes={{testid: 'phone-number-field-lite'}}
+ {...rest}
+ />
+
+
+ value: {typeof value === 'undefined' ? '' : `(${typeof value}) ${inspect(value)}`}
+
+
+ rawValue:{' '}
+ {typeof rawValue === 'undefined'
+ ? ''
+ : `(${typeof rawValue}) ${inspect(rawValue)}`}
+
+
+
+
+
+ );
+};
+
+Uncontrolled.storyName = 'uncontrolled';
+Uncontrolled.args = {
+ defaultValue: '654834455',
+ ...defaultBaseArgs,
+};
diff --git a/src/__stories__/phone-number-field-story.tsx b/src/__stories__/phone-number-field-story.tsx
index d58a6ef2e..01570e403 100644
--- a/src/__stories__/phone-number-field-story.tsx
+++ b/src/__stories__/phone-number-field-story.tsx
@@ -56,7 +56,7 @@ export const Controlled: StoryComponent = ({
const [value, setValue] = React.useState(undefined);
return (
-
+
= ({
const [value, setValue] = React.useState(undefined);
return (
-
+
{
useDisableBodyScroll(true);
diff --git a/src/__tests__/phone-number-field-lite-test.tsx b/src/__tests__/phone-number-field-lite-test.tsx
new file mode 100644
index 000000000..d565fc2fd
--- /dev/null
+++ b/src/__tests__/phone-number-field-lite-test.tsx
@@ -0,0 +1,103 @@
+import {render, screen} from '@testing-library/react';
+import PhoneNumberFieldLite from '../phone-number-field-lite';
+import {getMovistarSkin} from '../skins/movistar';
+import ThemeContextProvider from '../theme-context-provider';
+import userEvent from '@testing-library/user-event';
+import * as React from 'react';
+import PhoneNumberField from '../phone-number-field';
+
+test.each`
+ regionCode | number | expected | expectedRaw | expectedE164 | description
+ ${'ZZ'} | ${'123456789012345'} | ${'123456789012345'} | ${'123456789012345'} | ${'123456789012345'} | ${'Unknown region'}
+ ${'ES'} | ${'654834455'} | ${'654834455'} | ${'654 83 44 55'} | ${'+34654834455'} | ${'ES mobile'}
+ ${'ES'} | ${'914-44/10 25'} | ${'914441025'} | ${'914 44 10 25'} | ${'+34914441025'} | ${'ES landline'}
+ ${'ES'} | ${'6548344556'} | ${'6548344556'} | ${'6548344556'} | ${'+346548344556'} | ${'ES mobile too long'}
+ ${'ES'} | ${'914-44/10 256'} | ${'9144410256'} | ${'9144410256'} | ${'+349144410256'} | ${'ES landline too long'}
+ ${'ES'} | ${'+34 654 834 455'} | ${'+34654834455'} | ${'+34 654 83 44 55'} | ${'+34654834455'} | ${'ES E164 mobile'}
+ ${'ES'} | ${'+34 914-44/10 25'} | ${'+34914441025'} | ${'+34 914 44 10 25'} | ${'+34914441025'} | ${'ES E164 landline'}
+ ${'BR'} | ${'21987654321'} | ${'21987654321'} | ${'(21) 98765-4321'} | ${'+5521987654321'} | ${'BR mobile'}
+ ${'BR'} | ${'219876543210'} | ${'219876543210'} | ${'219876543210'} | ${'+55219876543210'} | ${'BR mobile too long'}
+ ${'BR'} | ${'2123456789'} | ${'2123456789'} | ${'(21) 2345-6789'} | ${'+552123456789'} | ${'BR landline'}
+ ${'BR'} | ${'21234567890'} | ${'21234567890'} | ${'(21) 23456-7890'} | ${'+5521234567890'} | ${'BR landline long'}
+ ${'BR'} | ${'212345678901'} | ${'212345678901'} | ${'212345678901'} | ${'+55212345678901'} | ${'BR landline too long'}
+ ${'BR'} | ${'+5521987654321'} | ${'+5521987654321'} | ${'+55 21 98765-4321'} | ${'+5521987654321'} | ${'BR E164 mobile'}
+ ${'BR'} | ${'+34654834455'} | ${'+34654834455'} | ${'+34 654 83 44 55'} | ${'+34654834455'} | ${'BR with ES E164'}
+ ${'DE'} | ${'015789012345'} | ${'015789012345'} | ${'01578 9012345'} | ${'+4915789012345'} | ${'DE mobile 15'}
+ ${'DE'} | ${'01601234567'} | ${'01601234567'} | ${'0160 1234567'} | ${'+491601234567'} | ${'DE mobile 16'}
+ ${'DE'} | ${'01701234567'} | ${'01701234567'} | ${'0170 1234567'} | ${'+491701234567'} | ${'DE mobile 17'}
+ ${'DE'} | ${'12345678901'} | ${'12345678901'} | ${'12345678901'} | ${'+4912345678901'} | ${'DE unknown'}
+ ${'DE'} | ${'+4915789012345'} | ${'+4915789012345'} | ${'+49 1578 9012345'} | ${'+4915789012345'} | ${'DE E164 mobile'}
+ ${'DE'} | ${'+49015789012345'} | ${'+49015789012345'} | ${'+49 015789012345'} | ${'+49015789012345'} | ${'DE E164 mobile wrong zero'}
+ ${'GB'} | ${'07123456789'} | ${'07123456789'} | ${'07123 456789'} | ${'+447123456789'} | ${'GB mobile'}
+ ${'GB'} | ${'071234567890'} | ${'071234567890'} | ${'071234567890'} | ${'+4471234567890'} | ${'GB mobile too long'}
+ ${'GB'} | ${'+447123456789'} | ${'+447123456789'} | ${'+44 7123 456789'} | ${'+447123456789'} | ${'GB E164 mobile'}
+ ${'GB'} | ${'+4407123456789'} | ${'+4407123456789'} | ${'+44 07123456789'} | ${'+4407123456789'} | ${'GB E164 mobile wrong zero'}
+`(
+ 'PhoneNumberFieldLite - $description - $number',
+ async ({regionCode, number, expected, expectedRaw, expectedE164}) => {
+ const onChangeValue = jest.fn();
+ const onChangeValueE164 = jest.fn();
+ const onChangeValueUsingLibphonenumber = jest.fn();
+
+ render(
+
+
+
+
+
+ );
+
+ const input = screen.getByLabelText('Phone');
+ const inputE164 = screen.getByLabelText('Phone E164');
+ const referenceInput = screen.getByLabelText('Reference');
+
+ await userEvent.type(input, number);
+ await userEvent.type(inputE164, number);
+ await userEvent.type(referenceInput, number);
+
+ expect(onChangeValue).toHaveBeenLastCalledWith(expected, expectedRaw);
+ expect(onChangeValueE164).toHaveBeenLastCalledWith(expectedE164, expectedRaw);
+
+ // We expect the same result as the libphonenumber version, except for the E164 format
+ if (!number.startsWith('+')) {
+ // This checks all the calls to onChangeValue (as you type)
+ expect(onChangeValue.mock.calls).toEqual(onChangeValueUsingLibphonenumber.mock.calls);
+ }
+ }
+);
+
+test('PhoneNumberFieldLite custom formatter', async () => {
+ const onChangeValue = jest.fn();
+
+ render(
+
+ {
+ return number.replace(/\D/g, '').split('').join('-');
+ }}
+ />
+
+ );
+
+ const input = screen.getByLabelText('Phone');
+ await userEvent.type(input, '654834455');
+
+ expect(onChangeValue).toHaveBeenLastCalledWith('654834455', '6-5-4-8-3-4-4-5-5');
+});
diff --git a/src/hooks.tsx b/src/hooks.tsx
index a89af8d3f..9b8293f4f 100644
--- a/src/hooks.tsx
+++ b/src/hooks.tsx
@@ -53,7 +53,6 @@ export const useDisableBodyScroll = (disable: boolean): void => {
`top: ${-bodyScrollTop}px;`,
'left: 0px;',
'right: 0px;',
- 'bottom: 0px;',
'overscroll-behavior-y: contain;', // disable overscroll
].join('');
}
diff --git a/src/index.tsx b/src/index.tsx
index 6cc2c9579..e8d1dc47e 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -136,6 +136,7 @@ export {TextFieldBase} from './text-field-base';
export {default as SearchField} from './search-field';
export {default as EmailField} from './email-field';
export {default as PhoneNumberField} from './phone-number-field';
+export {default as PhoneNumberFieldLite, formatPhoneLite} from './phone-number-field-lite';
export {default as CreditCardNumberField} from './credit-card-number-field';
export {default as CreditCardExpirationField} from './credit-card-expiration-field';
export {default as CreditCardFields} from './credit-card-fields';
diff --git a/src/phone-number-field-lite.tsx b/src/phone-number-field-lite.tsx
new file mode 100644
index 000000000..946954ada
--- /dev/null
+++ b/src/phone-number-field-lite.tsx
@@ -0,0 +1,251 @@
+'use client';
+import * as React from 'react';
+import {useRifm} from 'rifm';
+import {useFieldProps} from './form-context';
+import {TextFieldBaseAutosuggest} from './text-field-base';
+import {useTheme} from './hooks';
+import {createChangeEvent} from './utils/dom';
+import {combineRefs} from './utils/common';
+
+import type {CommonFormFieldProps} from './text-field-base';
+import type {RegionCode} from './utils/region-code';
+
+const COUNTRY_CODE_TO_REGION_CODE: Record = {
+ '+34': 'ES',
+ '+55': 'BR',
+ '+49': 'DE',
+ '+44': 'GB',
+};
+
+const REGION_CODE_TO_COUNTRY_CODE: Record = Object.fromEntries(
+ Object.entries(COUNTRY_CODE_TO_REGION_CODE).map(([k, v]) => [v, k])
+);
+
+const clean = (number: string): string => {
+ return number.trim().replace(/[^\d\+]/g, ''); // keep digits and "+"
+};
+
+const asE164 = (number: string, regionCode: RegionCode): string => {
+ if (number.startsWith('+')) {
+ return number;
+ }
+
+ switch (regionCode) {
+ case 'ES':
+ return `${REGION_CODE_TO_COUNTRY_CODE[regionCode]} ${number}`;
+ case 'BR':
+ return `${REGION_CODE_TO_COUNTRY_CODE[regionCode]} ${number.replace(/[\(\)]/g, '')}`;
+ case 'DE':
+ return `${REGION_CODE_TO_COUNTRY_CODE[regionCode]} ${number.replace(/^0/, '')}`;
+ case 'GB':
+ return `${REGION_CODE_TO_COUNTRY_CODE[regionCode]} ${number.replace(/^0/, '')}`;
+ default:
+ return number;
+ }
+};
+
+/**
+ * Simple phone formatter for a few countries and a subset of phone numbers
+ *
+ * Formatting conditions have been adapted to exactly match libphonenumber's as you type formatting
+ * Not all formatting rules are implemented, only the most common ones. For a more complete solution, use PhoneNumberField
+ */
+export const formatPhoneLite = (regionCode: RegionCode, number: string): string => {
+ const cleanNumber = clean(number);
+ const isE164 = cleanNumber.startsWith('+');
+ let digits = cleanNumber.replace(/\D/g, ''); // keep digits only
+ let countryCode = '';
+ let formattingRegionCode = regionCode;
+
+ if (isE164) {
+ // check if the number matches a known country code
+ countryCode =
+ Object.keys(COUNTRY_CODE_TO_REGION_CODE).find((code) => cleanNumber.startsWith(code)) || '';
+
+ if (countryCode) {
+ digits = cleanNumber.slice(countryCode.length); // remove country code
+ formattingRegionCode = COUNTRY_CODE_TO_REGION_CODE[countryCode]; // override region code, the country code has precedence
+ } else {
+ // unknown E164 is returned without formatting
+ return '+' + digits;
+ }
+ }
+
+ if (formattingRegionCode === 'ES') {
+ // https://en.wikipedia.org/wiki/Telephone_numbers_in_Spain
+ // Example mobile: 654 83 44 55
+ // Example landline: 914 44 10 25
+ if (digits.length <= 9) {
+ return `${countryCode} ${digits.slice(0, 3)} ${digits.slice(3, 5)} ${digits.slice(5, 7)} ${digits.slice(7)}`.trim();
+ }
+ } else if (formattingRegionCode === 'BR') {
+ // https://en.wikipedia.org/wiki/Telephone_numbers_in_Brazil
+ // Example mobile: (xx) (6..9)xxxx-xxxx
+ // Example landline: (xx) xxxx-xxxx
+ let national: string | undefined;
+ if (digits.length === 11) {
+ national = `(${digits.slice(0, 2)}) ${digits.slice(2, 7)}-${digits.slice(7)}`.replace(/\D+$/, '');
+ } else if (digits.length > 2 && digits.length <= 11 && digits[2] <= '5') {
+ national = `(${digits.slice(0, 2)}) ${digits.slice(2, 6)}-${digits.slice(6)}`.replace(/\D+$/, '');
+ }
+ if (national) {
+ return isE164 ? asE164(national, formattingRegionCode) : national;
+ }
+ } else if (formattingRegionCode === 'DE') {
+ // https://en.wikipedia.org/wiki/Telephone_numbers_in_Germany
+ // Only formatting mobile numbers, landline numbers have a lot of variations:
+ // https://en.wikipedia.org/wiki/Telephone_numbers_in_Germany#/media/File:Karte_Telefonvorwahlen_Deutschland.png
+ // Example mobile: 0157 89012345
+ // Example E164: +49 1578 9012345
+ const zeroPadded = isE164 ? '0' + digits : digits;
+ if (zeroPadded.length >= 4 && zeroPadded.match(/^(015|016|017)/)) {
+ let national: string | undefined;
+ if (zeroPadded.length <= 12 && zeroPadded.startsWith('015')) {
+ national = `${zeroPadded.slice(0, 5)} ${zeroPadded.slice(5)}`.trim();
+ } else {
+ national = `${countryCode} ${zeroPadded.slice(0, 4)} ${zeroPadded.slice(4)}`.trim();
+ }
+ return isE164 ? asE164(national, formattingRegionCode) : national;
+ }
+ } else if (formattingRegionCode === 'GB') {
+ // https://en.wikipedia.org/wiki/Telephone_numbers_in_the_United_Kingdom#Mobile_telephones
+ // Like in DE, only mobile numbers are formatted
+ // Example mobile: 07xxx xxxxxx
+ const zeroPadded = isE164 ? '0' + digits : digits;
+ if (zeroPadded.length <= 11 && zeroPadded.startsWith('07')) {
+ const national = `${zeroPadded.slice(0, 5)} ${zeroPadded.slice(5)}`.trim();
+ return isE164 ? asE164(national, formattingRegionCode) : national;
+ }
+ }
+ return isE164 ? `${countryCode} ${digits}` : digits;
+};
+
+type InputProps = Omit, 'value' | 'onInput'> & {
+ inputRef?: React.Ref;
+ value?: string;
+ defaultValue?: string;
+ onInput?: (event: React.FormEvent) => void;
+ prefix?: string;
+ format?: (number: string) => string;
+};
+
+const PhoneInput = ({
+ inputRef,
+ value,
+ defaultValue,
+ onChange,
+ prefix,
+ format: formatFromProps,
+ ...other
+}: InputProps) => {
+ const [selfValue, setSelfValue] = React.useState(defaultValue || '');
+ const ref = React.useRef(null);
+ const {
+ i18n: {phoneNumberFormattingRegionCode: regionCode},
+ } = useTheme();
+
+ const isControlledByParent = typeof value !== 'undefined';
+ const controlledValue = (isControlledByParent ? value : selfValue) as string;
+
+ const handleChangeValue = React.useCallback(
+ (newFormattedValue: string) => {
+ if (!isControlledByParent) {
+ setSelfValue(newFormattedValue);
+ }
+ if (ref.current) {
+ onChange?.(createChangeEvent(ref.current, newFormattedValue));
+ }
+ },
+ [isControlledByParent, onChange]
+ );
+
+ const format = React.useCallback(
+ (number: string): string => {
+ if (formatFromProps) {
+ return formatFromProps(number);
+ }
+ return formatPhoneLite(regionCode, number);
+ },
+ [formatFromProps, regionCode]
+ );
+
+ const rifm = useRifm({
+ format,
+ value: controlledValue,
+ accept: /[\d\+]+/g,
+ onChange: handleChangeValue,
+ });
+
+ return (
+
+ );
+};
+
+export interface PhoneNumberFieldProps extends CommonFormFieldProps {
+ onChangeValue?: (value: string, rawValue: string) => void;
+ prefix?: string;
+ getSuggestions?: (value: string) => Array;
+ format?: (number: string) => string;
+ e164?: boolean;
+}
+
+const PhoneNumberFieldLite = ({
+ disabled,
+ error,
+ helperText,
+ name,
+ label,
+ optional,
+ validate,
+ onChange,
+ onChangeValue,
+ onBlur,
+ value,
+ defaultValue,
+ dataAttributes,
+ e164,
+ ...rest
+}: PhoneNumberFieldProps): JSX.Element => {
+ const {
+ i18n: {phoneNumberFormattingRegionCode},
+ } = useTheme();
+
+ const processValue = (value: string) => {
+ return e164 ? clean(asE164(value, phoneNumberFormattingRegionCode)) : clean(value);
+ };
+
+ const fieldProps = useFieldProps({
+ name,
+ label,
+ value,
+ defaultValue,
+ processValue,
+ helperText,
+ optional,
+ error,
+ disabled,
+ onBlur,
+ validate,
+ onChange,
+ onChangeValue,
+ });
+
+ return (
+
+ );
+};
+
+export default PhoneNumberFieldLite;
diff --git a/src/phone-number-field.tsx b/src/phone-number-field.tsx
index 032a5b965..4082c5821 100644
--- a/src/phone-number-field.tsx
+++ b/src/phone-number-field.tsx
@@ -20,12 +20,11 @@ type InputProps = Omit, 'value' | 'o
defaultValue?: string;
onInput?: (event: React.FormEvent) => void;
prefix?: string;
- e164?: boolean;
};
const isValidPrefix = (prefix: string): boolean => !!prefix.match(/^\+\d+$/);
-const PhoneInput = ({inputRef, value, defaultValue, onChange, prefix, e164, ...other}: InputProps) => {
+const PhoneInput = ({inputRef, value, defaultValue, onChange, prefix, ...other}: InputProps) => {
const [selfValue, setSelfValue] = React.useState(defaultValue ?? '');
const ref = React.useRef(null);
const {i18n} = useTheme();
diff --git a/src/text-field-base.tsx b/src/text-field-base.tsx
index 201de0e7c..7facace60 100644
--- a/src/text-field-base.tsx
+++ b/src/text-field-base.tsx
@@ -221,7 +221,8 @@ export const TextFieldBase = React.forwardRef(
preventCopy = preventCopy ?? preventCopyInFormFields;
const reactId = React.useId();
const id = idProp || reactId;
- const helperTextid = React.useId();
+ const leftHelperTextid = React.useId();
+ const rightHelperTextid = React.useId();
const [inputState, setInputState] = React.useState(
defaultValue?.length || value?.length ? 'filled' : 'default'
@@ -317,7 +318,8 @@ export const TextFieldBase = React.forwardRef(
(
defaultValue,
value,
...(error && {'aria-invalid': true}),
- ...(helperText && {'aria-describedby': helperTextid}),
+ ...((helperText || (multiline && maxLength)) && {
+ 'aria-describedby': `${leftHelperTextid} ${rightHelperTextid}`,
+ }),
...(preventCopy && {
onCopy: preventCopyHandler,
onCut: preventCopyHandler,
diff --git a/src/text-field-components.tsx b/src/text-field-components.tsx
index 4b9146c2c..bdba1353d 100644
--- a/src/text-field-components.tsx
+++ b/src/text-field-components.tsx
@@ -81,16 +81,18 @@ type HelperTextProps = {
rightText?: string;
rightTextLabel?: string;
error?: boolean;
- id?: string;
+ leftTextId?: string;
+ rightTextId?: string;
children?: void;
};
export const HelperText = ({
leftText,
+ leftTextId,
rightText,
+ rightTextId,
rightTextLabel,
error,
- id,
}: HelperTextProps): JSX.Element => {
const isInverse = useIsInverseOrMediaVariant();
const leftColor = isInverse
@@ -112,7 +114,7 @@ export const HelperText = ({
)}
-
+
{leftText}
@@ -122,20 +124,20 @@ export const HelperText = ({
className={classnames(styles.helperText, {[styles.rightHelperText]: !!leftText})}
data-testid="endHelperText"
>
- {rightTextLabel && (
-
- {rightTextLabel}
-
- )}
- {rightText}
+ {rightTextLabel && (
+
+ {rightTextLabel}
+
+ )}
+ {rightText}
)}