Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(CustomSelect): a11y allow NVDA to read selected option (#7235)
Оказалось, что `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` вместо названия опции зачитывается "секция")
- Loading branch information