Intro

At Salesforce, we have a strong commitment to deliver apps that are usable by persons of all ability. Because of that, Salesforce follows the internationally recognized best practices in Section 508 of the Rehabilitation Act and the Web Content Accessibility Guidelines Version 2.0 (WCAG 2.0) level AA to the extent possible. You, as a developer, should follow the sample principle and make your content accessible to everyone, both when extending Salesforce products, and when building out of the platform. Accessibility is a big topic, and cannot be covered in a single blog post. However, in this post I’ll tell you the story of how we audited the accessibility of Sample Gallery apps and how we created automated DOM accessibility tests using sa11y, a set of open source libraries created by Salesforce.

Do Sample Gallery apps comply with WCAG 2.0?

Some weeks ago, my team was asked this question. At that point, we didn’t know to which level sample apps complied with accessibility standards. We knew that base components, as well as SLDS, try to follow accessibility standards as much as possible, and that we are using them heavily in our Sample Gallery apps. But, to which level the custom code that we had created complied with accessibility standards? To respond to this question, we decided to start a project: evaluate accessibility in Sample Apps!

Don’t know where to start?

There are many aspects of accessibility that need to be taken into account when designing, developing and testing apps. These include:

  • The use of semantic structure of the DOM, and the utilization of ARIA attributes (if needed) to allow assistive technologies to correctly identify the markup meaning.
  • The appropriate use of colors, with enough contrast and not relying on them as the only way to convey information.
  • Making sure the app is fully accessible using the keyboard alone (i.e. no mouse!) with a correct implementation of focus.
  • More!

Check the Lightning Design System guide accessibility section and this Trailhead trail, to learn about these accessibility best practices more in depth.

When thinking about evaluating if these practices are being followed by an existing application, you may not know where to start. Luckily there are some tools out there that can help you in testing some of the requirements in an automated way. This is the case with sa11y.

sa11y to the rescue!

sa11y is a set of JavaScript libraries, created by Salesforce, to help you write automated accessibility tests. sa11y helps you detect machine-knowable static DOM accessibility issues. It’s based on axe-core, an accessibility testing JavaScript engine. sa11y provides support for Jest, WebdriverIO, and generic JavaScript tests. It’s used internally by teams at Salesforce, and you can use it as well because it’s open source!

sa11y ensures your DOM follows a set of accessibility guidelines (WCAG) and best practices. This is a list of some of the issues that sa11y will help you with, and some of the issues you will have to manually test:

Note that sa11y can be configured using three different presets, that will make the checks less or more strict.

As you can see, implementing sa11y is a good starting point. However, this doesn’t exempt you from knowing and understanding accessibility best practices, as well as from manually testing with specific interfaces, like the keyboard or a mobile phone.

Fun fact: sa11y is pronounced as “sally”, however, it’s written as sa-11-y (salesforce + a11y). a11y stands for accessibility and it’s a numeronym! Make sure to not confuse it with sally, as that’s a different npm module.

Integrating @sa11y/jest module

With all this in mind, we decided to give our first step and implement sa11y accessibility tests for Sample Gallery apps. This would help us find out some accessibility issues that the apps may have had. LWC unit tests are written with Jest, so our first step was to integrate the @sa11y/jest module. These are the steps that we followed:

    Install @sa11y/jest as it’s a dev dependency running the next command on the command line positioned at the project folder:

npm install -D @sa11y/jest

This adds a new line into your package.json devDependencies:

"devDependencies": {
        "@sa11y/jest": "^0.1.4-beta.0",
        ...
    },

    Create a setup file that registers the toBeAccessible() sa11y matcher so that it can be used with expect().

jest-sa11y-setup.js

import { registerSa11yMatcher } from '@sa11y/jest';

registerSa11yMatcher();

    Include the newly created setup file into your generic jest config file.

jest.config.js

const { jestConfig } = require('@salesforce/sfdx-lwc-jest/config');

const setupFilesAfterEnv = jestConfig.setupFilesAfterEnv || [];
setupFilesAfterEnv.push('<rootDir>/jest-sa11y-setup.js');

module.exports = {
    ...jestConfig,
    setupFilesAfterEnv
};

Note that as sample apps use the @salesforce/sfdx-lwc-jest package, the config file has code specifically for it. However, for an out of the box sa11y setup, the setupFilesAfterEnv line is all that’s needed.

How to write good accessibility tests

Once done with the above, you can start creating Jest tests to see how accessibility guidelines are being enforced by the DOM that your components generate. This is fairly straightforward. To make sure an element doesn’t have any objective a11y defects (all the ones that we listed above!) you just call expect(element).toBeAccessible();.

Let’s say that we have the following component:

helloConditionalRendering.html

<template>
   <template if:true={areDetailsVisible}>
       <p>These are the details!</p>
   </template>
   <template if:true={areMoreDetailsVisible}>
       <p>These are more details!.</p>
   </template>
</template>

helloConditionalRendering.js

import { LightningElement, api } from 'lwc';

export default class HelloConditionalRendering extends LightningElement {
    @api areDetailsVisible;
    @api areMoreDetailsVisible;
}

