From 708c52e2ab922350cffe84c7bd60019d136a63da Mon Sep 17 00:00:00 2001 From: Claudio Roma <36543510+roma-claudio@users.noreply.github.com> Date: Tue, 15 Oct 2024 17:50:32 +0200 Subject: [PATCH] Allow multiple countries with same country code (#2586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Connor Bär Co-authored-by: Connor Bär --- .changeset/two-horses-fold.md | 5 ++++ .../PhoneNumberInput.spec.tsx | 13 ++++----- .../PhoneNumberInput.stories.tsx | 18 +++++++------ .../PhoneNumberInput/PhoneNumberInput.tsx | 27 +++++++++++++------ .../PhoneNumberInputService.spec.ts | 24 ++++++++++------- .../PhoneNumberInputService.ts | 2 +- 6 files changed, 57 insertions(+), 32 deletions(-) create mode 100644 .changeset/two-horses-fold.md diff --git a/.changeset/two-horses-fold.md b/.changeset/two-horses-fold.md new file mode 100644 index 0000000000..2c4afdb1ff --- /dev/null +++ b/.changeset/two-horses-fold.md @@ -0,0 +1,5 @@ +--- +'@sumup/circuit-ui': minor +--- + +Use the country code instead of the phone prefix as the value of the PhoneNumberInput component's country code select. This is an implementation detail, but might require changes to unit tests that query the country code select. diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx index 4fa77700bf..c1f5eb381e 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.spec.tsx @@ -25,8 +25,9 @@ import { } from './PhoneNumberInput.js'; const countryCodeMap: { [key: string]: string } = { - '+1': 'US', - '+49': 'DE', + CA: '+1', + US: '+1', + DE: '+49', }; const defaultProps = { @@ -35,8 +36,8 @@ const defaultProps = { label: 'Country code', defaultValue: '+1', options: Object.keys(countryCodeMap).map((key) => ({ - country: countryCodeMap[key], - code: key, + country: key, + code: countryCodeMap[key], })), }, subscriberNumber: { @@ -85,7 +86,7 @@ describe('PhoneNumberInput', () => { }; render(); const select = screen.getByRole('combobox'); - await userEvent.selectOptions(select, '+49'); + await userEvent.selectOptions(select, 'DE'); expect(onChange).toHaveBeenCalledOnce(); }); @@ -100,7 +101,7 @@ describe('PhoneNumberInput', () => { }; render(); const select = screen.getByRole('combobox'); - await userEvent.selectOptions(select, '+49'); + await userEvent.selectOptions(select, 'DE'); expect(onChange).toHaveBeenCalledOnce(); }); diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx index 966dcc8088..9dd5381c68 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx @@ -13,7 +13,7 @@ * limitations under the License. */ -import { FlagDe, FlagUs, type IconComponentType } from '@sumup/icons'; +import { FlagCa, FlagDe, FlagUs, type IconComponentType } from '@sumup/icons'; import { useState } from 'react'; import { action } from '@storybook/addon-actions'; @@ -33,13 +33,15 @@ export default { }; const flagIconMap: { [key: string]: IconComponentType<'16'> } = { - DE: FlagDe, + CA: FlagCa, US: FlagUs, + DE: FlagDe, }; const countryCodeMap: { [key: string]: string } = { - '+1': 'US', - '+49': 'DE', + CA: '+1', + US: '+1', + DE: '+49', }; export const Base = (args: PhoneNumberInputProps) => ( @@ -52,8 +54,8 @@ Base.args = { label: 'Country code', defaultValue: '+1', options: Object.keys(countryCodeMap).map((key) => ({ - country: countryCodeMap[key], - code: key, + country: key, + code: countryCodeMap[key], })), }, subscriberNumber: { @@ -125,14 +127,14 @@ Disabled.args = { }; export const WithPrefix = (args: PhoneNumberInputProps) => { - const [selectedCountry, setSelectedCountry] = useState('US'); + const [selectedCountry, setSelectedCountry] = useState('CA'); return ( { - setSelectedCountry(countryCodeMap[event.target.value]); + setSelectedCountry(event.target.value); }, renderPrefix: (props) => { const Icon = flagIconMap[selectedCountry]; diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx index 3aa8bcc1c6..caef5886ac 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx @@ -240,8 +240,20 @@ export const PhoneNumberInput = forwardRef< ) { return; } + + const selectedCountry = countryCodeRef?.current?.value; + if (!selectedCountry) { + return; + } + const code = countryCode.options.find( + ({ country }) => country === selectedCountry, + )?.code; + + if (!code) { + return; + } const phoneNumber = normalizePhoneNumber( - countryCodeRef.current.value, + code, subscriberNumberRef.current.value, ); onChange(phoneNumber); @@ -273,21 +285,20 @@ export const PhoneNumberInput = forwardRef< return; } - const pastedCountryCode = countryCode.options - .map((option) => option.code) + const pastedOption = countryCode.options // Match longer, more specific country codes first - .sort((a, b) => b.length - a.length) - .find((code) => pastedText.startsWith(code)); + .sort((a, b) => b.code.length - a.code.length) + .find(({ code }) => pastedText.startsWith(code)); - if (!pastedCountryCode) { + if (!pastedOption) { return; } event.preventDefault(); - const pastedSubscriberNumber = pastedText.split(pastedCountryCode)[1]; + const pastedSubscriberNumber = pastedText.split(pastedOption.code)[1]; - countryCodeRef.current.value = pastedCountryCode; + countryCodeRef.current.value = pastedOption.country; // React overwrites the input.value setter. In order to be able to trigger // a 'change' event on the input, we need to use the native setter. diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts index acecc17410..58dc62a91e 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts @@ -52,24 +52,28 @@ describe('PhoneNumberInputService', () => { }); describe('mapCountryCodeOptions', () => { - it('should use the country code as the option value', () => { + it('should use the country as the option value', () => { const options = [ + { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, { country: 'DE', code: '+49' }, ]; const actual = mapCountryCodeOptions(options); - expect(actual[0].value).toBe('+49'); - expect(actual[1].value).toBe('+1'); + expect(actual[0].value).toBe('CA'); + expect(actual[1].value).toBe('DE'); + expect(actual[2].value).toBe('US'); }); it('should use the country name and code as the option label', () => { const options = [ + { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, { country: 'DE', code: '+49' }, ]; const actual = mapCountryCodeOptions(options); - expect(actual[0].label).toBe('Germany (+49)'); - expect(actual[1].label).toBe('United States (+1)'); + expect(actual[0].label).toBe('Canada (+1)'); + expect(actual[1].label).toBe('Germany (+49)'); + expect(actual[2].label).toBe('United States (+1)'); }); it('should omit the country name when it is not available', () => { @@ -80,22 +84,24 @@ describe('PhoneNumberInputService', () => { it('should sort the options alphabetically', () => { const options = [ + { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, { country: 'DE', code: '+49' }, ]; const actual = mapCountryCodeOptions(options); - expect(actual[0].label).toBe('Germany (+49)'); - expect(actual[1].label).toBe('United States (+1)'); + expect(actual[0].label).toBe('Canada (+1)'); + expect(actual[1].label).toBe('Germany (+49)'); + expect(actual[2].label).toBe('United States (+1)'); }); it('should use the locale as the default country code', () => { const options = [ + { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, { country: 'DE', code: '+49' }, - { country: 'CA', code: '+1' }, ]; const actual = mapCountryCodeOptions(options, 'DE'); - expect(actual[0].value).toBe('+49'); + expect(actual[0].value).toBe('DE'); }); }); }); diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts index e78b011571..8a8f0355f5 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts @@ -49,7 +49,7 @@ export function mapCountryCodeOptions( const countryName = displayName.of(country); return { label: countryName ? `${countryName} (${code})` : code, - value: code, + value: country, }; }) .sort((a, b) => a.label.localeCompare(b.label));