diff --git a/src/components/PhoneInput/PhoneInput.spec.tsx b/src/components/PhoneInput/PhoneInput.spec.tsx
index 5410c9795..279d02daa 100644
--- a/src/components/PhoneInput/PhoneInput.spec.tsx
+++ b/src/components/PhoneInput/PhoneInput.spec.tsx
@@ -42,6 +42,39 @@ describe('PhoneInput', () => {
expect(document.activeElement).toEqual(screen.getByLabelText('Phone Number'));
});
+ it('should only render countries from allowedCountries when provided', async () => {
+ render();
+
+ fireEvent.keyDown(screen.getByText(defaultCountry.dialCode), { keyCode: 40 });
+
+ expect(await screen.findByText('Andorra +376')).toBeInTheDocument();
+ expect(screen.queryByText(/Afghanistan/)).not.toBeInTheDocument();
+ });
+
+ it('should not render the selected country value when it is not part of allowedCountries', () => {
+ render();
+
+ expect(screen.queryByText(defaultCountry.dialCode)).not.toBeInTheDocument();
+ });
+
+ it('should not render the selected country value when it is not a valid country', () => {
+ const invalidCountry = { value: 'POTATO', label: 'Potato', dialCode: '+0' };
+ render();
+
+ expect(screen.queryByText('Potato')).not.toBeInTheDocument();
+ expect(screen.queryByText('+0')).not.toBeInTheDocument();
+ });
+
+ it('should warn when the selected country is not among the available options', () => {
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
+
+ render();
+
+ expect(consoleErrorSpy).toHaveBeenCalledWith(expect.stringContaining('is not among the available options'));
+
+ consoleErrorSpy.mockRestore();
+ });
+
it('should call the change handler when typing in the national number input', () => {
const mockCountryChangeHandler = jest.fn();
const mockTextChangeHandler = jest.fn();
diff --git a/src/components/PhoneInput/PhoneInput.tsx b/src/components/PhoneInput/PhoneInput.tsx
index 59a01fabe..f8a47d913 100644
--- a/src/components/PhoneInput/PhoneInput.tsx
+++ b/src/components/PhoneInput/PhoneInput.tsx
@@ -1,5 +1,6 @@
import React, { useRef } from 'react';
import styled from 'styled-components';
+import warning from 'warning';
import {
compose,
layout,
@@ -64,6 +65,10 @@ interface PhoneInputProps
* Pass props directly to the internal SelectList component used to show prefixes. Any value from the `SelectList` component props are allowed, but props from the `PhoneInput` take precedence
*/
selectListProps?: SelectListProps;
+ /**
+ * Restricts the country list to only these ISO codes (e.g. ['DE', 'FR']). When omitted, all countries are shown.
+ */
+ allowedCountries?: ReadonlyArray;
}
const Box = styled.div`
@@ -83,6 +88,20 @@ const PhoneInput: React.FC = ({
const containerRef = useRef();
const spaceBetweenInputs = variant === 'boxed' ? '0.25rem' : '0.75rem';
+ const countries = props.allowedCountries
+ ? COUNTRIES.filter(it => props.allowedCountries.includes(it.value))
+ : COUNTRIES;
+
+ // Avoid rendering a invalid option/country that isn't amongst allowedCountries
+ const selectedCountry = countries.find(it => it.value === props.country?.value) && props.country;
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ warning(
+ !props.country || Boolean(selectedCountry),
+ `[@freenow/wave] PhoneInput received a \`country\` ("${props.country?.value}") that is not among the ` +
+ 'available options and will not be displayed. Ensure it is included in `allowedCountries`.'
+ );
+
const handleCountrySelection = value => {
if (props.onCountryChange) {
props.onCountryChange(value);
@@ -97,9 +116,9 @@ const PhoneInput: React.FC = ({
{...selectListProps}
id={`${props.id}-area-code`}
name={`${props.name}-area-code`}
- value={props.country}
+ value={selectedCountry}
onChange={handleCountrySelection}
- options={COUNTRIES.map(it => ({ ...it, label: `${it.label} ${it.dialCode}` }))}
+ options={countries.map(it => ({ ...it, label: `${it.label} ${it.dialCode}` }))}
placeholder=""
width="8rem"
components={{
diff --git a/src/components/PhoneInput/docs/PhoneInput.stories.tsx b/src/components/PhoneInput/docs/PhoneInput.stories.tsx
index 7947553ee..84a2fcb85 100644
--- a/src/components/PhoneInput/docs/PhoneInput.stories.tsx
+++ b/src/components/PhoneInput/docs/PhoneInput.stories.tsx
@@ -69,6 +69,13 @@ export const Preselected: Story = {
}
};
+export const AllowedCountries: Story = {
+ ...Default,
+ args: {
+ allowedCountries: ['DE', 'ES', 'FR', 'GB', 'AD']
+ }
+};
+
export const BottomLined: Story = {
...Default,
args: {
diff --git a/src/components/PhoneInput/docs/PhoneInput.storybook.mdx b/src/components/PhoneInput/docs/PhoneInput.storybook.mdx
index 26963a2b5..84dc0bcf4 100644
--- a/src/components/PhoneInput/docs/PhoneInput.storybook.mdx
+++ b/src/components/PhoneInput/docs/PhoneInput.storybook.mdx
@@ -8,7 +8,7 @@ import * as PhoneInputStories from './PhoneInput.stories';
The `PhoneInput` is a form component for inputting a phone number in international format with usage of country selector.
-The component consists of two controls: a select to pick a prefix and an input to type in the phone number. The select shows all the country codes available world-wide.
+The component consists of two controls: a select to pick a prefix and an input to type in the phone number. The select shows all the country codes available world-wide by default, or a restricted subset when the `allowedCountries` prop is provided.