diff --git a/package.json b/package.json
index d87550cb4..a5a92a10d 100644
--- a/package.json
+++ b/package.json
@@ -110,6 +110,7 @@
"jest": "^24.0.0",
"jest-axe": "^5.0.1",
"mini-css-extract-plugin": "1.6.2",
+ "mutationobserver-shim": "^0.3.7",
"polished": "^4.0.3",
"postcss": "^8.4.5",
"postcss-loader": "^4.1.0",
diff --git a/scripts/testsPolyfill.js b/scripts/testsPolyfill.js
index b84a13532..383b29cee 100644
--- a/scripts/testsPolyfill.js
+++ b/scripts/testsPolyfill.js
@@ -1,3 +1,5 @@
+import 'mutationobserver-shim';
+
global.requestAnimationFrame = callback => {
setTimeout(callback, 0);
};
diff --git a/src/components/accordion/Accordion.a11y.spec.jsx b/src/components/accordion/Accordion.a11y.spec.jsx
index 82582643a..9b27e0b9a 100644
--- a/src/components/accordion/Accordion.a11y.spec.jsx
+++ b/src/components/accordion/Accordion.a11y.spec.jsx
@@ -1,25 +1,11 @@
import * as React from 'react';
-import {render, waitForElementToBeRemoved} from '@testing-library/react';
+import {render, waitFor, fireEvent} from '@testing-library/react';
import Accordion from './Accordion';
import AccordionItem from './AccordionItem';
import {testA11y} from '../../axe';
import userEvent from '@testing-library/user-event';
-describe('Accordion a11y', () => {
- it('renders accordion with expanded and collapsed items', async () => {
- const accordionIds = ['id-1', 'id-2'];
-
- await testA11y(
-
- {accordionIds.map(id => (
-
- Accordion Item Description
-
- ))}
-
- );
- });
-
+describe('Accordion', () => {
it('renders with named items', () => {
const title = 'Item_1';
const accordion = render(
@@ -40,7 +26,7 @@ describe('Accordion a11y', () => {
);
});
- it('expands and collapses item on Enter/Space keydown', async () => {
+ it('expands and collapses item on click', async () => {
const accordionId = 'id-1';
const accordion = render(
@@ -52,17 +38,64 @@ describe('Accordion a11y', () => {
const item = accordion.getByRole('button');
expect(item.getAttribute('aria-expanded')).toEqual('false');
- expect(accordion.queryByRole('region')).toBeFalsy();
+ expect(accordion.queryByRole('region')).toBeNull();
+ accordion.getByRole('button').click();
+
+ expect(item.getAttribute('aria-expanded')).toEqual('true');
+ await waitFor(() => expect(accordion.getByRole('region')).toBeTruthy());
+
+ accordion.getByRole('button').click();
+
+ expect(item.getAttribute('aria-expanded')).toEqual('false');
+ fireEvent(accordion.queryByRole('region'), new Event('transitionend'));
+ await waitFor(() => expect(accordion.queryByRole('region')).toBeNull());
+ });
+
+ it('expands and collapses item on Enter/Space keydown when motion is reduced', async () => {
+ const accordionId = 'id-1';
+ const accordion = render(
+
+
+ Accordion Item Description
+
+
+ );
+ const item = accordion.getByRole('button');
+
+ expect(item.getAttribute('aria-expanded')).toEqual('false');
+ expect(accordion.queryByRole('region')).toBeNull();
accordion.getByRole('button').focus();
expect(item).toEqual(document.activeElement);
- userEvent.keyboard('{enter}');
+ userEvent.keyboard('{enter}');
expect(item.getAttribute('aria-expanded')).toEqual('true');
expect(accordion.getByRole('region')).toBeTruthy();
userEvent.keyboard('{space}');
-
expect(item.getAttribute('aria-expanded')).toEqual('false');
- waitForElementToBeRemoved(accordion.queryByRole('region'));
+ expect(accordion.queryByRole('region')).toBeNull();
+ });
+
+ it('has an accessible name', () => {
+ const label = 'Accordion name';
+ const accordion = render();
+
+ expect(accordion.getByLabelText(label)).toBeTruthy();
+ });
+});
+
+describe('Accordion a11y', () => {
+ it('should have no a11y violations when renders Accordion with expanded and collapsed items', async () => {
+ const accordionIds = ['id-1', 'id-2'];
+
+ await testA11y(
+
+ {accordionIds.map(id => (
+
+ Accordion Item Description
+
+ ))}
+
+ );
});
});
diff --git a/src/components/accordion/Accordion.jsx b/src/components/accordion/Accordion.jsx
index a2aaf3bee..6049bc4dc 100644
--- a/src/components/accordion/Accordion.jsx
+++ b/src/components/accordion/Accordion.jsx
@@ -66,6 +66,8 @@ export type AccordionPropsType = $ReadOnly<{
expanded?: string | Array,
defaultExpanded?: string | Array,
onChange?: string => void,
+ 'aria-label'?: string,
+ 'aria-labelledby'?: string,
}>;
type ContextType = {
@@ -89,6 +91,8 @@ const Accordion = ({
defaultExpanded,
expanded,
onChange,
+ 'aria-label': ariaLabel,
+ 'aria-labelledby': ariaLabelledby,
}: AccordionPropsType) => {
const wrapperRef = useRef(null);
const isControlled = expanded !== undefined;
@@ -305,6 +309,8 @@ const Accordion = ({
),
className
)}
+ aria-label={ariaLabel}
+ aria-labelledby={ariaLabelledby}
>
{children}
diff --git a/src/components/accordion/stories/rules.a11y.js b/src/components/accordion/stories/rules.a11y.js
index f4c91920f..fd6505610 100644
--- a/src/components/accordion/stories/rules.a11y.js
+++ b/src/components/accordion/stories/rules.a11y.js
@@ -1,8 +1,10 @@
const rules = [
{
pattern: 'Can have an accessible name.',
- status: 'TO DO',
- tests: 'TO DO',
+ comment: `Can be named by a label specified by aria-label
prop or a value
+ (IDREF
) set for the aria-labelledby
prop that refers to an element.`,
+ status: 'DONE',
+ tests: 'DONE',
},
];
diff --git a/yarn.lock b/yarn.lock
index 14c7ad301..81c5d5370 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,11 @@
# yarn lockfile v1
+"@adobe/css-tools@^4.0.1":
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd"
+ integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==
+
"@ampproject/remapping@^2.0.0":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.0.2.tgz#f3d9760bf30588c51408dbe7c05ff2bb13069307"
@@ -4773,9 +4778,9 @@
resolve-from "^5.0.0"
"@testing-library/dom@^8.0.0", "@testing-library/dom@^8.11.1":
- version "8.11.1"
- resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.1.tgz#03fa2684aa09ade589b460db46b4c7be9fc69753"
- integrity sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg==
+ version "8.19.0"
+ resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.19.0.tgz#bd3f83c217ebac16694329e413d9ad5fdcfd785f"
+ integrity sha512-6YWYPPpxG3e/xOo6HIWwB/58HukkwIVTOaZ0VwdMVjhRUX/01E4FtQbck9GazOOj7MXHc5RBzMrU86iBJHbI+A==
dependencies:
"@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5"
@@ -4801,35 +4806,36 @@
pretty-format "^27.0.2"
"@testing-library/jest-dom@^5.16.4":
- version "5.16.4"
- resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.4.tgz#938302d7b8b483963a3ae821f1c0808f872245cd"
- integrity sha512-Gy+IoFutbMQcky0k+bqqumXZ1cTGswLsFqmNLzNdSKkU9KGV2u9oXhukCbbJ9/LRPKiqwxEE8VpV/+YZlfkPUA==
+ version "5.16.5"
+ resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e"
+ integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA==
dependencies:
+ "@adobe/css-tools" "^4.0.1"
"@babel/runtime" "^7.9.2"
"@types/testing-library__jest-dom" "^5.9.1"
aria-query "^5.0.0"
chalk "^3.0.0"
- css "^3.0.0"
css.escape "^1.5.1"
dom-accessibility-api "^0.5.6"
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react-hooks@^8.0.0":
- version "8.0.0"
- resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.0.tgz#7d0164bffce4647f506039de0a97f6fcbd20f4bf"
- integrity sha512-uZqcgtcUUtw7Z9N32W13qQhVAD+Xki2hxbTR461MKax8T6Jr8nsUvZB+vcBTkzY2nFvsUet434CsgF0ncW2yFw==
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
+ integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
dependencies:
"@babel/runtime" "^7.12.5"
react-error-boundary "^3.1.0"
"@testing-library/react@^12.1.2":
- version "12.1.2"
- resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.2.tgz#f1bc9a45943461fa2a598bb4597df1ae044cfc76"
- integrity sha512-ihQiEOklNyHIpo2Y8FREkyD1QAea054U0MVbwH1m8N9TxeFz+KoJ9LkqoKqJlzx2JDm56DVwaJ1r36JYxZM05g==
+ version "12.1.5"
+ resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b"
+ integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg==
dependencies:
"@babel/runtime" "^7.12.5"
"@testing-library/dom" "^8.0.0"
+ "@types/react-dom" "<18.0.0"
"@testing-library/user-event@^13.2.1", "@testing-library/user-event@^13.5.0":
version "13.5.0"
@@ -5079,6 +5085,13 @@
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+"@types/react-dom@<18.0.0":
+ version "17.0.18"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.18.tgz#8f7af38f5d9b42f79162eea7492e5a1caff70dc2"
+ integrity sha512-rLVtIfbwyur2iFKykP2w0pl/1unw26b5td16d5xMgp7/yjTHomkyxPYChFoCr/FtEX1lN9wY6lFj1qvKdS5kDw==
+ dependencies:
+ "@types/react" "^17"
+
"@types/react-dom@^16.2.0":
version "16.9.17"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.17.tgz#29100cbcc422d7b7dba7de24bb906de56680dd34"
@@ -5095,6 +5108,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^17":
+ version "17.0.52"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.52.tgz#10d8b907b5c563ac014a541f289ae8eaa9bf2e9b"
+ integrity sha512-vwk8QqVODi0VaZZpDXQCmEmiOuyjEFPY7Ttaw5vjM112LOq37yz1CDJGrRJwA1fYEq4Iitd5rnjd1yWAc/bT+A==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/scheduler@*":
version "0.16.2"
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
@@ -8258,15 +8280,6 @@ css@2.X, css@^2.2.1:
source-map-resolve "^0.5.2"
urix "^0.1.0"
-css@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/css/-/css-3.0.0.tgz#4447a4d58fdd03367c516ca9f64ae365cee4aa5d"
- integrity sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==
- dependencies:
- inherits "^2.0.4"
- source-map "^0.6.1"
- source-map-resolve "^0.6.0"
-
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@@ -14517,6 +14530,11 @@ mustache@^2.3.0:
resolved "https://registry.yarnpkg.com/mustache/-/mustache-2.3.2.tgz#a6d4d9c3f91d13359ab889a812954f9230a3d0c5"
integrity sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==
+mutationobserver-shim@^0.3.7:
+ version "0.3.7"
+ resolved "https://registry.yarnpkg.com/mutationobserver-shim/-/mutationobserver-shim-0.3.7.tgz#8bf633b0c0b0291a1107255ed32c13088a8c5bf3"
+ integrity sha512-oRIDTyZQU96nAiz2AQyngwx1e89iApl2hN5AOYwyxLUB47UYsU3Wv9lJWqH5y/QdiYkc5HQLi23ZNB3fELdHcQ==
+
mute-stdout@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331"
@@ -17909,14 +17927,6 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-resolve@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2"
- integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==
- dependencies:
- atob "^2.1.2"
- decode-uri-component "^0.2.0"
-
source-map-support@^0.4.15:
version "0.4.18"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"