From df77bd6422ba1dc0dac4a113ebcbc123cb48806f Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Thu, 22 Jan 2026 12:21:08 +0545 Subject: [PATCH 1/9] refactor(ui/country-picker): move getLabel to utils --- packages/ui/src/FormWidgets/CountryPicker/index.tsx | 10 +--------- packages/ui/src/utils/country-picker.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/FormWidgets/CountryPicker/index.tsx b/packages/ui/src/FormWidgets/CountryPicker/index.tsx index d110177f9..da443c57e 100644 --- a/packages/ui/src/FormWidgets/CountryPicker/index.tsx +++ b/packages/ui/src/FormWidgets/CountryPicker/index.tsx @@ -3,6 +3,7 @@ import React, { useCallback, useMemo } from "react"; import { getFallbackTranslation, getFlagClass, + getLabel, } from "../../utils/country-picker"; import { Select, ISelectProperties } from "../Select"; @@ -14,15 +15,6 @@ import type { } from "../../types/country-picker"; import type { GroupedOption as OptionGroup, Option } from "../Select"; -const getLabel = ( - code: string, - locale: string, - locales: Locales | undefined, - fallbackTranslation: Translation, -) => { - return locales?.[locale]?.[code] || fallbackTranslation[code] || code; -}; - const sortByLabel = ( optionA: Option | OptionGroup, optionB: Option | OptionGroup, diff --git a/packages/ui/src/utils/country-picker.ts b/packages/ui/src/utils/country-picker.ts index 3cf102ab6..4a7542109 100644 --- a/packages/ui/src/utils/country-picker.ts +++ b/packages/ui/src/utils/country-picker.ts @@ -32,3 +32,12 @@ export const getFlagClass = ( ] .filter(Boolean) .join(" "); + +export const getLabel = ( + code: string, + locale: string, + locales: Locales | undefined, + fallbackTranslation: Translation, +) => { + return locales?.[locale]?.[code] || fallbackTranslation[code] || code; +}; From 9c0a079be50e62ab3a5f0703745e232c2e262283 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Thu, 22 Jan 2026 12:40:32 +0545 Subject: [PATCH 2/9] test(ui/country-picker): unit test for getLabel: --- .../src/utils/__test__/country-picker.test.ts | 108 +++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index ff5952eaf..74c80a309 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -1,7 +1,11 @@ import { describe, expect, test } from "vitest"; import defaultEnglishTranslation from "../../FormWidgets/CountryPicker/en.json"; -import { getFallbackTranslation, getFlagClass } from "../country-picker"; +import { + getFallbackTranslation, + getFlagClass, + getLabel, +} from "../country-picker"; const frenchTranslation = { DE: "Allemagne", @@ -127,3 +131,105 @@ describe("getFlagClass", () => { expect(result).toBe("flag-icon"); }); }); + +describe("getLabel", () => { + const frenchTranslation = { + DE: "Allemagne", + US: "États‑Unis", + FR: "France", + }; + + const spanishTranslation = { + DE: "Alemania", + US: "Estados Unidos", + FR: "Francia", + }; + + const locales = { + fr: frenchTranslation, + es: spanishTranslation, + }; + + const fallbackTranslation = { + US: "United States", + FR: "France", + DE: "Germany", + JP: "Japan", + }; + + const testCases = [ + { + name: "Should return translation from locales if available (ES -> FR)", + arguments: { + code: "FR", + locale: "es", + locales: locales, + fallback: fallbackTranslation, + }, + result: "Francia", + }, + { + name: "Should return fallback translation if locale is missing in locales", + arguments: { + code: "DE", + locale: "it", + locales: locales, + fallback: fallbackTranslation, + }, + result: "Germany", + }, + { + name: "Should return fallback translation if code is missing in specific locale", + arguments: { + code: "JP", + locale: "fr", + locales: { fr: { FR: "France" } }, + fallback: fallbackTranslation, + }, + result: "Japan", + }, + { + name: "Should return the raw code if found nowhere", + arguments: { + code: "ZZ", + locale: "fr", + locales: locales, + fallback: fallbackTranslation, + }, + result: "ZZ", + }, + { + name: "Should handle undefined locales gracefully", + arguments: { + code: "US", + locale: "fr", + locales: undefined, + fallback: fallbackTranslation, + }, + result: "United States", + }, + { + name: "Should return code if locales is undefined and fallback is missing code", + arguments: { + code: "ZZ", + locale: "en", + locales: undefined, + fallback: fallbackTranslation, + }, + result: "ZZ", + }, + ]; + + testCases.map((testCase) => { + test(testCase.name, () => { + const result = getLabel( + testCase.arguments.code, + testCase.arguments.locale, + testCase.arguments.locales, + testCase.arguments.fallback, + ); + + expect(result).toBe(testCase.result); + }); + }); +}); From dd0f89b9bcdf35031369b1768fa1615198261e55 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Thu, 22 Jan 2026 15:09:10 +0545 Subject: [PATCH 3/9] test(ui/countryPicker): refactor test functions --- .../src/utils/__test__/country-picker.test.ts | 265 ++++++++++-------- 1 file changed, 149 insertions(+), 116 deletions(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index 74c80a309..27fd283e1 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -7,128 +7,161 @@ import { getLabel, } from "../country-picker"; -const frenchTranslation = { - DE: "Allemagne", - BR: "Brésil", - CA: "Canada", - CN: "Chine", - ES: "Espagne", - US: "États‑Unis", - FR: "France", - IT: "Italie", - JP: "Japon", - GB: "Royaume‑Uni", - RU: "Russie", -}; - -const spanishTranslation = { - DE: "Alemania", - BR: "Brasil", - CA: "Canadá", - CN: "China", - ES: "España", - US: "Estados Unidos", - FR: "Francia", - IT: "Italia", - JP: "Japón", - GB: "Reino Unido", - RU: "Rusia", -}; - -const locales = { - fr: frenchTranslation, - es: spanishTranslation, -}; +import type { Locales } from "../../types"; describe("getFallbackTranslation", () => { - test("Should return translation from locales if fallbackLocale exists in it", () => { - const result = getFallbackTranslation("fr", locales); - - expect(result).toEqual(frenchTranslation); - }); - - test("Should return defaultEnglishTranslation if fallbackLocale is 'en' and not in locales", () => { - const result = getFallbackTranslation("en", {}); - - expect(result).toEqual(defaultEnglishTranslation); - }); - - test("Should prioritize locales['en'] over defaultEnglishTranslation if provided", () => { - const englishTranslation = { FR: "France" }; - const locales = { en: englishTranslation }; - const result = getFallbackTranslation("en", locales); - - expect(result).toEqual(englishTranslation); - }); + const frenchTranslation = { + DE: "Allemagne", + BR: "Brésil", + CA: "Canada", + CN: "Chine", + ES: "Espagne", + US: "États‑Unis", + FR: "France", + IT: "Italie", + JP: "Japon", + GB: "Royaume‑Uni", + RU: "Russie", + }; - test("Should return null if fallbackLocale is not found and is not 'en'", () => { - const result = getFallbackTranslation("de", locales); + const spanishTranslation = { + DE: "Alemania", + BR: "Brasil", + CA: "Canadá", + CN: "China", + ES: "España", + US: "Estados Unidos", + FR: "Francia", + IT: "Italia", + JP: "Japón", + GB: "Reino Unido", + RU: "Rusia", + }; - expect(result).toBeNull(); - }); + const locales = { + fr: frenchTranslation, + es: spanishTranslation, + }; - test("Should handle undefined locales gracefully", () => { - const result = getFallbackTranslation("fr", undefined); + const customEnglishTranslation = { FR: "France" }; - expect(result).toBeNull(); - }); + const testCases = [ + { + name: "Should return translation from locales if fallbackLocale exists in it", + arguments: { + fallbackLocale: "fr", + locales: locales, + }, + result: frenchTranslation, + }, + { + name: "Should return defaultEnglishTranslation if fallbackLocale is 'en' and not in locales", + arguments: { + fallbackLocale: "en", + locales: {}, + }, + result: defaultEnglishTranslation, + }, + { + name: "Should prioritize locales['en'] over defaultEnglishTranslation if provided", + arguments: { + fallbackLocale: "en", + locales: { en: customEnglishTranslation }, + }, + result: customEnglishTranslation, + }, + { + name: "Should return null if fallbackLocale is not found and is not 'en'", + arguments: { + fallbackLocale: "de", + locales: locales, + }, + result: null, + }, + { + name: "Should handle undefined locales gracefully", + arguments: { + fallbackLocale: "fr", + locales: undefined, + }, + result: null, + }, + { + name: "Should return defaultEnglishTranslation when locales is undefined but fallback is 'en'", + arguments: { + fallbackLocale: "en", + locales: undefined, + }, + result: defaultEnglishTranslation, + }, + ]; - test("Should return defaultEnglishTranslation when locales is undefined but fallback is 'en'", () => { - const result = getFallbackTranslation("en", undefined); + testCases.map((testCase) => { + test(testCase.name, () => { + const result = getFallbackTranslation( + testCase.arguments.fallbackLocale, + testCase.arguments.locales as Locales, + ); - expect(result).toEqual(defaultEnglishTranslation); + expect(result).toEqual(testCase.result); + }); }); }); describe("getFlagClass", () => { - test("Should generate basic class with code only", () => { - const result = getFlagClass("US", "left", "normal"); - - expect(result).toBe("flag-icon flag-icon-us"); - }); - - test("Should handle lowercase and trimming of country code", () => { - const result = getFlagClass(" GB ", "left", "normal"); - - expect(result).toBe("flag-icon flag-icon-gb"); - }); - - test("Should add 'flag-icon-right' when position is 'right'", () => { - const result = getFlagClass("fr", "right", "normal"); - - expect(result).toBe("flag-icon flag-icon-fr flag-icon-right"); - }); - - test("Should add 'flag-icon-right-edge' when position is 'right-edge'", () => { - const result = getFlagClass("fr", "right-edge", "normal"); - - expect(result).toBe("flag-icon flag-icon-fr flag-icon-right-edge"); - }); - - test("Should add 'flag-icon-rounded' when style is 'circle'", () => { - const result = getFlagClass("jp", "left", "circle"); - - expect(result).toBe("flag-icon flag-icon-jp flag-icon-rounded"); - }); - - test("Should add 'flag-icon-squared' when style is 'square'", () => { - const result = getFlagClass("jp", "left", "square"); - - expect(result).toBe("flag-icon flag-icon-jp flag-icon-squared"); - }); - - test("Should combine multiple classes correctly", () => { - const result = getFlagClass("ca", "right", "circle"); - - expect(result).toBe( - "flag-icon flag-icon-ca flag-icon-right flag-icon-rounded", - ); - }); + const testCases = [ + { + name: "Should generate basic class with code only", + arguments: { code: "US", position: "left", style: "normal" }, + result: "flag-icon flag-icon-us", + }, + { + name: "Should handle lowercase and trimming of country code", + arguments: { code: " GB ", position: "left", style: "normal" }, + result: "flag-icon flag-icon-gb", + }, + { + name: "Should add 'flag-icon-right' when position is 'right'", + arguments: { code: "fr", position: "right", style: "normal" }, + result: "flag-icon flag-icon-fr flag-icon-right", + }, + { + name: "Should add 'flag-icon-right-edge' when position is 'right-edge'", + arguments: { code: "fr", position: "right-edge", style: "normal" }, + result: "flag-icon flag-icon-fr flag-icon-right-edge", + }, + { + name: "Should add 'flag-icon-rounded' when style is 'circle'", + arguments: { code: "jp", position: "left", style: "circle" }, + result: "flag-icon flag-icon-jp flag-icon-rounded", + }, + { + name: "Should add 'flag-icon-squared' when style is 'square'", + arguments: { code: "jp", position: "left", style: "square" }, + result: "flag-icon flag-icon-jp flag-icon-squared", + }, + { + name: "Should combine multiple classes correctly", + arguments: { code: "ca", position: "right", style: "circle" }, + result: "flag-icon flag-icon-ca flag-icon-right flag-icon-rounded", + }, + { + name: "Should handle undefined code gracefully", + arguments: { code: undefined, position: "left", style: "normal" }, + result: "flag-icon", + }, + ]; - test("Should handle undefined code gracefully", () => { - const result = getFlagClass(undefined, "left", "normal"); + testCases.map((testCase) => { + test(testCase.name, () => { + const result = getFlagClass( + testCase.arguments.code as string, + testCase.arguments.position, + testCase.arguments.style, + ); - expect(result).toBe("flag-icon"); + expect(result).toBe(testCase.result); + }); }); }); @@ -164,7 +197,7 @@ describe("getLabel", () => { code: "FR", locale: "es", locales: locales, - fallback: fallbackTranslation, + fallbackTranslation: fallbackTranslation, }, result: "Francia", }, @@ -174,7 +207,7 @@ describe("getLabel", () => { code: "DE", locale: "it", locales: locales, - fallback: fallbackTranslation, + fallbackTranslation: fallbackTranslation, }, result: "Germany", }, @@ -184,7 +217,7 @@ describe("getLabel", () => { code: "JP", locale: "fr", locales: { fr: { FR: "France" } }, - fallback: fallbackTranslation, + fallbackTranslation: fallbackTranslation, }, result: "Japan", }, @@ -194,7 +227,7 @@ describe("getLabel", () => { code: "ZZ", locale: "fr", locales: locales, - fallback: fallbackTranslation, + fallbackTranslation: fallbackTranslation, }, result: "ZZ", }, @@ -204,7 +237,7 @@ describe("getLabel", () => { code: "US", locale: "fr", locales: undefined, - fallback: fallbackTranslation, + fallbackTranslation: fallbackTranslation, }, result: "United States", }, @@ -214,7 +247,7 @@ describe("getLabel", () => { code: "ZZ", locale: "en", locales: undefined, - fallback: fallbackTranslation, + fallbackTranslation: fallbackTranslation, }, result: "ZZ", }, @@ -225,8 +258,8 @@ describe("getLabel", () => { const result = getLabel( testCase.arguments.code, testCase.arguments.locale, - testCase.arguments.locales, - testCase.arguments.fallback, + testCase.arguments.locales as Locales, + testCase.arguments.fallbackTranslation, ); expect(result).toBe(testCase.result); From f077e06fff2493024fa28e84baf5f09848006e83 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Fri, 23 Jan 2026 16:23:57 +0545 Subject: [PATCH 4/9] test(ui/country-picker): destructure object --- .../ui/src/utils/__test__/country-picker.test.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index 27fd283e1..851582f08 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -16,11 +16,11 @@ describe("getFallbackTranslation", () => { CA: "Canada", CN: "Chine", ES: "Espagne", - US: "États‑Unis", + US: "États-Unis", FR: "France", IT: "Italie", JP: "Japon", - GB: "Royaume‑Uni", + GB: "Royaume-Uni", RU: "Russie", }; @@ -168,7 +168,7 @@ describe("getFlagClass", () => { describe("getLabel", () => { const frenchTranslation = { DE: "Allemagne", - US: "États‑Unis", + US: "États-Unis", FR: "France", }; @@ -254,13 +254,10 @@ describe("getLabel", () => { ]; testCases.map((testCase) => { + const { code, locale, locales, fallbackTranslation } = testCase.arguments; + test(testCase.name, () => { - const result = getLabel( - testCase.arguments.code, - testCase.arguments.locale, - testCase.arguments.locales as Locales, - testCase.arguments.fallbackTranslation, - ); + const result = getLabel(code, locale, locales, fallbackTranslation); expect(result).toBe(testCase.result); }); From e041387b8fd10da7445c18ef483f2a7cd60bcf92 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Fri, 23 Jan 2026 16:30:49 +0545 Subject: [PATCH 5/9] test(ui/country-picker): destructure objects --- .../ui/src/utils/__test__/country-picker.test.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index 851582f08..adc7d465d 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -97,11 +97,10 @@ describe("getFallbackTranslation", () => { ]; testCases.map((testCase) => { + const { fallbackLocale, locales } = testCase.arguments; + test(testCase.name, () => { - const result = getFallbackTranslation( - testCase.arguments.fallbackLocale, - testCase.arguments.locales as Locales, - ); + const result = getFallbackTranslation(fallbackLocale, locales as Locales); expect(result).toEqual(testCase.result); }); @@ -153,12 +152,10 @@ describe("getFlagClass", () => { ]; testCases.map((testCase) => { + const { code, position, style } = testCase.arguments; + test(testCase.name, () => { - const result = getFlagClass( - testCase.arguments.code as string, - testCase.arguments.position, - testCase.arguments.style, - ); + const result = getFlagClass(code, position, style); expect(result).toBe(testCase.result); }); From 1479ee4cb8ace8b22f7d526f0d37779b0ddadcae Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Fri, 23 Jan 2026 16:37:24 +0545 Subject: [PATCH 6/9] test: arrange in alphabetical order --- .../src/utils/__test__/country-picker.test.ts | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index adc7d465d..f91602589 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -47,51 +47,51 @@ describe("getFallbackTranslation", () => { const testCases = [ { - name: "Should return translation from locales if fallbackLocale exists in it", arguments: { fallbackLocale: "fr", locales: locales, }, + name: "Should return translation from locales if fallbackLocale exists in it", result: frenchTranslation, }, { - name: "Should return defaultEnglishTranslation if fallbackLocale is 'en' and not in locales", arguments: { fallbackLocale: "en", locales: {}, }, + name: "Should return defaultEnglishTranslation if fallbackLocale is 'en' and not in locales", result: defaultEnglishTranslation, }, { - name: "Should prioritize locales['en'] over defaultEnglishTranslation if provided", arguments: { fallbackLocale: "en", locales: { en: customEnglishTranslation }, }, + name: "Should prioritize locales['en'] over defaultEnglishTranslation if provided", result: customEnglishTranslation, }, { - name: "Should return null if fallbackLocale is not found and is not 'en'", arguments: { fallbackLocale: "de", locales: locales, }, + name: "Should return null if fallbackLocale is not found and is not 'en'", result: null, }, { - name: "Should handle undefined locales gracefully", arguments: { fallbackLocale: "fr", locales: undefined, }, + name: "Should handle undefined locales gracefully", result: null, }, { - name: "Should return defaultEnglishTranslation when locales is undefined but fallback is 'en'", arguments: { fallbackLocale: "en", locales: undefined, }, + name: "Should return defaultEnglishTranslation when locales is undefined but fallback is 'en'", result: defaultEnglishTranslation, }, ]; @@ -110,43 +110,43 @@ describe("getFallbackTranslation", () => { describe("getFlagClass", () => { const testCases = [ { - name: "Should generate basic class with code only", arguments: { code: "US", position: "left", style: "normal" }, + name: "Should generate basic class with code only", result: "flag-icon flag-icon-us", }, { - name: "Should handle lowercase and trimming of country code", arguments: { code: " GB ", position: "left", style: "normal" }, + name: "Should handle lowercase and trimming of country code", result: "flag-icon flag-icon-gb", }, { - name: "Should add 'flag-icon-right' when position is 'right'", arguments: { code: "fr", position: "right", style: "normal" }, + name: "Should add 'flag-icon-right' when position is 'right'", result: "flag-icon flag-icon-fr flag-icon-right", }, { - name: "Should add 'flag-icon-right-edge' when position is 'right-edge'", arguments: { code: "fr", position: "right-edge", style: "normal" }, + name: "Should add 'flag-icon-right-edge' when position is 'right-edge'", result: "flag-icon flag-icon-fr flag-icon-right-edge", }, { - name: "Should add 'flag-icon-rounded' when style is 'circle'", arguments: { code: "jp", position: "left", style: "circle" }, + name: "Should add 'flag-icon-rounded' when style is 'circle'", result: "flag-icon flag-icon-jp flag-icon-rounded", }, { - name: "Should add 'flag-icon-squared' when style is 'square'", arguments: { code: "jp", position: "left", style: "square" }, + name: "Should add 'flag-icon-squared' when style is 'square'", result: "flag-icon flag-icon-jp flag-icon-squared", }, { name: "Should combine multiple classes correctly", - arguments: { code: "ca", position: "right", style: "circle" }, result: "flag-icon flag-icon-ca flag-icon-right flag-icon-rounded", + arguments: { code: "ca", position: "right", style: "circle" }, }, { - name: "Should handle undefined code gracefully", arguments: { code: undefined, position: "left", style: "normal" }, + name: "Should handle undefined code gracefully", result: "flag-icon", }, ]; @@ -189,63 +189,63 @@ describe("getLabel", () => { const testCases = [ { - name: "Should return translation from locales if available (ES -> FR)", arguments: { code: "FR", + fallbackTranslation: fallbackTranslation, locale: "es", locales: locales, - fallbackTranslation: fallbackTranslation, }, + name: "Should return translation from locales if available (ES -> FR)", result: "Francia", }, { - name: "Should return fallback translation if locale is missing in locales", arguments: { code: "DE", + fallbackTranslation: fallbackTranslation, locale: "it", locales: locales, - fallbackTranslation: fallbackTranslation, }, + name: "Should return fallback translation if locale is missing in locales", result: "Germany", }, { - name: "Should return fallback translation if code is missing in specific locale", arguments: { code: "JP", + fallbackTranslation: fallbackTranslation, locale: "fr", locales: { fr: { FR: "France" } }, - fallbackTranslation: fallbackTranslation, }, + name: "Should return fallback translation if code is missing in specific locale", result: "Japan", }, { - name: "Should return the raw code if found nowhere", arguments: { code: "ZZ", + fallbackTranslation: fallbackTranslation, locale: "fr", locales: locales, - fallbackTranslation: fallbackTranslation, }, + name: "Should return the raw code if found nowhere", result: "ZZ", }, { - name: "Should handle undefined locales gracefully", arguments: { code: "US", + fallbackTranslation: fallbackTranslation, locale: "fr", locales: undefined, - fallbackTranslation: fallbackTranslation, }, + name: "Should handle undefined locales gracefully", result: "United States", }, { - name: "Should return code if locales is undefined and fallback is missing code", arguments: { code: "ZZ", + fallbackTranslation: fallbackTranslation, locale: "en", locales: undefined, - fallbackTranslation: fallbackTranslation, }, + name: "Should return code if locales is undefined and fallback is missing code", result: "ZZ", }, ]; From 006206d13d4be99163b91347409e2127d6166de4 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Mon, 26 Jan 2026 11:34:05 +0545 Subject: [PATCH 7/9] test(ui/country-picker): add tests for sortByLabel --- .../src/FormWidgets/CountryPicker/index.tsx | 16 +--- .../src/utils/__test__/country-picker.test.ts | 80 ++++++++++++++++++- packages/ui/src/utils/country-picker.ts | 19 +++++ 3 files changed, 99 insertions(+), 16 deletions(-) diff --git a/packages/ui/src/FormWidgets/CountryPicker/index.tsx b/packages/ui/src/FormWidgets/CountryPicker/index.tsx index da443c57e..fa8c9ace5 100644 --- a/packages/ui/src/FormWidgets/CountryPicker/index.tsx +++ b/packages/ui/src/FormWidgets/CountryPicker/index.tsx @@ -4,6 +4,7 @@ import { getFallbackTranslation, getFlagClass, getLabel, + sortByLabel, } from "../../utils/country-picker"; import { Select, ISelectProperties } from "../Select"; @@ -15,21 +16,6 @@ import type { } from "../../types/country-picker"; import type { GroupedOption as OptionGroup, Option } from "../Select"; -const sortByLabel = ( - optionA: Option | OptionGroup, - optionB: Option | OptionGroup, -) => { - if (!optionA.label) { - return 1; - } - - if (!optionB.label) { - return -1; - } - - return optionA.label.localeCompare(optionB.label); -}; - const getFavoriteOptions = ( favorites: string[], locale: string, diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index f91602589..00f678ac2 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -5,6 +5,7 @@ import { getFallbackTranslation, getFlagClass, getLabel, + sortByLabel, } from "../country-picker"; import type { Locales } from "../../types"; @@ -140,9 +141,9 @@ describe("getFlagClass", () => { result: "flag-icon flag-icon-jp flag-icon-squared", }, { + arguments: { code: "ca", position: "right", style: "circle" }, name: "Should combine multiple classes correctly", result: "flag-icon flag-icon-ca flag-icon-right flag-icon-rounded", - arguments: { code: "ca", position: "right", style: "circle" }, }, { arguments: { code: undefined, position: "left", style: "normal" }, @@ -260,3 +261,80 @@ describe("getLabel", () => { }); }); }); + +describe("sortByLabel", () => { + const optionAlpha = { label: "Alpha", value: 1 }; + const optionBeta = { label: "Beta", value: 2 }; + const optionZebra = { label: "Zebra", value: 3 }; + const optionNoLabel = { label: undefined, value: 4 }; + const optionEmptyLabel = { label: "", value: 5 }; + + const testCases = [ + { + arguments: { + optionA: optionAlpha, + optionB: optionBeta, + }, + name: "Should return negative number when Option A comes alphabetically before Option B", + result: -1, + }, + { + arguments: { + optionA: optionZebra, + optionB: optionAlpha, + }, + name: "Should return positive number when Option A comes alphabetically after Option B", + result: 1, + }, + { + arguments: { + optionA: optionAlpha, + optionB: { ...optionAlpha, value: 99 }, + }, + name: "Should return 0 when labels are identical", + result: 0, + }, + { + arguments: { + optionA: optionNoLabel, + optionB: optionAlpha, + }, + name: "Should return 1 (move A to end) if Option A has no label", + result: 1, + }, + { + arguments: { + optionA: optionAlpha, + optionB: optionNoLabel, + }, + name: "Should return -1 (move B to end) if Option B has no label", + result: -1, + }, + { + arguments: { + optionA: optionEmptyLabel, + optionB: optionAlpha, + }, + name: "Should return 1 (move A to end) if Option A has an empty string label", + result: 1, + }, + { + arguments: { + optionA: optionNoLabel, + optionB: optionNoLabel, + }, + name: "Should return 1 (move A to end) if both options have missing labels", + result: 1, + }, + ]; + + testCases.map((testCase) => { + const { optionA, optionB } = testCase.arguments; + + test(testCase.name, () => { + const result = sortByLabel(optionA, optionB); + + expect(result).toBe(testCase.result); + }); + }); +}); diff --git a/packages/ui/src/utils/country-picker.ts b/packages/ui/src/utils/country-picker.ts index 4a7542109..31e39dba8 100644 --- a/packages/ui/src/utils/country-picker.ts +++ b/packages/ui/src/utils/country-picker.ts @@ -1,5 +1,9 @@ import defaultEnglishTranslation from "../FormWidgets/CountryPicker/en.json"; +import type { + GroupedOption as OptionGroup, + Option, +} from "../FormWidgets/Select"; import type { Locales, Translation } from "../types"; export const getFallbackTranslation = ( @@ -41,3 +45,18 @@ export const getLabel = ( ) => { return locales?.[locale]?.[code] || fallbackTranslation[code] || code; }; + +export const sortByLabel = ( + optionA: Option | OptionGroup, + optionB: Option | OptionGroup, +) => { + if (!optionA.label) { + return 1; + } + + if (!optionB.label) { + return -1; + } + + return optionA.label.localeCompare(optionB.label); +}; From c5e589685a18ba8487bf16be12a617f32986ad83 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Mon, 26 Jan 2026 12:58:08 +0545 Subject: [PATCH 8/9] test(ui/country-picker): update tests --- .../src/utils/__test__/country-picker.test.ts | 110 +++++++++--------- packages/ui/src/utils/country-picker.ts | 10 +- 2 files changed, 58 insertions(+), 62 deletions(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index 00f678ac2..27e2866a3 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -262,79 +262,83 @@ describe("getLabel", () => { }); }); -describe("sortByLabel", () => { - const optionAlpha = { label: "Alpha", value: 1 }; - const optionBeta = { label: "Beta", value: 2 }; - const optionZebra = { label: "Zebra", value: 3 }; - const optionNoLabel = { label: undefined, value: 4 }; - const optionEmptyLabel = { label: "", value: 5 }; +describe("sortByLabel with Real Country Data", () => { + const optionAfghanistan = { label: "Afghanistan", value: "AF" }; + const optionAlbania = { label: "Albania", value: "AL" }; + const optionAlandIslands = { label: "Åland Islands", value: "AX" }; + const optionAlgeria = { label: "Algeria", value: "DZ" }; + + const groupEuropeanHubs = { + label: "European Hubs", + options: [ + { label: "United Kingdom", value: "GB" }, + { label: "Germany", value: "DE" }, + { label: "France", value: "FR" }, + ], + }; + + const groupNorthAmerica = { + label: "North America HQ", + options: [ + { label: "United States", value: "US" }, + { label: "Canada", value: "CA" }, + ], + }; + + const groupOffshore = { + label: "Offshore Dev Center", + options: [{ label: "India", value: "IN" }], + }; const testCases = [ { - arguments: { - optionA: optionAlpha, - optionB: optionBeta, - }, - name: "Should return negative number when Option A comes alphabetically before Option B", - result: -1, + name: "Should sort 'Afghanistan' before 'Albania'", + args: { a: optionAfghanistan, b: optionAlbania }, + expected: "negative", }, { - arguments: { - optionA: optionZebra, - optionB: optionAlpha, - }, - name: "Should return positive number when Option A comes alphabetically after Option B", - result: 1, + name: "Should sort 'Albania' before 'Algeria'", + args: { a: optionAlbania, b: optionAlgeria }, + expected: "negative", }, { - arguments: { - optionA: optionAlpha, - optionB: { ...optionAlpha, value: 99 }, - }, - name: "Should return 0 when labels are identical", - result: 0, + name: "Should sort 'Afghanistan' before 'Åland Islands' (Standard Locale)", + args: { a: optionAfghanistan, b: optionAlandIslands }, + expected: "negative", }, { - arguments: { - optionA: optionNoLabel, - optionB: optionAlpha, - }, - name: "Should return 1 (move A to end) if Option A has no label", - result: 1, + name: "Should sort 'European Hubs' before 'North America HQ'", + args: { a: groupEuropeanHubs, b: groupNorthAmerica }, + expected: "negative", }, { - arguments: { - optionA: optionAlpha, - optionB: optionNoLabel, - }, - name: "Should return -1 (move B to end) if Option B has no label", - result: -1, + name: "Should sort 'Offshore Dev Center' after 'North America HQ'", + args: { a: groupOffshore, b: groupNorthAmerica }, + expected: "positive", }, { - arguments: { - optionA: optionEmptyLabel, - optionB: optionAlpha, - }, - name: "Should return 1 (move A to end) if Option A has an empty string label", - result: 1, + name: "Should sort 'Algeria' (Option) before 'European Hubs' (Group)", + args: { a: optionAlgeria, b: groupEuropeanHubs }, + expected: "negative", }, { - arguments: { - optionA: optionNoLabel, - optionB: optionNoLabel, - }, - name: "Should return 1 (move A to end) if both options have missing labels", - result: 1, + name: "Should sort 'North America HQ' (Group) after 'Afghanistan' (Option)", + args: { a: groupNorthAmerica, b: optionAfghanistan }, + expected: "positive", }, ]; - testCases.map((testCase) => { - const { optionA, optionB } = testCase.arguments; - + testCases.forEach((testCase) => { test(testCase.name, () => { - const result = sortByLabel(optionA, optionB); + const result = sortByLabel(testCase.args.a, testCase.args.b); - expect(result).toBe(testCase.result); + if (testCase.expected === "negative") { + expect(result).toBeLessThan(0); + } else if (testCase.expected === "positive") { + expect(result).toBeGreaterThan(0); + } else { + expect(result).toBe(0); + } }); }); }); diff --git a/packages/ui/src/utils/country-picker.ts b/packages/ui/src/utils/country-picker.ts index 31e39dba8..63e7a1234 100644 --- a/packages/ui/src/utils/country-picker.ts +++ b/packages/ui/src/utils/country-picker.ts @@ -50,13 +50,5 @@ export const sortByLabel = ( optionA: Option | OptionGroup, optionB: Option | OptionGroup, ) => { - if (!optionA.label) { - return 1; - } - - if (!optionB.label) { - return -1; - } - - return optionA.label.localeCompare(optionB.label); + return (optionA.label ?? "").localeCompare(optionB.label ?? ""); }; From f77c70d6389af5989ef43fff5ddb2d705acae423 Mon Sep 17 00:00:00 2001 From: Utsav Luintel Date: Mon, 26 Jan 2026 13:35:31 +0545 Subject: [PATCH 9/9] test(ui/country-picker): update test cases' --- .../src/utils/__test__/country-picker.test.ts | 104 ++++++++---------- 1 file changed, 43 insertions(+), 61 deletions(-) diff --git a/packages/ui/src/utils/__test__/country-picker.test.ts b/packages/ui/src/utils/__test__/country-picker.test.ts index 27e2866a3..95984b800 100644 --- a/packages/ui/src/utils/__test__/country-picker.test.ts +++ b/packages/ui/src/utils/__test__/country-picker.test.ts @@ -8,6 +8,7 @@ import { sortByLabel, } from "../country-picker"; +import type { Option } from "../../FormWidgets/Select"; import type { Locales } from "../../types"; describe("getFallbackTranslation", () => { @@ -262,83 +263,64 @@ describe("getLabel", () => { }); }); -describe("sortByLabel with Real Country Data", () => { - const optionAfghanistan = { label: "Afghanistan", value: "AF" }; - const optionAlbania = { label: "Albania", value: "AL" }; - const optionAlandIslands = { label: "Åland Islands", value: "AX" }; - const optionAlgeria = { label: "Algeria", value: "DZ" }; - - const groupEuropeanHubs = { - label: "European Hubs", - options: [ - { label: "United Kingdom", value: "GB" }, - { label: "Germany", value: "DE" }, - { label: "France", value: "FR" }, - ], - }; - - const groupNorthAmerica = { - label: "North America HQ", - options: [ - { label: "United States", value: "US" }, - { label: "Canada", value: "CA" }, - ], - }; - - const groupOffshore = { - label: "Offshore Dev Center", - options: [{ label: "India", value: "IN" }], - }; +describe("sortByLabel", () => { + const selectOptions = [ + { label: "France", value: "FR" }, + { label: "Nepal", value: "NP" }, + { label: "Thailand", value: "TH" }, + { label: "", value: "ZZ" }, + ] as Option[]; const testCases = [ { - name: "Should sort 'Afghanistan' before 'Albania'", - args: { a: optionAfghanistan, b: optionAlbania }, - expected: "negative", - }, - { - name: "Should sort 'Albania' before 'Algeria'", - args: { a: optionAlbania, b: optionAlgeria }, - expected: "negative", - }, - { - name: "Should sort 'Afghanistan' before 'Åland Islands' (Standard Locale)", - args: { a: optionAfghanistan, b: optionAlandIslands }, - expected: "negative", + arguments: { + optionA: selectOptions[0], + optionB: selectOptions[1], + }, + name: "Should return negative number when Option A comes alphabetically before Option B", + result: -1, }, { - name: "Should sort 'European Hubs' before 'North America HQ'", - args: { a: groupEuropeanHubs, b: groupNorthAmerica }, - expected: "negative", + arguments: { + optionA: selectOptions[1], + optionB: selectOptions[0], + }, + name: "Should return positive number when Option A comes alphabetically after Option B", + result: 1, }, { - name: "Should sort 'Offshore Dev Center' after 'North America HQ'", - args: { a: groupOffshore, b: groupNorthAmerica }, - expected: "positive", + arguments: { + optionA: selectOptions[0], + optionB: selectOptions[0], + }, + name: "Should return 0 when labels are identical", + result: 0, }, { - name: "Should sort 'Algeria' (Option) before 'European Hubs' (Group)", - args: { a: optionAlgeria, b: groupEuropeanHubs }, - expected: "negative", + arguments: { + optionA: selectOptions[3], + optionB: selectOptions[0], + }, + name: "Should return negative if Option A has no label", + result: -1, }, { - name: "Should sort 'North America HQ' (Group) after 'Afghanistan' (Option)", - args: { a: groupNorthAmerica, b: optionAfghanistan }, - expected: "positive", + arguments: { + optionA: selectOptions[0], + optionB: selectOptions[3], + }, + name: "Should return positive if Option B has no label", + result: 1, }, ]; - testCases.forEach((testCase) => { + testCases.map((testCase) => { + const { optionA, optionB } = testCase.arguments; + test(testCase.name, () => { - const result = sortByLabel(testCase.args.a, testCase.args.b); + const result = sortByLabel(optionA, optionB); - if (testCase.expected === "negative") { - expect(result).toBeLessThan(0); - } else if (testCase.expected === "positive") { - expect(result).toBeGreaterThan(0); - } else { - expect(result).toBe(0); - } + expect(result).toBe(testCase.result); }); }); });