Skip to content

Commit

Permalink
Allow multiple countries with same country code (#2586)
Browse files Browse the repository at this point in the history
Co-authored-by: Connor Bär <[email protected]>
Co-authored-by: Connor Bär <[email protected]>
  • Loading branch information
3 people authored Oct 15, 2024
1 parent 7fce1de commit 708c52e
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 32 deletions.
5 changes: 5 additions & 0 deletions .changeset/two-horses-fold.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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: {
Expand Down Expand Up @@ -85,7 +86,7 @@ describe('PhoneNumberInput', () => {
};
render(<PhoneNumberInput {...props} />);
const select = screen.getByRole('combobox');
await userEvent.selectOptions(select, '+49');
await userEvent.selectOptions(select, 'DE');
expect(onChange).toHaveBeenCalledOnce();
});

Expand All @@ -100,7 +101,7 @@ describe('PhoneNumberInput', () => {
};
render(<PhoneNumberInput {...props} />);
const select = screen.getByRole('combobox');
await userEvent.selectOptions(select, '+49');
await userEvent.selectOptions(select, 'DE');
expect(onChange).toHaveBeenCalledOnce();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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) => (
Expand All @@ -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: {
Expand Down Expand Up @@ -125,14 +127,14 @@ Disabled.args = {
};

export const WithPrefix = (args: PhoneNumberInputProps) => {
const [selectedCountry, setSelectedCountry] = useState('US');
const [selectedCountry, setSelectedCountry] = useState('CA');
return (
<PhoneNumberInput
{...args}
countryCode={{
...args.countryCode,
onChange: (event) => {
setSelectedCountry(countryCodeMap[event.target.value]);
setSelectedCountry(event.target.value);
},
renderPrefix: (props) => {
const Icon = flagIconMap[selectedCountry];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand All @@ -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');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down

0 comments on commit 708c52e

Please sign in to comment.