[EuiSuperSelect] Fix various focus and accessibility issues: The Sequel, Part Two#7650
Conversation
…label - mimicking default `<select>` component
- requires disabling the default focus trap, since this component handles so much of its own focus manually
- open/close keyboard a11y (+ refocus class check) - tab to select, escape should not select - form row focus, which was fixed 2 ish years ago
…options` array - would previously stop / not skip past disabled options like native selects should
| // Note: this component purposely does not cycle arrow key navigation | ||
| // to match native <select> elements | ||
| if (direction === ShiftDirection.BACK) { | ||
| targetElementIndex = | ||
| currentIndex === 0 ? this.itemNodes.length - 1 : currentIndex - 1; | ||
| targetElementIndex = currentIndex - 1; | ||
| } else { | ||
| targetElementIndex = | ||
| currentIndex === this.itemNodes.length - 1 ? 0 : currentIndex + 1; |
There was a problem hiding this comment.
Open to feedback on this opinionated change - obviously our native <select> component doesn't support cycling/looping between the list of options, but our EuiComboBox and EuiSelectable components do.
I leaned towards removing the behavior so that EuiSuperSelect more closely matches a native <select> (but with JSX rendering) and also to reduce the needed logic within the component, but feel free to shout if you feel strongly otherwise!
There was a problem hiding this comment.
I think this is a reasonable change if we want it to be closer related to EuiSelect 👍
And in doubt we can still iterate on it in the future if the need arises for cycling the options.
|
Preview staging links for this PR:
|
💚 Build Succeeded
cc @cee-chen |
mgadewoll
left a comment
There was a problem hiding this comment.
🚢 🐈⬛ Awesome changes to ensure a more accessible component! 🎉
1Copenut
left a comment
There was a problem hiding this comment.
Summary
One change request for you @cee-chen.
In testing I noticed some unpredictability with Safari + VO on MacOS Ventura. This wasn't a regression from the prior production code, just a flaky handling of focus. VO acts like it's re-rendering and recycles itself. I did not experience this with any other pairing.
Screen reader test pairings
- MacOS + Safari + VO
- Win10 + Firefox + NVDA
- Win10 + Chrome + NVDA
- Win10 + Chrome + JAWS
- iOS + Safari + VO
| // Note: this component purposely does not cycle arrow key navigation | ||
| // to match native <select> elements | ||
| if (direction === ShiftDirection.BACK) { | ||
| targetElementIndex = | ||
| currentIndex === 0 ? this.itemNodes.length - 1 : currentIndex - 1; | ||
| targetElementIndex = currentIndex - 1; | ||
| } else { | ||
| targetElementIndex = | ||
| currentIndex === this.itemNodes.length - 1 ? 0 : currentIndex + 1; |
| // Refocus back to the toggling control button on popover close | ||
| requestAnimationFrame(() => { | ||
| this.controlButtonRef.current?.focus(); | ||
| }); | ||
|
|
There was a problem hiding this comment.
Safari + VO is flaky on this function. It acts like there's a full refresh happening, because the introductory message "Welcome to VoiceOver" is being announced. I have a feeling this might be another event handler confounding the focus behavior. I did not witness this behavior with NVDA, JAWS or iOS Safari + VoiceOver, just the desktop.
This doesn't introduce regression that I can say definitively, but we'll want to keep an ear for feedback from the community.
There was a problem hiding this comment.
That's super weird, I didn't experience that during my testing (not to be the "it works on my machine" developer 🙈). Just to clarify, you're OK with leaving this for now and keeping an eye on feedback to see if we need to fix it later?
There was a problem hiding this comment.
Yes I'm okay leaving as is, and listening for feedback. I had a thought my phantom issues could be Chrome being open in the background and causing just enough CPU load to adversely affect VO.
|
|
||
| const buttonId = hasFormLabel ? `${id}-button` : undefined; | ||
| const ariaLabelledBy = hasFormLabel | ||
| ? `${buttonId} ${formLabelId}` |
There was a problem hiding this comment.
Is it possible to reliably have the form label first, then the button label when we're building the aria-labelledby attribute? I think it makes more sense to hear "Status, Minor" because that feels like the key: value relationship order.
If it can be swapped, let's go for it.
There was a problem hiding this comment.
I kept it in this order to match how native <select>s work (at least for Mac+VO) - see https://eui.elastic.co/#/forms/form-controls#select.
Is the order different on Windows/JAWS/NVDA etc? If so I'm down to swap it since those are more used screen readers. What I'm going for is 1:1 screen reader experience with EuiSelect.
There was a problem hiding this comment.
That's good enough reason for me. I'm much for consistency.
1Copenut
left a comment
There was a problem hiding this comment.
I responded in thread to two questions, I have no issue approving as is.
|
Thanks y'all!! |
`v93.5.2` ⏩ `v93.6.0` --- ## [`v93.6.0`](https://github.com/elastic/eui/releases/v93.6.0) - Updated `EuiBreadcrumb` styles to improve visual distinction of clickable breadcrumbs ([#7615](elastic/eui#7615)) **Deprecations** - Deprecated `color` prop on `EuiBreadcrumb` ([#7615](elastic/eui#7615)) **Bug fixes** - Fixed `EuiComboBox` to correctly select full matches within groups via the `Enter` key ([#7658](elastic/eui#7658)) **Accessibility** - Updated `EuiHeaderBreadcrumb` styles to ensure min. required color contrast ([#7643](elastic/eui#7643)) - `EuiSuperSelect` now correctly reads out parent `EuiFormRow` labels to screen readers ([#7650](elastic/eui#7650)) - `EuiSuperSelect` now more closely mimics native `<select>` behavior in its keyboard behavior and navigation ([#7650](elastic/eui#7650)) - `EuiSuperSelect` no longer strands keyboard focus on close ([#7650](elastic/eui#7650)) - `EuiSuperSelect` now correctly allows keyboard navigating past disabled options in the middle of the options list ([#7650](elastic/eui#7650))
…test (#180457) closes #180424 The SuperSelect EUI component started showing an extra `<span/>` when it displayed the selected value ([change](https://github.com/elastic/eui/blob/d08f49cf1f0ee6a31301ade914a2d06143d4a3d4/src/components/form/super_select/super_select_control.tsx#L195-L201) from elastic/eui#7650) . This caused a problem in a test that checks the agent policy name strictly. I changed the test to see if the text `contains` the string instead of `has` it. I checked the flow and found it doesn't affect the data sent to the API. Also, the `euiScreenReaderOnly` property hides this extra span in the regular UI so it is not visible to users. 
Summary
The year was 2021, and a younger and much more foolish Cee thought they were squashing bugs and taking names in #5097.
But as it turns out, EuiSuperSelect is kind of an a11y tire fire and needed much more than that!
Fixes in this PR:
EuiSuperSelectnow announces parentEuiFormRowlabels, the same way anEuiSelectcomponent would (thank you @cleydyr for pointing this out to us!)EuiSuperSelectno longer strands keyboard focus on popover close 😬EuiSuperSelect's popover can now also be opened onSpacekeypress (same asEuiSelect)EuiSuperSelect's options can now be selected viaTabkeypress (same asEuiSelect)EuiSuperSelectno longer cycles through its options when pressing arrow up or arrow down at the top or bottom of the options list (same asEuiSelect)EuiSuperSelectnow correctly keyboard navigates up/down past disabled options in the middle of the options list (we didn't notice this bug in our docs examples previously because the disabled options were at the start or end of the options list 🤦)QA
<select>wouldSpaceto open the dropdown list andTabto select an optionGeneral checklist
- [ ] Checked in both light and dark modes- [ ] Checked in mobile- [ ] If applicable, added the breaking change issue label (and filled out the breaking change checklist)