diff --git a/src/__tests__/role-helpers.js b/src/__tests__/role-helpers.js index aadc1dc2..6624ed7a 100644 --- a/src/__tests__/role-helpers.js +++ b/src/__tests__/role-helpers.js @@ -3,6 +3,7 @@ import { logRoles, getImplicitAriaRoles, isInaccessible, + computeAriaLevel, } from '../role-helpers' import {render} from './helpers/test-utils' @@ -72,6 +73,28 @@ function setup() {
Definition
+ + `) return { @@ -107,6 +130,9 @@ function setup() { dt: getByTestId('a-dt'), dd: getByTestId('a-dd'), header: getByTestId('a-header'), + tree: getByTestId('a-tree'), + treeItem2: getByTestId('level2-treeitem'), + treeItem3: getByTestId('level3-treeitem'), } } @@ -200,3 +226,12 @@ test.each([ expect(isInaccessible(container.querySelector('button'))).toBe(expected) }) + +test('computeAriaLevel', () => { + const {treeItem2, treeItem3, h1, h2, h3} = setup() + expect(computeAriaLevel(treeItem2)).toBe(2) + expect(computeAriaLevel(treeItem3)).toBe(3) + expect(computeAriaLevel(h1)).toBe(1) + expect(computeAriaLevel(h2)).toBe(2) + expect(computeAriaLevel(h3)).toBe(3) +}) diff --git a/src/queries/role.js b/src/queries/role.js index 339647a6..ec113162 100644 --- a/src/queries/role.js +++ b/src/queries/role.js @@ -6,7 +6,7 @@ import { computeAriaPressed, computeAriaCurrent, computeAriaExpanded, - computeHeadingLevel, + computeAriaLevel, getImplicitAriaRoles, prettyRoles, isInaccessible, @@ -148,7 +148,7 @@ function queryAllByRole( return expanded === computeAriaExpanded(element) } if (level !== undefined) { - return level === computeHeadingLevel(element) + return level === computeAriaLevel(element) } // don't care if aria attributes are unspecified return true diff --git a/src/role-helpers.js b/src/role-helpers.js index 7de55932..80ad45ee 100644 --- a/src/role-helpers.js +++ b/src/role-helpers.js @@ -280,6 +280,50 @@ function checkBooleanAttribute(element, attribute) { return undefined } +/** + * @param {Element} element - + * @returns {number | undefined} - number if implicit heading or aria-level present, otherwise undefined + */ +function computeTreeItemLevel(element, level) { + // https://www.w3.org/TR/wai-aria-1.1/#treeitem + // https://www.w3.org/TR/wai-aria-1.1/#aria-level + if (element.getAttribute('role') === 'tree') { + return level + } + + if (element.getAttribute('role') === 'treeitem') { + level += 1 + } + + if (element.parentElement) { + return computeTreeItemLevel(element.parentElement, level) + } + + return undefined +} + +/** + * @param {Element} element - + * @returns {number | undefined} - number if implicit aria-level can be inferred or aria-level present, otherwise undefined + */ +function computeAriaLevel(element) { + // implicit aria-level + if (element.getAttribute('role') === 'treeitem') { + return computeTreeItemLevel(element, 0) + } + if (getImplicitAriaRoles(element).includes('heading')) { + return computeHeadingLevel(element) + } + + // explicit aria-level value + // https://www.w3.org/TR/wai-aria-1.2/#aria-level + const ariaLevelAttribute = + element.getAttribute('aria-level') && + Number(element.getAttribute('aria-level')) + + return ariaLevelAttribute +} + /** * @param {Element} element - * @returns {number | undefined} - number if implicit heading or aria-level present, otherwise undefined @@ -295,13 +339,8 @@ function computeHeadingLevel(element) { H5: 5, H6: 6, } - // explicit aria-level value - // https://www.w3.org/TR/wai-aria-1.2/#aria-level - const ariaLevelAttribute = - element.getAttribute('aria-level') && - Number(element.getAttribute('aria-level')) - return ariaLevelAttribute || implicitHeadingLevels[element.tagName] + return implicitHeadingLevels[element.tagName] } export { @@ -316,5 +355,5 @@ export { computeAriaPressed, computeAriaCurrent, computeAriaExpanded, - computeHeadingLevel, + computeAriaLevel, }