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

🚀 feat(Input): add a11y attributes to Input #634

Merged
merged 4 commits into from
Mar 28, 2023

Conversation

marcos-creuz
Copy link
Contributor

JIRA Issue

Description 📄

This PR adds some accessibility improvements to the Input component, focusing mainly in improving the usability with screen readers:

  • Adds two new props to the Input component: a11yId and includeAriaAttributes (more details about these below);
  • Adds aria-labelledby to the input element, so that screen readers can announce the label of the input. If no a11yId is provided, aria-label is added instead (aria-labelledby is preferable when there is a visible text that labels an element according to MDN);
  • Adds aria-describedby to the input element, referencing the helper as it's description element. This allows the screen reader to announce helper and error messages associated with the input when the user focuses the input (works only if the a11yId prop is provided).
  • Adds aria-live="polite" to the Helper component, so that error messages can be announced by the screen reader as soon as they appear;
  • Adds aria-invalid to the input element, setting it's value to true when the error prop has a truthy value. This allows the screen reader to announce that the input has invalid data.

Generating HTML IDs for a11y

In order to use aria-labelledby and aria-describedby on the input element, HTML IDs had to be added to the label and helper elements. Some approaches were considered in order to do this (thanks @caiotracera for giving some ideas here):

  1. The optimal solution would be using React's useId hook, made specifically to solve this problem. Unfortunately, it is only available on React 18 (Yoga is currently on React 17.0.2).
  2. Generate a UUID for the input, store it with useRef or useState, and use it as a prefix for the HTML IDs. The pro of this approach is that aria-labelledby and aria-describedby would automatically work on all inputs without the need of passing a prop for that. The con is that some inconsistencies might happen with server side rendering (I honestly don't have experience with SSR and would like to know your opinions on this).
  3. Manually pass an ID as a prop to the Input component, which seemed to be the safest approach to start with. Although this approach is not scalable, since devs need to manually pass an ID to each input that they want to have aria-labelledby and aria-describedby working, it at least makes it possible to do it somehow. Another downside of this approach is that making the ID prop (a11yId) optional made the code a bit hard to read due to, for example, conditional props passing.

More about this can be found in this SO question and in this React issue.

New includeAriaAttributes prop

Besides the new a11yId prop, this PR also adds another prop to the Input component: includeAriaAttributes. This prop has a true value by default and was added to give the possibility of including or not ARIA attributes on the Input, depending on the situation.

The need for this prop arose from components that extend the Input component and have their own ARIA implementations, like Dropdown and AutoComplete (by the way, the usability of these two components with screen readers also need to be revisited in the future, even though they have the ARIA attributes added by downshift).

Platforms 📲

  • Web
  • Mobile

Type of change 🔍

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested? 🧪

  • Open the Input page in the documentation app;

  • Start running a screen reader (on mac, press cmd+F5 to start VoiceOver);

  • Focus the input element:

    • it should announce something like "Find an activity, edit text" (the label is announced due to aria-label, since no a11yId has being passed yet);
  • Press the < > button in order to edit the code;

  • Edit the code adding error={value == "error" && "Error message"} to the list of props being passed to the Input component;

  • Focus the input and type the word "error":

    • it should announce "Error message" as soon as the input enters in error state (due to aria-live);
  • Focus any other element and then focus the input element again (still in the error state):

    • it should mention "invalid data" during the announcement (due to aria-invalid). Although, in this case it's not expected to have the error description announced, since there's no link between the helper and the input yet;
  • Edit the code once again, now adding a11yId="id" to the list of props being passed to the Input component;

  • Focus the input element:

    • it should now announce the content of the helper component too (i.e. the error message if the the input has errors or the helper text otherwise). That works now due to aria-describedby, that could be added because of the a11yId;
  • Edit the code one last time, adding now includeAriaAttributes={false} to the list of props being passed to the Input component (feel free to keep the other added props or remove them);

  • Focus the input element:

    • it should announce "edit text, blank". The label of the input is not expected to be announced due to the removal of aria-label/aria-labelledby.
  • Unit Test

  • Snapshot Test

Checklist: 🔍

  • My code follows the contribution guide of this project Contributing Guide
  • Layout matches design prototype: FIGMA
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have checked my code and corrected any misspellings

Screenshots 📸

The videos below do not have audio, but the announcements of VoiceOver are shown as text in the gray box.

Before:

Screen.Recording.2023-03-22.at.09.43.21.mov
  • The label of the input is not announced
  • The screen reader is not aware whether the input has invalid data or not
  • The helper text and the error description are not announced

After:

Screen.Recording.2023-03-22.at.09.22.28.mov
  • The label of the input is announced (even when no a11yId is passed)
  • The screen reader announces if the input has invalid data
  • The error description is announced at least once due to aria-live, or everytime the input is focused when an a11yId is provided

@marcos-creuz marcos-creuz marked this pull request as draft March 23, 2023 14:09
@marcos-creuz marcos-creuz marked this pull request as ready for review March 24, 2023 19:18
@matheushdsbr matheushdsbr merged commit 8976765 into master Mar 28, 2023
@matheushdsbr matheushdsbr deleted the feat/improve-input-a11y branch March 28, 2023 12:05
@matheushdsbr
Copy link
Collaborator

@all-contributors please add @marcos-creuz for code

@allcontributors
Copy link
Contributor

@matheushdsbr

I've put up a pull request to add @marcos-creuz! 🎉

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.

6 participants