0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-21 06:02:32 -05:00

🐛 Fix some a11y errors on select component

This commit is contained in:
Eva Marco 2024-12-30 13:20:05 +01:00
parent e683564ab2
commit cef89e7734
4 changed files with 69 additions and 38 deletions

View file

@ -17,6 +17,8 @@
[app.util.object :as obj] [app.util.object :as obj]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def listbox-id-index (atom 0))
(def ^:private schema:select-option (def ^:private schema:select-option
[:and [:and
[:map {:title "option"} [:map {:title "option"}
@ -71,14 +73,9 @@
::mf/schema schema:select} ::mf/schema schema:select}
[{:keys [options class disabled default-selected on-change] :rest props}] [{:keys [options class disabled default-selected on-change] :rest props}]
(let [open* (mf/use-state false) (let [open* (mf/use-state false)
listbox-id-ref (mf/use-ref (dm/str "select-listbox-" (swap! listbox-id-index inc)))
listbox-id (mf/ref-val listbox-id-ref)
open (deref open*) open (deref open*)
on-click
(mf/use-fn
(mf/deps disabled)
(fn [event]
(dom/stop-propagation event)
(when-not disabled
(swap! open* not))))
selected* (mf/use-state #(get-selected-option-id options default-selected)) selected* (mf/use-state #(get-selected-option-id options default-selected))
selected (deref selected*) selected (deref selected*)
@ -86,6 +83,18 @@
focused* (mf/use-state nil) focused* (mf/use-state nil)
focused (deref focused*) 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 on-option-click
(mf/use-fn (mf/use-fn
(mf/deps on-change) (mf/deps on-change)
@ -116,7 +125,8 @@
(let [click-outside (nil? (.-relatedTarget event))] (let [click-outside (nil? (.-relatedTarget event))]
(when click-outside (when click-outside
(reset! focused* nil) (reset! focused* nil)
(reset! open* false))))) (reset! open* false)
(reset! has-focus* false)))))
on-key-down on-key-down
(mf/use-fn (mf/use-fn
@ -146,12 +156,17 @@
(do (reset! open* false) (do (reset! open* false)
(reset! focused* nil))))))) (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 props (mf/spread-props props {:class class
:role "combobox" :role "combobox"
:aria-controls "listbox" :aria-controls listbox-id
:aria-haspopup "listbox" :aria-haspopup listbox-id
:aria-activedescendant focused :aria-activedescendant focused
:aria-expanded open :aria-expanded open
:on-key-down on-key-down :on-key-down on-key-down
@ -166,7 +181,10 @@
(mf/with-effect [options] (mf/with-effect [options]
(mf/set-ref-val! options-ref 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
:on-blur on-blur}
[:> :button props [:> :button props
[:span {:class (stl/css-case :select-header true [:span {:class (stl/css-case :select-header true
:header-icon (some? icon))} :header-icon (some? icon))}

View file

@ -53,6 +53,10 @@
appearance: none; appearance: none;
} }
.focused {
--select-outline-color: var(--color-accent-primary);
}
.arrow { .arrow {
color: var(--select-icon-fg-color); color: var(--select-icon-fg-color);
transform: rotate(90deg); transform: rotate(90deg);

View file

@ -16,34 +16,37 @@
{::mf/props :obj {::mf/props :obj
::mf/private true} ::mf/private true}
[{:keys [id label icon aria-label on-click selected set-ref focused] :rest props}] [{: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-current focused)
:aria-selected selected
:ref (fn [node]
(set-ref node id))
:role "option"
:id id
:on-click on-click
:data-id id
:data-testid "dropdown-option"}
(when (some? icon) (let [_ (prn selected)]
[:> icon* [:> :li {:value id
{:id icon :class (stl/css-case :option true
:size "s" :option-with-icon (some? icon)
:class (stl/css :option-icon) :option-selected selected
:aria-hidden (when label true) :option-current focused)
:aria-label (when (not label) aria-label)}]) :aria-selected selected
:ref (fn [node]
(set-ref node id))
:role "option"
:id id
:on-click on-click
:data-id id
:data-testid "dropdown-option"}
[:span {:class (stl/css :option-text)} label] (when (some? icon)
(when selected [:> icon*
[:> icon* {:id icon
{:id i/tick :size "s"
:size "s" :class (stl/css :option-icon)
:class (stl/css :option-check) :aria-hidden (when label true)
:aria-hidden (when label true)}])]) :aria-label (when (not label) aria-label)}])
[:span {:class (stl/css :option-text)} label]
(when selected
[:> icon*
{:id i/tick
:size "s"
:class (stl/css :option-check)
:aria-hidden (when label true)}])]))
(mf/defc options-dropdown* (mf/defc options-dropdown*
{::mf/props :obj} {::mf/props :obj}

View file

@ -44,6 +44,7 @@
outline: $b-1 solid var(--options-dropdown-outline-color); outline: $b-1 solid var(--options-dropdown-outline-color);
outline-offset: -1px; outline-offset: -1px;
background-color: var(--options-dropdown-bg-color); background-color: var(--options-dropdown-bg-color);
color: var(--options-dropdown-fg-color);
&:hover, &:hover,
&[aria-selected="true"] { &[aria-selected="true"] {
@ -72,3 +73,8 @@
--options-dropdown-outline-color: var(--color-accent-primary); --options-dropdown-outline-color: var(--color-accent-primary);
outline: $b-1 solid var(--options-dropdown-outline-color); 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);
}