From 9d041b130c676839b030a447067314c61d8603d4 Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Mon, 30 Dec 2024 13:20:05 +0100 Subject: [PATCH] :bug: Fix some a11y errors on select component --- .../src/app/main/ui/ds/controls/select.cljs | 57 +++++++++++++------ .../src/app/main/ui/ds/controls/select.scss | 4 ++ .../ds/controls/shared/options_dropdown.cljs | 2 + .../ds/controls/shared/options_dropdown.scss | 6 ++ 4 files changed, 51 insertions(+), 18 deletions(-) diff --git a/frontend/src/app/main/ui/ds/controls/select.cljs b/frontend/src/app/main/ui/ds/controls/select.cljs index 0d2730ebb..8269889df 100644 --- a/frontend/src/app/main/ui/ds/controls/select.cljs +++ b/frontend/src/app/main/ui/ds/controls/select.cljs @@ -17,6 +17,8 @@ [app.util.object :as obj] [rumext.v2 :as mf])) +(def listbox-id-index (atom 0)) + (def ^:private schema:select-option [:and [:map {:title "option"} @@ -72,13 +74,12 @@ [{:keys [options class disabled default-selected on-change] :rest props}] (let [open* (mf/use-state false) open (deref open*) - on-click - (mf/use-fn - (mf/deps disabled) - (fn [event] - (dom/stop-propagation event) - (when-not disabled - (swap! open* not)))) + + listbox-id-ref (mf/use-ref (dm/str "select-listbox-" (swap! listbox-id-index inc))) + options-nodes-refs (mf/use-ref nil) + options-ref (mf/use-ref nil) + select-ref (mf/use-ref nil) + listbox-id (mf/ref-val listbox-id-ref) selected* (mf/use-state #(get-selected-option-id options default-selected)) selected (deref selected*) @@ -86,6 +87,18 @@ focused* (mf/use-state nil) focused (deref focused*) + has-focus* (mf/use-state false) + has-focus (deref has-focus*) + + on-click + (mf/use-fn + (mf/deps disabled) + (fn [event] + (dom/stop-propagation event) + (reset! has-focus* true) + (when-not disabled + (swap! open* not)))) + on-option-click (mf/use-fn (mf/deps on-change) @@ -98,9 +111,6 @@ (when (fn? on-change) (on-change id))))) - options-nodes-refs (mf/use-ref nil) - options-ref (mf/use-ref nil) - set-ref (mf/use-fn (fn [node id] @@ -113,10 +123,12 @@ on-blur (mf/use-fn (fn [event] - (let [click-outside (nil? (.-relatedTarget event))] - (when click-outside + (let [target (.-relatedTarget event) + outside? (not (.contains (mf/ref-val select-ref) target))] + (when outside? (reset! focused* nil) - (reset! open* false))))) + (reset! open* false) + (reset! has-focus* false))))) on-key-down (mf/use-fn @@ -146,18 +158,22 @@ (do (reset! open* false) (reset! focused* nil))))))) - class (dm/str class " " (stl/css :select)) + on-focus + (mf/use-fn + (fn [_] (reset! has-focus* true))) + + class (dm/str class " " (stl/css-case :select true + :focused has-focus)) props (mf/spread-props props {:class class :role "combobox" - :aria-controls "listbox" + :aria-controls listbox-id :aria-haspopup "listbox" :aria-activedescendant focused :aria-expanded open :on-key-down on-key-down :disabled disabled - :on-click on-click - :on-blur on-blur}) + :on-click on-click}) selected-option (get-option options selected) label (obj/get selected-option "label") @@ -166,7 +182,11 @@ (mf/with-effect [options] (mf/set-ref-val! options-ref options)) - [:div {:class (stl/css :select-wrapper)} + [:div {:class (stl/css :select-wrapper) + :on-click on-click + :on-focus on-focus + :ref select-ref + :on-blur on-blur} [:> :button props [:span {:class (stl/css-case :select-header true :header-icon (some? icon))} @@ -182,6 +202,7 @@ :aria-hidden true}]] (when open [:> options-dropdown* {:on-click on-option-click + :id listbox-id :options options :selected selected :focused focused diff --git a/frontend/src/app/main/ui/ds/controls/select.scss b/frontend/src/app/main/ui/ds/controls/select.scss index 072870cd4..db13d2c25 100644 --- a/frontend/src/app/main/ui/ds/controls/select.scss +++ b/frontend/src/app/main/ui/ds/controls/select.scss @@ -53,6 +53,10 @@ appearance: none; } +.focused { + --select-outline-color: var(--color-accent-primary); +} + .arrow { color: var(--select-icon-fg-color); transform: rotate(90deg); diff --git a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs index 16ce0240a..d42a4de70 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs +++ b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.cljs @@ -16,9 +16,11 @@ {::mf/props :obj ::mf/private true} [{:keys [id label icon aria-label on-click selected set-ref focused] :rest props}] + [:> :li {:value id :class (stl/css-case :option true :option-with-icon (some? icon) + :option-selected selected :option-current focused) :aria-selected selected :ref (fn [node] diff --git a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss index 8b330e30b..bdaf95f66 100644 --- a/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss +++ b/frontend/src/app/main/ui/ds/controls/shared/options_dropdown.scss @@ -44,6 +44,7 @@ outline: $b-1 solid var(--options-dropdown-outline-color); outline-offset: -1px; background-color: var(--options-dropdown-bg-color); + color: var(--options-dropdown-fg-color); &:hover, &[aria-selected="true"] { @@ -72,3 +73,8 @@ --options-dropdown-outline-color: var(--color-accent-primary); outline: $b-1 solid var(--options-dropdown-outline-color); } + +.option-selected { + --options-dropdown-fg-color: var(--color-accent-primary); + --options-dropdown-icon-fg-color: var(--color-accent-primary); +}