To test the component with sa11y, you must think about the different DOM configurations that the component can have. In this case, there are two sections that contain different DOM elements. So, what we can do, is to write a single test that configures the two sections as open. This way, all the possible DOM elements that can be rendered in the component will be evaluated.

it('is accessible', () => {
    const element = createElement('c-hello-conditional-rendering', {
        is: HelloConditionalRendering
    });

    element.areDetailsVisible = true;
    element.areMoreDetailsVisible = true;
    document.body.appendChild(element);

    return Promise.resolve().then(() => expect(element).toBeAccessible());
});

Let’s take a look at a different example. Think about components in which DOM elements may have different states. For instance, imagine that you need to implement a custom expandable section using a button (don’t do this at home!).

helloConditionalRendering.html

<template>
  <button type="button" onclick={toggleDetails} style={buttonStyle}>
    Click Me!
  </button>
  <template if:true={areDetailsVisible}>
    <p>These are the details!</p>
  </template>
</template>

helloConditionalRendering.js

import { LightningElement } from "lwc";

export default class HelloConditionalRendering extends LightningElement {
  areDetailsVisible = false;

  get buttonStyle() {
    return `background-color:${this.areDetailsVisible ? "blue" : "red"}`;
  }

  toggleDetails() {
    this.areDetailsVisible = !this.areDetailsVisible;
    const button = this.template
      .querySelector("button")
      .setAttribute("aria-expanded", this.areDetailsVisible);
  }
}

In that case, you should test the two different states in which the button can be:

it('is accessible when details are not visible', () => {
    const element = createElement('c-hello-conditional-rendering', {
        is: HelloConditionalRendering
    });

    document.body.appendChild(element);

    return Promise.resolve().then(() => expect(element).toBeAccessible());
});
it('is accessible when details are visible', () => {
    const element = createElement('c-hello-conditional-rendering', {
        is: HelloConditionalRendering
    });

    document.body.appendChild(element);
    
    // Emulate button click
    const buttonEl = element.shadowRoot.querySelector('button');
    buttonEl.click();

    return Promise.resolve().then(() => expect(element).toBeAccessible());
});

Here you have some additional tips to bear in mind:

  • When running unit tests, base components are replaced by stubs that are part of the @salesforce/sfdx-lwc-jest package. So, for instance, if you’re using lightning-accordion and lightning-accordion-section, Jest tests really use a lightning-accordion stub and a lightning-accordion-section stub. If you pay attention to these stubs, you’ll see they just contain a <slot></slot>. This means that anything passed in via slots will render, and you don’t have to test an accordion with the different sections open, because indeed there’s no way to open a section in the stubs code, it’s a simple fake interface!
  • Follow the blackbox testing approach when possible. Test in each component whatever belongs to that component. For example, in the next component, the parent component is responsible for testing only the lines highlighted in blue. It’s responsibility of c-contact-tile to test its inner implementation.
<template>
    <div class="slds-var-m-around_medium">
        <c-contact-tile
            class="slds-show slds-is-relative"
            contact={contact}
        ></c-contact-tile>
    </div>
</template>

Issues found

After writing accessibility tests with sa11y for all our Sample Gallery apps, this is a summary of what we found:

As you can see in the table, we didn’t find many issues. This is due to the fact that sample apps make extensive use of base components (which remember, are stubs in our Jest tests), and use the least custom code as possible. At the same time, sample apps custom code follows SLDS guidelines and uses SLDS classes, so it’s not a surprise that we didn’t find lots of issues.

What we did next was to fix the issues found, adapting the custom code to follow accessibility best practices. Jest tests were already part of our CI jobs, so now, accessibility tests execute for every pull request, making sure that we’re not breaking the rules.

If you want to take a look at a concrete pull request to dig more into how we’ve implemented the tests and fixes, check out the E-bikes example.

Testing for accessibility

Accessibility is a huge topic, and it’s the responsibility of everyone in the software manufacturing chain. From UX designers, through developers, to QA engineers, everyone in the chain should be aware of accessibility best practices and understand how to apply them in their job.

There are many aspects that need to be tested to ensure apps follow accessibility best practices, and you can start by implementing sa11y, that works with Jest, WebdriverIO and can also be used with generic JavaScript tests. But remember that other kinds of accessibility testing as manual keyboard navigation testing cannot be forgotten, as well as the ideal of making sure it works with a screen reader like NVDA.

Have you implemented or plan to implement sa11y? Let us know by writing a tweet and mentioning @SalesforceDevs, @SalesforceUX or @SalesforceA11y!

Finally, remember by using LWC base components and following SLDS guidelines as much as possible, you’ll get an accessible foundation for free. For Salesforce, accessibility is a priority, and we work hard to ensure that we comply with accessibility best practices.

About the author

Alba Rivas works as a Lead Developer Evangelist at Salesforce. She focuses on Lightning Web Components and Lightning adoption strategy. You can follow her on Twitter @AlbaSFDC.

Please click here to read the original article as posted on Salesforce Developers Blog.

We source the web to bring you best Salesforce articles for our reader’s convenience. If you want to have this article removed, please follow guidelines at Digital Millennium Copyright Act (DMCA)..

LEAVE A REPLY

Please enter your comment!
Please enter your name here