From ad3e1073e290dc6323e05e08f76c06282db8e6e9 Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Tue, 7 Jul 2026 13:22:26 +0200 Subject: [PATCH 1/6] feat(PhoneNumberInput): add shouldDisplayCountryNames prop Allow opting out of Intl.DisplayNames localization so the country selector can show calling codes from countryCode.options directly. Co-authored-by: Cursor --- .../PhoneNumberInput/PhoneNumberInput.tsx | 18 ++++++++++++++++-- .../PhoneNumberInputService.ts | 10 ++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx index c0b0217caa..d2788a27da 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx @@ -117,6 +117,14 @@ export interface PhoneNumberInputProps * preselected country code. Defaults to `navigator.languages`. */ locale?: string | string[]; + /** + * When `true`, localizes country names in the country code selector using + * `Intl.DisplayNames`. When `false`, displays the calling codes from + * `countryCode.options` without localization. + * + * @default true + */ + shouldDisplayCountryNames?: boolean; /** * Choose from 2 sizes. * @default m @@ -232,6 +240,7 @@ export const PhoneNumberInput = forwardRef< readOnly, 'aria-describedby': descriptionId, locale, + shouldDisplayCountryNames = true, size = 'm', className, style, @@ -257,8 +266,13 @@ export const PhoneNumberInput = forwardRef< ); const options = useMemo( - () => mapCountryCodeOptions(countryCode.options, locale), - [countryCode.options, locale], + () => + mapCountryCodeOptions( + countryCode.options, + locale, + shouldDisplayCountryNames, + ), + [countryCode.options, locale, shouldDisplayCountryNames], ); const handleChange = () => { diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts index 030170c404..3cc48bf950 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts @@ -132,7 +132,17 @@ export function normalizePhoneNumber( export function mapCountryCodeOptions( countryCodeOptions: CountryCodeOption[], locale: Locale | undefined, + shouldDisplayCountryNames = true, ): Required['options'] { + if (!shouldDisplayCountryNames) { + return countryCodeOptions + .map(({ code, country }) => ({ + label: code, + value: country, + })) + .sort((a, b) => a.label.localeCompare(b.label)); + } + const getCountryName = (country: string) => { // eslint-disable-next-line compat/compat const isIntlDisplayNamesSupported = typeof Intl.DisplayNames === 'function'; From cdccb3525f2318c18f30c9b19fd0f60fa3e02c52 Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Tue, 7 Jul 2026 13:22:47 +0200 Subject: [PATCH 2/6] test(PhoneNumberInput): cover shouldDisplayCountryNames option mapping Co-authored-by: Cursor --- .../PhoneNumberInputService.spec.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts index 8ffa95ae0c..8144eedc9d 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts @@ -182,5 +182,30 @@ describe('PhoneNumberInputService', () => { const actual = mapCountryCodeOptions(options, locale); expect(actual[0].value).toBe('DE'); }); + + it('should use calling codes as labels when shouldDisplayCountryNames is false', () => { + const options = [ + { country: 'CA', code: '+1' }, + { country: 'US', code: '+1' }, + { country: 'DE', code: '+49' }, + ]; + const actual = mapCountryCodeOptions(options, undefined, false); + expect(actual[0].label).toBe('+1'); + expect(actual[0].value).toBe('CA'); + expect(actual[1].label).toBe('+1'); + expect(actual[1].value).toBe('US'); + expect(actual[2].label).toBe('+49'); + expect(actual[2].value).toBe('DE'); + }); + + it('should sort calling code labels when shouldDisplayCountryNames is false', () => { + const options = [ + { country: 'DE', code: '+49' }, + { country: 'CA', code: '+1' }, + { country: 'US', code: '+1' }, + ]; + const actual = mapCountryCodeOptions(options, undefined, false); + expect(actual.map(({ label }) => label)).toEqual(['+1', '+1', '+49']); + }); }); }); From acd50a819b329bfe88835c427c42aa86b17ac8ce Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Tue, 7 Jul 2026 13:22:48 +0200 Subject: [PATCH 3/6] docs(PhoneNumberInput): add WithoutCountryNames story Co-authored-by: Cursor --- .../PhoneNumberInput/PhoneNumberInput.stories.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx index 1967029c13..5f177f1a8b 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx @@ -168,6 +168,15 @@ export const WithPrefix = (args: PhoneNumberInputProps) => { WithPrefix.args = Base.args; +export const WithoutCountryNames = (args: PhoneNumberInputProps) => ( + +); + +WithoutCountryNames.args = { + ...Base.args, + shouldDisplayCountryNames: false, +}; + export const Sizes = (args: PhoneNumberInputProps) => ( From f424d91f58257c240e7ec198f8a6c077b8ea6d4d Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Tue, 7 Jul 2026 13:22:50 +0200 Subject: [PATCH 4/6] chore(changesets): add changeset for shouldDisplayCountryNames Co-authored-by: Cursor --- .../phone-number-input-should-display-country-names.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/phone-number-input-should-display-country-names.md diff --git a/.changeset/phone-number-input-should-display-country-names.md b/.changeset/phone-number-input-should-display-country-names.md new file mode 100644 index 0000000000..8a9f141412 --- /dev/null +++ b/.changeset/phone-number-input-should-display-country-names.md @@ -0,0 +1,5 @@ +--- +'@sumup-oss/circuit-ui': minor +--- + +Added `shouldDisplayCountryNames` prop to `PhoneNumberInput` to opt out of `Intl.DisplayNames` localization and display calling codes from `countryCode.options` directly. From cb9626661c930d78b6f92566a578e68a15b3950d Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Tue, 7 Jul 2026 13:58:32 +0200 Subject: [PATCH 5/6] fix(PhoneNumberInput): default shouldDisplayCountryNames to false Skip Intl.DisplayNames localization by default so existing integrations are not affected by server/client label mismatches. Co-authored-by: Cursor --- ...mber-input-should-display-country-names.md | 2 +- .../PhoneNumberInput.stories.tsx | 1 + .../PhoneNumberInput/PhoneNumberInput.tsx | 4 ++-- .../PhoneNumberInputService.spec.ts | 20 +++++++++---------- .../PhoneNumberInputService.ts | 2 +- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.changeset/phone-number-input-should-display-country-names.md b/.changeset/phone-number-input-should-display-country-names.md index 8a9f141412..72d8265670 100644 --- a/.changeset/phone-number-input-should-display-country-names.md +++ b/.changeset/phone-number-input-should-display-country-names.md @@ -2,4 +2,4 @@ '@sumup-oss/circuit-ui': minor --- -Added `shouldDisplayCountryNames` prop to `PhoneNumberInput` to opt out of `Intl.DisplayNames` localization and display calling codes from `countryCode.options` directly. +Added `shouldDisplayCountryNames` prop to `PhoneNumberInput`. Defaults to `false`, displaying calling codes from `countryCode.options` without `Intl.DisplayNames` localization. Set to `true` to opt in to localized country names in the selector labels. diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx index 5f177f1a8b..4634534012 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx @@ -67,6 +67,7 @@ export const Base = (args: PhoneNumberInputProps) => { Base.args = { value: '', label: 'Phone number', + shouldDisplayCountryNames: true, countryCode: { label: 'Country code', defaultValue: 'CA', diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx index d2788a27da..b8684e2295 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx @@ -122,7 +122,7 @@ export interface PhoneNumberInputProps * `Intl.DisplayNames`. When `false`, displays the calling codes from * `countryCode.options` without localization. * - * @default true + * @default false */ shouldDisplayCountryNames?: boolean; /** @@ -240,7 +240,7 @@ export const PhoneNumberInput = forwardRef< readOnly, 'aria-describedby': descriptionId, locale, - shouldDisplayCountryNames = true, + shouldDisplayCountryNames = false, size = 'm', className, style, diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts index 8144eedc9d..80d84bedea 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts @@ -135,8 +135,8 @@ describe('PhoneNumberInputService', () => { const locale = undefined; const actual = mapCountryCodeOptions(options, locale); expect(actual[0].value).toBe('CA'); - expect(actual[1].value).toBe('DE'); - expect(actual[2].value).toBe('US'); + expect(actual[1].value).toBe('US'); + expect(actual[2].value).toBe('DE'); }); it('should use the country name and code as the option label', () => { @@ -146,7 +146,7 @@ describe('PhoneNumberInputService', () => { { country: 'DE', code: '+49' }, ]; const locale = undefined; - const actual = mapCountryCodeOptions(options, locale); + const actual = mapCountryCodeOptions(options, locale, true); expect(actual[0].label).toBe('Canada (+1)'); expect(actual[1].label).toBe('Germany (+49)'); expect(actual[2].label).toBe('United States (+1)'); @@ -155,7 +155,7 @@ describe('PhoneNumberInputService', () => { it('should omit the country name when it is not available', () => { const options = [{ country: '', code: '+49' }]; const locale = undefined; - const actual = mapCountryCodeOptions(options, locale); + const actual = mapCountryCodeOptions(options, locale, true); expect(actual[0].label).toBe('+49'); }); @@ -166,7 +166,7 @@ describe('PhoneNumberInputService', () => { { country: 'DE', code: '+49' }, ]; const locale = undefined; - const actual = mapCountryCodeOptions(options, locale); + const actual = mapCountryCodeOptions(options, locale, true); expect(actual[0].label).toBe('Canada (+1)'); expect(actual[1].label).toBe('Germany (+49)'); expect(actual[2].label).toBe('United States (+1)'); @@ -179,17 +179,17 @@ describe('PhoneNumberInputService', () => { { country: 'DE', code: '+49' }, ]; const locale = 'DE'; - const actual = mapCountryCodeOptions(options, locale); + const actual = mapCountryCodeOptions(options, locale, true); expect(actual[0].value).toBe('DE'); }); - it('should use calling codes as labels when shouldDisplayCountryNames is false', () => { + it('should use calling codes as labels by default', () => { const options = [ { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, { country: 'DE', code: '+49' }, ]; - const actual = mapCountryCodeOptions(options, undefined, false); + const actual = mapCountryCodeOptions(options, undefined); expect(actual[0].label).toBe('+1'); expect(actual[0].value).toBe('CA'); expect(actual[1].label).toBe('+1'); @@ -198,13 +198,13 @@ describe('PhoneNumberInputService', () => { expect(actual[2].value).toBe('DE'); }); - it('should sort calling code labels when shouldDisplayCountryNames is false', () => { + it('should sort calling code labels by default', () => { const options = [ { country: 'DE', code: '+49' }, { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, ]; - const actual = mapCountryCodeOptions(options, undefined, false); + const actual = mapCountryCodeOptions(options, undefined); expect(actual.map(({ label }) => label)).toEqual(['+1', '+1', '+49']); }); }); diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts index 3cc48bf950..822d7a5860 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts @@ -132,7 +132,7 @@ export function normalizePhoneNumber( export function mapCountryCodeOptions( countryCodeOptions: CountryCodeOption[], locale: Locale | undefined, - shouldDisplayCountryNames = true, + shouldDisplayCountryNames = false, ): Required['options'] { if (!shouldDisplayCountryNames) { return countryCodeOptions From 3637a202951e9cd276687c20ec0822550a55cb5a Mon Sep 17 00:00:00 2001 From: Pedro Medeiros Date: Tue, 7 Jul 2026 13:58:57 +0200 Subject: [PATCH 6/6] fix(PhoneNumberInput): restore shouldDisplayCountryNames default to true Keep existing localized country name behavior as the default. Co-authored-by: Cursor --- ...mber-input-should-display-country-names.md | 2 +- .../PhoneNumberInput.stories.tsx | 1 - .../PhoneNumberInput/PhoneNumberInput.tsx | 4 ++-- .../PhoneNumberInputService.spec.ts | 20 +++++++++---------- .../PhoneNumberInputService.ts | 2 +- 5 files changed, 14 insertions(+), 15 deletions(-) diff --git a/.changeset/phone-number-input-should-display-country-names.md b/.changeset/phone-number-input-should-display-country-names.md index 72d8265670..8a9f141412 100644 --- a/.changeset/phone-number-input-should-display-country-names.md +++ b/.changeset/phone-number-input-should-display-country-names.md @@ -2,4 +2,4 @@ '@sumup-oss/circuit-ui': minor --- -Added `shouldDisplayCountryNames` prop to `PhoneNumberInput`. Defaults to `false`, displaying calling codes from `countryCode.options` without `Intl.DisplayNames` localization. Set to `true` to opt in to localized country names in the selector labels. +Added `shouldDisplayCountryNames` prop to `PhoneNumberInput` to opt out of `Intl.DisplayNames` localization and display calling codes from `countryCode.options` directly. diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx index 4634534012..5f177f1a8b 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.stories.tsx @@ -67,7 +67,6 @@ export const Base = (args: PhoneNumberInputProps) => { Base.args = { value: '', label: 'Phone number', - shouldDisplayCountryNames: true, countryCode: { label: 'Country code', defaultValue: 'CA', diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx index b8684e2295..d2788a27da 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInput.tsx @@ -122,7 +122,7 @@ export interface PhoneNumberInputProps * `Intl.DisplayNames`. When `false`, displays the calling codes from * `countryCode.options` without localization. * - * @default false + * @default true */ shouldDisplayCountryNames?: boolean; /** @@ -240,7 +240,7 @@ export const PhoneNumberInput = forwardRef< readOnly, 'aria-describedby': descriptionId, locale, - shouldDisplayCountryNames = false, + shouldDisplayCountryNames = true, size = 'm', className, style, diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts index 80d84bedea..8144eedc9d 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.spec.ts @@ -135,8 +135,8 @@ describe('PhoneNumberInputService', () => { const locale = undefined; const actual = mapCountryCodeOptions(options, locale); expect(actual[0].value).toBe('CA'); - expect(actual[1].value).toBe('US'); - expect(actual[2].value).toBe('DE'); + expect(actual[1].value).toBe('DE'); + expect(actual[2].value).toBe('US'); }); it('should use the country name and code as the option label', () => { @@ -146,7 +146,7 @@ describe('PhoneNumberInputService', () => { { country: 'DE', code: '+49' }, ]; const locale = undefined; - const actual = mapCountryCodeOptions(options, locale, true); + const actual = mapCountryCodeOptions(options, locale); expect(actual[0].label).toBe('Canada (+1)'); expect(actual[1].label).toBe('Germany (+49)'); expect(actual[2].label).toBe('United States (+1)'); @@ -155,7 +155,7 @@ describe('PhoneNumberInputService', () => { it('should omit the country name when it is not available', () => { const options = [{ country: '', code: '+49' }]; const locale = undefined; - const actual = mapCountryCodeOptions(options, locale, true); + const actual = mapCountryCodeOptions(options, locale); expect(actual[0].label).toBe('+49'); }); @@ -166,7 +166,7 @@ describe('PhoneNumberInputService', () => { { country: 'DE', code: '+49' }, ]; const locale = undefined; - const actual = mapCountryCodeOptions(options, locale, true); + const actual = mapCountryCodeOptions(options, locale); expect(actual[0].label).toBe('Canada (+1)'); expect(actual[1].label).toBe('Germany (+49)'); expect(actual[2].label).toBe('United States (+1)'); @@ -179,17 +179,17 @@ describe('PhoneNumberInputService', () => { { country: 'DE', code: '+49' }, ]; const locale = 'DE'; - const actual = mapCountryCodeOptions(options, locale, true); + const actual = mapCountryCodeOptions(options, locale); expect(actual[0].value).toBe('DE'); }); - it('should use calling codes as labels by default', () => { + it('should use calling codes as labels when shouldDisplayCountryNames is false', () => { const options = [ { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, { country: 'DE', code: '+49' }, ]; - const actual = mapCountryCodeOptions(options, undefined); + const actual = mapCountryCodeOptions(options, undefined, false); expect(actual[0].label).toBe('+1'); expect(actual[0].value).toBe('CA'); expect(actual[1].label).toBe('+1'); @@ -198,13 +198,13 @@ describe('PhoneNumberInputService', () => { expect(actual[2].value).toBe('DE'); }); - it('should sort calling code labels by default', () => { + it('should sort calling code labels when shouldDisplayCountryNames is false', () => { const options = [ { country: 'DE', code: '+49' }, { country: 'CA', code: '+1' }, { country: 'US', code: '+1' }, ]; - const actual = mapCountryCodeOptions(options, undefined); + const actual = mapCountryCodeOptions(options, undefined, false); expect(actual.map(({ label }) => label)).toEqual(['+1', '+1', '+49']); }); }); diff --git a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts index 822d7a5860..3cc48bf950 100644 --- a/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts +++ b/packages/circuit-ui/components/PhoneNumberInput/PhoneNumberInputService.ts @@ -132,7 +132,7 @@ export function normalizePhoneNumber( export function mapCountryCodeOptions( countryCodeOptions: CountryCodeOption[], locale: Locale | undefined, - shouldDisplayCountryNames = false, + shouldDisplayCountryNames = true, ): Required['options'] { if (!shouldDisplayCountryNames) { return countryCodeOptions