diff --git a/frontend/src/app/main/ui/dashboard/organization_team_switch.cljs b/frontend/src/app/main/ui/dashboard/organization_team_switch.cljs index 9d99ecb3c0..62144bfbf7 100644 --- a/frontend/src/app/main/ui/dashboard/organization_team_switch.cljs +++ b/frontend/src/app/main/ui/dashboard/organization_team_switch.cljs @@ -69,6 +69,18 @@ (nil? (dm/get-in team [:organization :id])) (contains? #{"unlimited" "enterprise"} (get-subscription-type (:subscription team))))) +(def ^:private combining-marks-re (js/RegExp. "\\p{M}" "gu")) + +(defn- name-sort-key + "Key that sorts `label` alphabetically regardless of case and + accents: NFD splits an accented letter into its base letter plus a + combining mark, and dropping the marks makes \"Ábaco\" sort like + \"abaco\" instead of after \"z\"." + [label] + (-> (.normalize label "NFD") + (str/replace combining-marks-re "") + (str/lower))) + (defn sort-organization-teams "Orders the teams of a single organization for the dropdown's second column: alphabetical, with the organization's default team @@ -76,7 +88,7 @@ `:is-default` and an explicit `false` into one comparator value." [teams] (sort-by (juxt (fn [team] (boolean (:is-default team))) - (fn [team] (str/lower (:name team "")))) + (fn [team] (name-sort-key (:name team "")))) teams)) (defn sort-all-teams @@ -89,7 +101,7 @@ (sort-all-teams teams team-display-name)) ([teams display-name] (sort-by (juxt (fn [team] (boolean (:is-default team))) - (comp str/lower display-name)) + (comp name-sort-key display-name)) teams))) (defn teams-for-organization @@ -113,7 +125,7 @@ stable." [organizations] (sort-by (juxt (fn [organization] (if (nil? (:id organization)) 1 0)) - (fn [organization] (str/lower (:name organization ""))) + (fn [organization] (name-sort-key (:name organization ""))) :id) organizations)) diff --git a/frontend/test/frontend_tests/ui/organization_team_switch_test.cljs b/frontend/test/frontend_tests/ui/organization_team_switch_test.cljs index 6cf00fa228..6d10a260f4 100644 --- a/frontend/test/frontend_tests/ui/organization_team_switch_test.cljs +++ b/frontend/test/frontend_tests/ui/organization_team_switch_test.cljs @@ -46,6 +46,26 @@ (t/is (= ["org-a" "org-b" "org-c" nil] (map :id (dts/sort-organizations organizations)))))) +(t/deftest names-starting-with-an-accented-letter-sort-as-the-plain-letter + ;; A code-point comparison would push "Ábaco", "Órbita" and "Ámbar" + ;; after "z"; accented letters must sort like their base letter. + (let [organizations [{:id "org-1" :name "OrgA"} + nil + {:id "org-2" :name "Órbita"} + {:id "org-3" :name "beta"} + {:id "org-4" :name "Ábaco"}] + teams [{:id "t1" :name "zeta"} + {:id "t2" :name "Personal projects" :is-default true} + {:id "t3" :name "apricot"} + {:id "t4" :name "Ámbar"}] + display-name (fn [team] (if (:is-default team) "Personal projects" (:name team)))] + (t/is (= ["Ábaco" "beta" "Órbita" "OrgA" nil] + (map :name (dts/sort-organizations organizations)))) + (t/is (= ["Ámbar" "apricot" "zeta" "Personal projects"] + (map :name (dts/sort-organization-teams teams)))) + (t/is (= ["Ámbar" "apricot" "zeta" "Personal projects"] + (map :name (dts/sort-all-teams teams display-name)))))) + (t/deftest closed-control-line-2-matrix (t/testing "profile with organizations, destination has an organization" (t/is (= "Org A"