Skip to content
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

[EuiSuperSelect] Fix various focus and accessibility issues: The Sequel, Part Two #7650

Merged
merged 9 commits into from
Apr 5, 2024

Conversation

cee-chen
Copy link
Member

@cee-chen cee-chen commented Apr 4, 2024

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:

  • EuiSuperSelect now announces parent EuiFormRow labels, the same way an EuiSelect component would (thank you @cleydyr for pointing this out to us!)
  • EuiSuperSelect no longer strands keyboard focus on popover close 😬
  • EuiSuperSelect's popover can now also be opened on Space keypress (same as EuiSelect)
  • EuiSuperSelect's options can now be selected via Tab keypress (same as EuiSelect)
  • EuiSuperSelect no longer cycles through its options when pressing arrow up or arrow down at the top or bottom of the options list (same as EuiSelect)
  • EuiSuperSelect now 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 🤦)
  • Various regression tests have been added for all of the above.

QA

  • Go to https://eui.elastic.co/pr_7650/#/forms/super-select
  • Confirm that the first demo correctly announces the form row label alongside the currently selected option, the same way that a native <select> would
  • Confirm you can use Space to open the dropdown list and Tab to select an option
  • Confirm that your keyboard focus doesn't get stranded on dropdown close

General checklist

  • Browser QA
    • Checked in Chrome, Safari, Edge, and Firefox
    • Checked for accessibility including keyboard-only and screenreader modes
      - [ ] Checked in both light and dark modes
      - [ ] Checked in mobile
  • Docs site QA - N/A
  • Code quality checklist
  • Release checklist
    • A changelog entry exists and is marked appropriately.
      - [ ] If applicable, added the breaking change issue label (and filled out the breaking change checklist)
  • Designer checklist - N/A

…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
@cee-chen cee-chen requested a review from 1Copenut April 5, 2024 00:01
@cee-chen cee-chen marked this pull request as ready for review April 5, 2024 00:01
@cee-chen cee-chen requested a review from a team as a code owner April 5, 2024 00:01
Comment on lines +251 to -248
// 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;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @mgadewoll on this item.

@kibanamachine
Copy link

Preview staging links for this PR:

@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

cc @cee-chen

Copy link
Contributor

@mgadewoll mgadewoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚢 🐈‍⬛ Awesome changes to ensure a more accessible component! 🎉

Copy link
Contributor

@1Copenut 1Copenut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +251 to -248
// 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to @mgadewoll on this item.

Comment on lines +170 to +174
// Refocus back to the toggling control button on popover close
requestAnimationFrame(() => {
this.controlButtonRef.current?.focus();
});

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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}`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Member Author

@cee-chen cee-chen Apr 5, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's good enough reason for me. I'm much for consistency.

Copy link
Contributor

@1Copenut 1Copenut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I responded in thread to two questions, I have no issue approving as is.

@cee-chen
Copy link
Member Author

cee-chen commented Apr 5, 2024

Thanks y'all!!

@cee-chen cee-chen merged commit d08f49c into elastic:main Apr 5, 2024
8 checks passed
@cee-chen cee-chen deleted the super-select/a11y-fixes branch April 5, 2024 18:32
cee-chen added a commit to elastic/kibana that referenced this pull request Apr 9, 2024
`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))
szwarckonrad added a commit to elastic/kibana that referenced this pull request Apr 11, 2024
…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.

![Screenshot 2024-04-10 at 12 47
31](https://github.com/elastic/kibana/assets/29123534/b7ff5b0e-5792-4bab-ac3a-61ba622c2c07)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants