Automated React Accessibility Checks
Testing apps on accessibility is something that is often forgotten. This post covers a couple of tools that provide automated accessibility checks for React apps.
eslint-plugin-jsx-a11y
The first tool is an ESLint plugin called eslint-plugin-jsx-a11y
.
To install the accessibility plugin, run the following command in a terminal:
npm install eslint-plugin-jsx-a11y --save-dev
We tell ESLint to use the accessibility plugin with its recommended rules in the ESLint configuration file as follows:
{
...,
"plugins": [..., "jsx-a11y"],
"extends": [..., "plugin:jsx-a11y/recommended"],
...
}
If you use Visual Studio Code and the ESLint extension, accessibility issues will be highlighted as shown in the screen shot below:
In the example above, eslint-plugin-jsx-a11y
has picked up that a form field’s label
and input
elements aren’t associated. This means that a screen reader won’t read out the label when the input
gains focus.
The great thing about this ESLint plugin is that it catches accessibility issues as you write code. This means that it is cheap to resolve the detected accessibility issues. 😊
jest-axe
The second tool we will look at is a Jest plugin called jest-axe
. To install this with its TypeScript types, run the following command in a terminal:
npm install --save-dev jest-axe @types/jest-axe
We can use jest-axe
to implement a component test that checks accessibility.
In the test file, we start by importing the following from jest-axe
:
import { axe, toHaveNoViolations } from 'jest-axe';
axe
will inspect the component’s DOM structure, and toHaveNoViolations
is a Jest matcher which fails the test if axe
finds accessibility problems.
After the import, we also have to make Jest aware of the toHaveNoViolations
matcher:
expect.extend(toHaveNoViolations);
We can then write a component test with React Testing Library as follows:
test('Should not violate accessibility rules', async () => {
const { container } = render(<Form1 />);
expect(await axe(container)).toHaveNoViolations();});
Running the test catches the same accessibility issue as the ESLint plugin caught:
The nice thing about doing accessibility checks in a component test is that you can interact with the component to get it in a particular state before doing the checks. So, for example, accessibility can be checked after the form has been submitted:
test('Should not violate accessibility rules when invalid form submitted', async () => {
const { container } = render(<Form2 />);
userEvent.click(screen.getByText('Submit'));
await screen.findByText('You must enter your name');
expect(await axe(container)).toHaveNoViolations();
});
Nice. 😊
These won’t guarantee your app is fully accessible
Consider the following form:
export function Form2() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>();
const onSubmit = (data: FormData) => console.log(data);
return (
<form onSubmit={handleSubmit(onSubmit)}>
<label htmlFor="name">Name</label>
<input id="name" type="text" {...register('name', { required: true })} />
{errors.name && <div>You must enter your name</div>}
<button type="submit">Submit</button>
</form>
);
}
The field label
and input
elements are associated and the ESLint and Jest plugins don’t report any accessibility issues.
The accessibility of this form can be improved though. For example, a screen reader won’t let the user know whether the field is invalid or not and will not read out the validation error message. 😞
These tools are worth it though, and it does catch some issues. Also, it costs very little time to set both these tools up and use them.
Check out the next post to learn how to make the above form more accessible.
This example project is on my GitHub
Did you find this post useful?
Let me know by sharing it on Twitter.If you to learn more about testing React apps, you may find my course useful: