Skip to content

Commit

Permalink
fix(loading): adds label and aria so loading state is read by screenr…
Browse files Browse the repository at this point in the history
…eaders (#4457)

* fix(loading): adds label and aria so loading state is read

* test(loading): updates snapshots and adds tests for added aria

* docs(loading): add comments explaining aria and changed id name
  • Loading branch information
abbeyhrt committed Oct 28, 2019
1 parent ddba360 commit 74a7196
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 4 deletions.
4 changes: 2 additions & 2 deletions packages/components/src/globals/js/misc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

# `event-matches.js`

Event delegation - returns the closest ancestor of the event target (or the event
target itself) which matches the selectors given in parameter.
Event delegation - returns the closest ancestor of the event target (or the
event target itself) which matches the selectors given in parameter.

# `get-launching-details.js`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ exports[`Filename renders as expected renders upload status icon as expected 1`]
withOverlay={false}
>
<div
aria-label="Upload complete"
aria-atomic="true"
aria-labelledby="loading-id-1"
aria-live="assertive"
className="bx--loading bx--loading--small"
>
<label
className="bx--visually-hidden"
id="loading-id-1"
>
Upload complete
</label>
<svg
className="bx--loading__svg"
viewBox="-75 -75 150 150"
Expand Down
22 changes: 22 additions & 0 deletions packages/react/src/components/Loading/Loading-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,27 @@ describe('Loading', () => {
const overlay = wrapper.find(`.${prefix}--loading-overlay`);
expect(overlay.length).toEqual(0);
});

it('should be an assertive live region when active', () => {
const wrapper = mount(<Loading active={false} />);
const getLiveRegion = () => wrapper.find('[aria-live]');

expect(getLiveRegion().prop('aria-atomic')).toBe('true');
expect(getLiveRegion().prop('aria-live')).toBe('off');
expect(getLiveRegion().prop('aria-labelledby')).toBeDefined();

wrapper.setProps({ active: true });
expect(getLiveRegion().prop('aria-live')).toBe('assertive');

wrapper.setProps({ active: false });
expect(getLiveRegion().prop('aria-live')).toBe('off');
});

it('should have an associated label for the live region', () => {
const wrapper = mount(<Loading />);
const node = wrapper.find('[aria-live][aria-labelledby]');
const id = node.prop('aria-labelledby');
expect(wrapper.find(`#${id}`)).toBeDefined();
});
});
});
27 changes: 26 additions & 1 deletion packages/react/src/components/Loading/Loading.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ import PropTypes from 'prop-types';
import React from 'react';
import classNames from 'classnames';
import { settings } from 'carbon-components';
import setupGetInstanceId from '../../tools/setupGetInstanceId';

const { prefix } = settings;

const getInstanceId = setupGetInstanceId();

export default class Loading extends React.Component {
constructor(props) {
super(props);
this.instanceId = getInstanceId();
}

static propTypes = {
/**
* Specify whether you want the loading indicator to be spinning or not
Expand Down Expand Up @@ -66,14 +74,31 @@ export default class Loading extends React.Component {
[`${prefix}--loading-overlay--stop`]: !active,
});

const loadingId = `loading-id-${this.instanceId}`;
const spinnerRadius = small ? '26.8125' : '37.5';

/**
* Various screenreaders (JAWS, VoiceOver, NVDA...) interpret live regions differently
* and change their interpretations over time. The aria on the div and the label
* associated with the div are currently necessary for the loading state to be properly
* read by all screenreaders. [0]
*
* JAWS does not read the loading state unless aria-atomic is set to true and the visually
* hidden label is required for the loading state to be read in VoiceOver on iOS. Please
* do not remove without testing on these platforms.
*
* [0] https://developer.paciellogroup.com/blog/2014/03/screen-reader-support-aria-live-regions/
* */
const loading = (
<div
{...other}
aria-label={description}
aria-atomic="true"
aria-labelledby={loadingId}
aria-live={active ? 'assertive' : 'off'}
className={loadingClasses}>
<label id={loadingId} className={`${prefix}--visually-hidden`}>
{description}
</label>
<svg className={`${prefix}--loading__svg`} viewBox="-75 -75 150 150">
<title>{description}</title>
{small ? (
Expand Down

0 comments on commit 74a7196

Please sign in to comment.