Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple countries with same country code #2586

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading