-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(CustomSelect): a11y allow NVDA to read selected option #7235
fix(CustomSelect): a11y allow NVDA to read selected option #7235
Conversation
size-limit report 📦
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
e2e tests
|
👀 Docs deployed
Commit 2608468 |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #7235 +/- ##
==========================================
+ Coverage 92.74% 92.76% +0.01%
==========================================
Files 374 375 +1
Lines 11103 11111 +8
Branches 3646 3643 -3
==========================================
+ Hits 10298 10307 +9
+ Misses 805 804 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
ec03446
to
006d11f
Compare
4b42895
to
479d29e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤯🤯🤯.... кропотливая работёнка 🧑🔬
спасибо 🙏
Вопрос
Ничего что в скриншотах placeholder
теперь ещё прозрачней?
Refactor SelectInput classes Always set input value for screen readers Reset inputValue in case selected option is not found Move extra functions and types to types and helpers files Check input value as well as it is important for screen readers
Add preselected value Fix test
Don't read first option when dropdown just opened and nothing is selected Turned out aria-own forced Firefox to read first option when no options selected on dropdown open. Removing it let us avoid this frustration
…input" This reverts commit 0e04f81.
Turned out section, section, section NVDA was reading because of aria-owns
Это как раз хорошо, у нас такая opacity по умолчанию в FormField. VKUI/packages/vkui/src/components/FormField/FormField.module.css Lines 140 to 141 in 146ebc6
И теперь такая же в CustomSelect. VKUI/packages/vkui/src/components/CustomSelect/CustomSelectInput.module.css Lines 84 to 85 in 0f3d72b
У нас как раз такая прозрачность в дизайне |
0f3d72b
to
2608468
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
VID_20240820172448.mp4Понимаю, что это к NVDA не относится, но, кажется, на андроиде (talk back), че т совсем неважно себя чувствуют селекты, они почему-то все зачитываются как "disabled", и активировать их никак нельзя 🤔 |
Да, дело в том, что |
Оказалось, что `NVDA` не зачитывает элементы расположенные рядом с `input`, если фокус есть на `input`, в отличии от `VoiceOver`. Есть несколько решений. 1. Отказаться от использования `input` как основного элемента `CustomSelect` c ролью `combobox`. Не хотелось бы от него отказываться, чтобы продолжал работать нативный фокус на `CustomSelect` при клике на связанный с ним `label`. > [!NOTE] > Но если проблемы продолжатся, и это будет причиной, то стоит пожертвовать нативным фокусом. 2. Переделать `input` так, чтобы он всегда хранил `label` выбранной опции, тогда `NVDA` сможет его зачитывать. Тут проблема в том, что` option.label `может быть не только строкой, но и react-компонентом, поэтому нельзя `option.label` выбранной в данный момент опции, просто так передать в `input`.\. https://github.com/VKCOM/VKUI/blob/1662eeae1a24e36f6f9f0c0331b16bd414d895cd/packages/vkui/src/components/CustomSelect/CustomSelect.tsx#L126 Про такой вариант даже немножка в доке про [combobox](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/combobox_role) на MDN сказано > If the combobox element is an [<input>](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) element, the value of the combobox is the input's value. Otherwise, the value of the combobox comes from its descendant elements. Изменения В качестве решения выбран второй вариант с передачей в `input.value` `option.label`. Для того, что мы могли передать туда и текстовое представление react-компонента используем утилитарную функцию [getTextFromChildren](https://github.com/VKCOM/VKUI/blob/1662eeae1a24e36f6f9f0c0331b16bd414d895cd/packages/vkui/src/lib/children.ts#L19) Чтобы минимизировать различия с дизайном, в том числе когда `option.label` это реакт-компонент, мы продолжаем input рендерить скрыто, с `opacity: 0`, а поверх рендерим контейнер для option.label, где спокойно может лежать react-компонент. В обычном режиме `CustomSelect` (не `searchable`) `input` не виден даже при фокусе. В режиме `searchable` мы продолжаем рендерить контейнер поверх `input`, но при фокусе на `CustomSelect` (а значит на `input`) мы `input` показываем, чтобы пользователь мог взаимодействовать с ним для ввода текста и поиска опций. Если в `CustomSelect` уже выбрана какая-то опция, при фокусе мы оставляем текст инпута на месте, чтобы пользователи скринридера могли прочитать выбранное в данный момент значение. - Отрефакторил классы `CustomSelectInput` чтобы было понятнее к чему они относятся. - Убрал из CustomSelect отдельный скрытый текст лэйбла, добавленный ранее для скринридеров, так как он никак не помогает `NVDA` и его теперь заменяет значение `value` у `CustomSelectInput`. - На `blur` мы устанавливаем значение` input.value` равным `option.label`, если есть выбранная опция, либо ''. - Также стараемся реагировать на изменения значения `select.value`, чтобы `input.value` всегда обновлялось в соответствии с текущей выбранной опцией. Это особенно актуально, когда `CustomSelect` `value` устанавливается снаружи. - Изменился способ передачи в `CustomSelectInput` значения `label` текущей выбранной опции, раньше label передавался как `children` и было не понятно что в этом свойстве хранится, пока не посмотришь на то, как `CustomSelectInput` используется внутри `CustomSelect`. Теперь`label` мы передаем в свойстве `selectedOptionLabel` - Убрал `aria-owns` из `combobox` так как аттрибут устарел и мешает `NVDA` в `Firefox` правильно зачитывать опции (c `aria-owns` вместо названия опции зачитывается "секция")
Описание
Оказалось, что
NVDA
не зачитывает элементы расположенные рядом сinput
, если фокус есть наinput
, в отличии отVoiceOver
.Есть несколько решений.
input
как основного элементаCustomSelect
c рольюcombobox
.Не хотелось бы от него отказываться, чтобы продолжал работать нативный фокус на
CustomSelect
при клике на связанный с нимlabel
.Note
Но если проблемы продолжатся, и это будет причиной, то стоит пожертвовать нативным фокусом.
input
так, чтобы он всегда хранилlabel
выбранной опции, тогдаNVDA
сможет его зачитывать.Тут проблема в том, что
option.label
может быть не только строкой, но и react-компонентом, поэтому нельзяoption.label
выбранной в данный момент опции, просто так передать вinput
..VKUI/packages/vkui/src/components/CustomSelect/CustomSelect.tsx
Line 126 in 1662eea
Про такой вариант даже немножка в доке про combobox на MDN сказано
Изменения
В качестве решения выбран второй вариант с передачей в
input.value
option.label
. Для того, что мы могли передать туда и текстовое представление react-компонента используем утилитарную функцию getTextFromChildrenЧтобы минимизировать различия с дизайном, в том числе когда
option.label
это реакт-компонент, мы продолжаем input рендерить скрыто, сopacity: 0
, а поверх рендерим контейнер для option.label, где спокойно может лежать react-компонент.В обычном режиме
CustomSelect
(неsearchable
)input
не виден даже при фокусе.В режиме
searchable
мы продолжаем рендерить контейнер поверхinput
, но при фокусе наCustomSelect
(а значит наinput
) мыinput
показываем, чтобы пользователь мог взаимодействовать с ним для ввода текста и поиска опций.Если в
CustomSelect
уже выбрана какая-то опция, при фокусе мы оставляем текст инпута на месте, чтобы пользователи скринридера могли прочитать выбранное в данный момент значение.CustomSelectInput
чтобы было понятнее к чему они относятся.NVDA
и его теперь заменяет значениеvalue
уCustomSelectInput
.blur
мы устанавливаем значениеinput.value
равнымoption.label
, если есть выбранная опция, либо ''.select.value
, чтобыinput.value
всегда обновлялось в соответствии с текущей выбранной опцией. Это особенно актуально, когдаCustomSelect
value
устанавливается снаружи.CustomSelectInput
значенияlabel
текущей выбранной опции, раньше label передавался какchildren
и было не понятно что в этом свойстве хранится, пока не посмотришь на то, какCustomSelectInput
используется внутриCustomSelect
. Теперьlabel
мы передаем в свойствеselectedOptionLabel
aria-owns
изcombobox
так как аттрибут устарел и мешаетNVDA
вFirefox
правильно зачитывать опции (caria-owns
вместо названия опции зачитывается "секция")Видео
Поведение в режиме я searchable.
При фокусе выделяется текст выбранной опции.
Screen.Recording.2024-08-09.at.13.49.57.mov