From 29fe08295daa3a0aee2383df7c00df2cdcffde4e Mon Sep 17 00:00:00 2001 From: Simon Nilsson Date: Fri, 6 Sep 2024 14:18:00 +0200 Subject: [PATCH] feat: Allow passing custom component to flags prop #203 --- packages/docs/docs/02-Usage/01-PhoneInput.md | 4 +- .../CountrySelector/CountrySelector.tsx | 45 ++++++++++++------ .../CountrySelectorDropdown.tsx | 46 +++++++++++++------ src/components/PhoneInput/PhoneInput.test.tsx | 19 +++++++- src/stories/PhoneInput/PhoneInput.stories.tsx | 2 + .../stories/CustomFlagComponent.story.tsx | 43 +++++++++++++++++ 6 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 src/stories/PhoneInput/stories/CustomFlagComponent.story.tsx diff --git a/packages/docs/docs/02-Usage/01-PhoneInput.md b/packages/docs/docs/02-Usage/01-PhoneInput.md index d5b2120..f45f68c 100644 --- a/packages/docs/docs/02-Usage/01-PhoneInput.md +++ b/packages/docs/docs/02-Usage/01-PhoneInput.md @@ -177,8 +177,8 @@ defaultValue="false" ### `flags` diff --git a/src/components/CountrySelector/CountrySelector.tsx b/src/components/CountrySelector/CountrySelector.tsx index dfc661c..26c3eae 100644 --- a/src/components/CountrySelector/CountrySelector.tsx +++ b/src/components/CountrySelector/CountrySelector.tsx @@ -117,21 +117,36 @@ export const CountrySelector: React.FC = ({ })} style={styleProps.buttonContentWrapperStyle} > - f.iso2 === selectedCountry)?.src} - className={buildClassNames({ - addPrefix: [ - 'country-selector-button__flag-emoji', - disabled && 'country-selector-button__flag-emoji--disabled', - ], - rawClassNames: [styleProps.flagClassName], - })} - style={{ - visibility: selectedCountry ? 'visible' : 'hidden', - ...styleProps.flagStyle, - }} - /> + {typeof flags === 'function' ? ( + React.createElement(flags, { + iso2: selectedCountry, + disabled, + style: styleProps.flagStyle, + className: buildClassNames({ + addPrefix: [ + 'country-selector-button__flag-emoji', + disabled && 'country-selector-button__flag-emoji--disabled', + ], + rawClassNames: [styleProps.flagClassName], + }), + }) + ) : ( + f.iso2 === selectedCountry)?.src} + className={buildClassNames({ + addPrefix: [ + 'country-selector-button__flag-emoji', + disabled && 'country-selector-button__flag-emoji--disabled', + ], + rawClassNames: [styleProps.flagClassName], + })} + style={{ + visibility: selectedCountry ? 'visible' : 'hidden', + ...styleProps.flagStyle, + }} + /> + )} {!hideDropdown && (
; onSelect?: (country: ParsedCountry) => void; onClose?: () => void; } @@ -272,7 +279,6 @@ export const CountrySelectorDropdown: React.FC< const isFocused = index === focusedItemIndex; const isPreferred = preferredCountries.includes(country.iso2); const isLastPreferred = index === preferredCountries.length - 1; - const flag = flags?.find((f) => f.iso2 === country.iso2); return ( @@ -297,17 +303,31 @@ export const CountrySelectorDropdown: React.FC< style={styleProps.listItemStyle} title={country.name} > - + {typeof flags === 'function' ? ( + React.createElement(flags, { + iso2: country.iso2, + disabled: false, + style: styleProps.listItemFlagStyle, + className: buildClassNames({ + addPrefix: [ + 'country-selector-dropdown__list-item-flag-emoji', + ], + rawClassNames: [styleProps.listItemFlagClassName], + }), + }) + ) : ( + f.iso2 === country.iso2)?.src} + className={buildClassNames({ + addPrefix: [ + 'country-selector-dropdown__list-item-flag-emoji', + ], + rawClassNames: [styleProps.listItemFlagClassName], + })} + style={styleProps.listItemFlagStyle} + /> + )} { }); }); + describe('custom flag component', () => { + test('should render custom flag component from flags prop', () => { + const flagComponent = () => custom-flag; + render(); + + const element = screen.getByText((content, element) => { + return ( + element?.tagName.toLowerCase() === 'span' && + element?.parentElement?.tagName.toLowerCase() === 'div' && + element?.parentElement?.parentElement?.tagName.toLowerCase() === 'button' + ); + }) as HTMLLIElement; + + expect(element).toHaveTextContent('custom-flag'); + }); + }); + describe('disableFormatting', () => { test('should remove mask chars from input if disableFormatting is set to true', async () => { const user = userEvent.setup({ delay: null }); diff --git a/src/stories/PhoneInput/PhoneInput.stories.tsx b/src/stories/PhoneInput/PhoneInput.stories.tsx index f31c1d3..99799c9 100644 --- a/src/stories/PhoneInput/PhoneInput.stories.tsx +++ b/src/stories/PhoneInput/PhoneInput.stories.tsx @@ -26,6 +26,7 @@ import { WithAutofocus } from './stories/WithAutofocus.story'; import { DisableFormatting } from './stories/DisableFormatting.story'; import { ControlledMode } from './stories/ControlledMode.story'; import { CustomFlags } from './stories/CustomFlags.story'; +import { CustomFlagComponent } from './stories/CustomFlagComponent.story'; export const _Default = Default; export const _WithInitialValue = WithInitialValue; @@ -42,3 +43,4 @@ export const _WithAutofocus = WithAutofocus; export const _DisableFormatting = DisableFormatting; export const _ControlledMode = ControlledMode; export const _CustomFlags = CustomFlags; +export const _CustomFlagComponent = CustomFlagComponent; diff --git a/src/stories/PhoneInput/stories/CustomFlagComponent.story.tsx b/src/stories/PhoneInput/stories/CustomFlagComponent.story.tsx new file mode 100644 index 0000000..450cbfe --- /dev/null +++ b/src/stories/PhoneInput/stories/CustomFlagComponent.story.tsx @@ -0,0 +1,43 @@ +import React from 'react'; + +import { + CountryIso2, + defaultCountries, + parseCountry, + PhoneInput, +} from '../../../index'; +import { PhoneInputStory } from '../PhoneInput.stories'; + +const CustomFlag = ({ + iso2, + style, + className, +}: { + iso2?: CountryIso2; + style?: React.CSSProperties; + className?: string; +}) => { + return ( + + ); +}; + +const countries = defaultCountries.filter((c) => { + const country = parseCountry(c); + return ['fr', 'jp', 'pl', 'ua'].includes(country.iso2); +}); + +export const CustomFlagComponent: PhoneInputStory = { + name: 'Custom Flag Component', + render: (args) => , + args: { + flags: CustomFlag, + countries, + defaultCountry: 'jp', + placeholder: 'Phone number', + }, +};