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

Refactor SelectNext into [role=combobox] to improve a11y #1330

Merged
merged 1 commit into from
Oct 23, 2023

Conversation

acelaya
Copy link
Contributor

@acelaya acelaya commented Oct 23, 2023

Note
This PR is easier to review hidding whitespaces

This PR refactors the SelectNext so that the toggle button has [role="combobox"], as we have seen this is the best way to make sure screen readers announce its content and the label linked to it, if any.

Without this, we have seen weird behaviors in which a label linked to the button will "replace" the content of the button itself when announced by screen readers, instead of combining both.

Additionally, and in order to make sure SelectNext is always labelled, this PR adds aria-label and aria-labelledby as optional props.

This allows for the control to be labelled even in contexts where adding a <label /> is not possible or not desired.

In order to test these changes, all examples in the pattern library are now labelled, and there's a new section explaining the different ways to label SelectNext.

image

@robertknight
Copy link
Member

Without this, we have seen weird behaviors in which a label linked to the button will "replace" the content of the button itself when announced by screen readers, instead of combining both.

I don't think this is a "weird behavior" but rather a case where the expected behavior is not clearly defined.

A common native form control has several aspects to it which get read out when a screen reader describes it. In the order they are read out, these are:

  1. The current value (eg. HTMLInputElement.value)
  2. The label (from one of <label>, aria-labelledby, aria-label)
  3. The control state (enabled, disabled, pressed etc.)
  4. The control type and role

The issue with SelectNext before this PR is that it isn't using a form control role where there is a value property. Rather it is a button with associated listbox, and it seems that screen readers vary in whether they include the button's content in the label or just read the button's associated label.

When using a combobox however, there is now a clear separation between the value and the label, and both get read out in the expected order.

@acelaya
Copy link
Contributor Author

acelaya commented Oct 23, 2023

Without this, we have seen weird behaviors in which a label linked to the button will "replace" the content of the button itself when announced by screen readers, instead of combining both.

I don't think this is a "weird behavior" but rather a case where the expected behavior is not clearly defined.

My bad. Probably not the best choice of words, as my intention was actually to say it was not the "desired" behavior for the use cases in which we want to use SelectNext, rather than "weird".

Sorry for the confusion.

@acelaya
Copy link
Contributor Author

acelaya commented Oct 23, 2023

This is now ready to review, as I have built this branch and tested it in LMS.

With these changes we can go back to use aria-label in SelectNext, instead of a <label className="sr-only" />, and it properly announces it together with selected value, as it happens in main, with the only addition that it now also announces it's a combobox.

@acelaya acelaya marked this pull request as ready for review October 23, 2023 09:43
'w-full flex justify-between',
className={classnames(
'focus-visible-ring transition-colors whitespace-nowrap',
'w-full flex items-center justify-between gap-x-2 p-2',
Copy link
Contributor Author

@acelaya acelaya Oct 23, 2023

Choose a reason for hiding this comment

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

These classes were automatically applied by our Button component, but we have moved to the regular button because our own only allows to set aria-label via title.

Copy link
Member

@robertknight robertknight left a comment

Choose a reason for hiding this comment

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

The change to be a combobox is good. When looking at this I noticed that the SelectNext documentation structure needs refinement:

  • At the top the component is described as a presentational component. I don't think that's really accurate any more. It isn't just styling around a native <select>.
  • There is an example near the top that shows what it looks like, but there is no code view
  • Underneath that is a "Working with SelectNext" section that references SelectNext.Option but at this point of reading through the page, there has been no explanation or demonstration of what an Option is or what the code structure looks like.

@@ -228,6 +246,44 @@ export default function SelectNextPage() {
</Library.Demo>
</Library.Example>

<Library.Example title="Labelling SelectNext">
Copy link
Member

Choose a reason for hiding this comment

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

In US English, "labeling" as one "l" in the middle. See eg. https://www.w3.org/WAI/tutorials/forms/labels/.

@@ -149,6 +149,9 @@ export type SelectProps<T> = PresentationalProps & {
*/
buttonId?: string;

'aria-label'?: string;
'aria-labelledby'?: string;
Copy link
Member

Choose a reason for hiding this comment

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

An issue I noticed when reading the docs, but that I don't think we should try to address in this PR:

The documentation creates some slight confusion here because at the top of the "Component API" section it links to http://localhost:4001/using-components#presentational-components-api, which states that HTML attributes are accepted. For this particular control, those attributes are not available however. I'm not sure that accepting HTML attributes as rest props makes sense because it might be unclear which element they are applied to (the container? the button? the listbox?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think this was a confusion on my part. SelectNextProps extends PresentationalProps, but that type does not mention anything about HTML attributes.

I have noticed though, that all other components extending PresentationalProps also extend some form of JSX.HTMLAttributes<...>, which is not the case for SelectNext.

So I don't know if PresentationalProps should actually implicitly extend JSX.HTMLAttributes and we should have some other type with a different name for what is currently PresentationalProps.

@acelaya
Copy link
Contributor Author

acelaya commented Oct 23, 2023

I have created an issue to address the needed documentation improvements #1331

@codecov
Copy link

codecov bot commented Oct 23, 2023

Codecov Report

Merging #1330 (eaeaf4d) into main (795843c) will not change coverage.
The diff coverage is n/a.

@@            Coverage Diff            @@
##              main     #1330   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           62        62           
  Lines          926       926           
  Branches       354       354           
=========================================
  Hits           926       926           
Files Coverage Δ
src/components/input/SelectNext.tsx 100.00% <ø> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@acelaya acelaya merged commit da4b94f into main Oct 23, 2023
4 checks passed
@acelaya acelaya deleted the select-next-combobox branch October 23, 2023 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants