diff --git a/apps/demo/src/Views/Ui/components/FormWidgets/CountryPicker.tsx b/apps/demo/src/Views/Ui/components/FormWidgets/CountryPicker.tsx index 92110f2a4..3686b0947 100644 --- a/apps/demo/src/Views/Ui/components/FormWidgets/CountryPicker.tsx +++ b/apps/demo/src/Views/Ui/components/FormWidgets/CountryPicker.tsx @@ -561,7 +561,7 @@ const myRegions = { favorites={["US", "FR"]} groups={{ "North America": ["US", "CA"], - "Europe": ["FR", "DE", "IT", "ES"], + Europe: ["FR", "DE", "IT", "ES"], }} label={t("countryPicker.labels.single")} locale={locale} diff --git a/packages/ui/src/FormWidgets/CountryPicker/index.tsx b/packages/ui/src/FormWidgets/CountryPicker/index.tsx index 266a4085c..29165dca6 100644 --- a/packages/ui/src/FormWidgets/CountryPicker/index.tsx +++ b/packages/ui/src/FormWidgets/CountryPicker/index.tsx @@ -4,7 +4,7 @@ import { getFallbackTranslation } from "../../utils/CountryPicker"; import { Select, ISelectProperties } from "../Select"; import defaultGroups from "./groups.json"; -import type { Option, GroupedOption } from "../Select"; +import type { Option, GroupedOption as OptionGroup } from "../Select"; export type Translation = Record; export type Locales = Record; @@ -78,22 +78,13 @@ const getBaseOptions = ( baseOptions.push({ value: code as unknown as T, - label: getLabel(code, locale, locales, fallbackData[code]), + label: getLabel(code, locale, locales, fallbackLabel), }); }); return baseOptions; }; -const getFavoriteOptions = ( - baseOptions: Option[], - favorites: string[], -) => { - return favorites - .map((code) => baseOptions.find((option) => String(option.value) === code)) - .filter(Boolean) as Option[]; -}; - const getFlagClass = ( code: string | undefined, position: string, @@ -110,15 +101,12 @@ const getFlagClass = ( .filter(Boolean) .join(" "); -const getGroups = ( - groups: Groups, - list: Option[], -): GroupedOption[] => { +const getGroups = (groups: Groups, list: Option[]): OptionGroup[] => { const optionMap = new Map( list.map((option) => [String(option.value), option]), ); - return Object.entries(groups).reduce[]>( + return Object.entries(groups).reduce[]>( (groupedOptions, [groupLabel, groupCodes]) => { const options = groupCodes .map((code) => optionMap.get(code)) @@ -144,30 +132,62 @@ const getLabel = ( }; const getOptions = ({ - baseOptions, + autoSortOptions = true, + exclude, + fallbackLocale = "en", favorites, groups, + include, includeFavorites = true, labels, + locale = "en", + locales, }: Pick< CountryPickerProperties, - "favorites" | "groups" | "includeFavorites" | "labels" -> & { baseOptions: Option[] }) => { + | "autoSortOptions" + | "exclude" + | "fallbackLocale" + | "favorites" + | "groups" + | "include" + | "includeFavorites" + | "labels" + | "locale" + | "locales" +>) => { + const baseOptions = getBaseOptions( + exclude, + fallbackLocale, + include, + locale, + locales, + ); + if (favorites && favorites.length > 0) { - return getOptionsWithFavorites( + const optionsWithFavorites = getOptionsWithFavorites( baseOptions, favorites, includeFavorites, groups, labels, ); + + const hasFavoriteOptions = optionsWithFavorites[0].options.length > 0; + + return getSortedOptions( + autoSortOptions, + optionsWithFavorites, + hasFavoriteOptions, + ); } if (groups && Object.keys(groups).length > 0) { - return getGroups(groups, baseOptions); + const optionGroups = getGroups(groups, baseOptions); + + return getSortedOptions(autoSortOptions, optionGroups); } - return baseOptions; + return getSortedOptions(autoSortOptions, baseOptions); }; const getOptionsWithFavorites = ( @@ -177,7 +197,9 @@ const getOptionsWithFavorites = ( groups?: Groups, labels?: CountryPickerLabels, ) => { - const favoriteOptions = getFavoriteOptions(baseOptions, favorites); + const favoriteOptions = favorites + .map((code) => baseOptions.find((option) => String(option.value) === code)) + .filter(Boolean) as Option[]; const mainOptions = includeFavorites ? baseOptions @@ -203,15 +225,19 @@ const getOptionsWithFavorites = ( }; const getSortedOptions = ( - isOptionsGrouped: boolean, - hasFavoriteOptions: boolean, - options: Option[] | GroupedOption[], + autoSortOptions: boolean, + options: Option[] | OptionGroup[], + hasFavoriteOptions?: boolean, ) => { - if (!isOptionsGrouped) { + if (!autoSortOptions) { + return options as Option[] | OptionGroup[]; + } + + if (!hasOptionGroup(options)) { return [...(options as Option[])].sort(sortByLabel); } - const sortedOptionsGroup = (options as GroupedOption[]).map((group) => { + const sortedOptionsGroup = (options as OptionGroup[]).map((group) => { return { ...group, options: [...(group.options as Option[])].sort(sortByLabel), @@ -228,6 +254,19 @@ const getSortedOptions = ( return sortedOptionsGroup.sort(sortByLabel); }; +const hasOptionGroup = ( + options: Option[] | OptionGroup[], +): options is OptionGroup[] => { + return ( + Array.isArray(options) && + options.length > 0 && + options.every( + (option): option is OptionGroup => + typeof option === "object" && "options" in option, + ) + ); +}; + const renderCountryOption = ( option: Option, { @@ -257,8 +296,8 @@ const renderCountryOption = ( }; const sortByLabel = ( - optionA: Option | GroupedOption, - optionB: Option | GroupedOption, + optionA: Option | OptionGroup, + optionB: Option | OptionGroup, ) => { if (!optionA.label) { return 1; @@ -288,47 +327,31 @@ export const CountryPicker = ({ locale = "en", ...properties }: CountryPickerProperties) => { - const baseOptions = useMemo(() => { - return getBaseOptions( - exclude, - fallbackLocale, - include, - locale, - locales, - ) as Option[]; - }, [exclude, fallbackLocale, include, locale, locales]); - const options = useMemo(() => { return getOptions({ - baseOptions, + autoSortOptions, + exclude, + fallbackLocale, favorites, groups, + include, includeFavorites, labels, + locale, + locales, }); - }, [baseOptions, favorites, groups, includeFavorites, labels]); - - const favoriteOptions = useMemo(() => { - return favorites?.length ? getFavoriteOptions(baseOptions, favorites) : []; - }, [baseOptions, favorites]); - - const isOptionsGrouped = useMemo(() => { - return ( - Array.isArray(options) && options.length > 0 && "options" in options[0] - ); - }, [options]); - - const sortedOptions = useMemo[] | GroupedOption[]>(() => { - if (!autoSortOptions) { - return options as Option[] | GroupedOption[]; - } - - return getSortedOptions( - isOptionsGrouped, - !!favoriteOptions.length, - options, - ); - }, [autoSortOptions, favoriteOptions, isOptionsGrouped, options]); + }, [ + autoSortOptions, + exclude, + fallbackLocale, + favorites, + groups, + include, + includeFavorites, + labels, + locale, + locales, + ]); const handleOnChange = useCallback( (value: T | T[]) => { @@ -359,7 +382,7 @@ export const CountryPicker = ({