Skip to content

Commit c54fdba

Browse files
committed
feat: compute treeitem aria-level
1 parent 1fc17be commit c54fdba

File tree

3 files changed

+80
-9
lines changed

3 files changed

+80
-9
lines changed

src/__tests__/role-helpers.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
logRoles,
44
getImplicitAriaRoles,
55
isInaccessible,
6+
computeAriaLevel,
67
} from '../role-helpers'
78
import {render} from './helpers/test-utils'
89

@@ -72,6 +73,28 @@ function setup() {
7273
<dd data-testid="a-dd">Definition</dd>
7374
</dl>
7475
</section>
76+
77+
<ul role="tree" data-testid="a-tree">
78+
<li role="treeitem" aria-expanded="false" tabIndex="0">
79+
<span className="">Projects</span>
80+
<ul role="group">
81+
<li role="treeitem" className="doc" tabIndex="-1">
82+
project-1.docx
83+
</li>
84+
<li role="treeitem" className="doc" tabIndex="-1">
85+
project-2.docx
86+
</li>
87+
<li role="treeitem" aria-expanded="true" tabIndex="-1" data-testid="level2-treeitem">
88+
<span className="">Project 3</span>
89+
<ul role="group">
90+
<li role="treeitem" className="doc" tabIndex="-1" data-testid="level3-treeitem">
91+
project-3A.docx
92+
</li>
93+
</ul>
94+
</li>
95+
</ul>
96+
</li>
97+
</ul>
7598
`)
7699

77100
return {
@@ -107,6 +130,9 @@ function setup() {
107130
dt: getByTestId('a-dt'),
108131
dd: getByTestId('a-dd'),
109132
header: getByTestId('a-header'),
133+
tree: getByTestId('a-tree'),
134+
treeItem2: getByTestId('level2-treeitem'),
135+
treeItem3: getByTestId('level3-treeitem'),
110136
}
111137
}
112138

@@ -200,3 +226,9 @@ test.each([
200226

201227
expect(isInaccessible(container.querySelector('button'))).toBe(expected)
202228
})
229+
230+
test('computeAriaLevel', () => {
231+
const {treeItem2, treeItem3} = setup()
232+
expect(computeAriaLevel(treeItem2)).toBe(2)
233+
expect(computeAriaLevel(treeItem3)).toBe(3)
234+
})

src/queries/role.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
computeAriaPressed,
77
computeAriaCurrent,
88
computeAriaExpanded,
9-
computeHeadingLevel,
9+
computeAriaLevel,
1010
getImplicitAriaRoles,
1111
prettyRoles,
1212
isInaccessible,
@@ -148,7 +148,7 @@ function queryAllByRole(
148148
return expanded === computeAriaExpanded(element)
149149
}
150150
if (level !== undefined) {
151-
return level === computeHeadingLevel(element)
151+
return level === computeAriaLevel(element)
152152
}
153153
// don't care if aria attributes are unspecified
154154
return true

src/role-helpers.js

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,50 @@ function checkBooleanAttribute(element, attribute) {
280280
return undefined
281281
}
282282

283+
/**
284+
* @param {Element} element -
285+
* @returns {number | undefined} - number if implicit heading or aria-level present, otherwise undefined
286+
*/
287+
function computeTreeItemLevel(element, level) {
288+
// https://www.w3.org/TR/wai-aria-1.1/#treeitem
289+
// https://www.w3.org/TR/wai-aria-1.1/#aria-level
290+
if (element.getAttribute('role') === 'tree') {
291+
return level
292+
}
293+
294+
if (element.getAttribute('role') === 'treeitem') {
295+
level += 1
296+
}
297+
298+
if (element.parentElement) {
299+
return computeTreeItemLevel(element.parentElement, level)
300+
}
301+
302+
return undefined
303+
}
304+
305+
/**
306+
* @param {Element} element -
307+
* @returns {number | undefined} - number if implicit aria-level can be inferred or aria-level present, otherwise undefined
308+
*/
309+
function computeAriaLevel(element) {
310+
// implicit aria-level
311+
if (element.getAttribute('role') === 'treeitem') {
312+
return computeTreeItemLevel(element, 0)
313+
}
314+
if (getImplicitAriaRoles(element).includes('heading')) {
315+
return computeHeadingLevel(element)
316+
}
317+
318+
// explicit aria-level value
319+
// https://www.w3.org/TR/wai-aria-1.2/#aria-level
320+
const ariaLevelAttribute =
321+
element.getAttribute('aria-level') &&
322+
Number(element.getAttribute('aria-level'))
323+
324+
return ariaLevelAttribute
325+
}
326+
283327
/**
284328
* @param {Element} element -
285329
* @returns {number | undefined} - number if implicit heading or aria-level present, otherwise undefined
@@ -295,13 +339,8 @@ function computeHeadingLevel(element) {
295339
H5: 5,
296340
H6: 6,
297341
}
298-
// explicit aria-level value
299-
// https://www.w3.org/TR/wai-aria-1.2/#aria-level
300-
const ariaLevelAttribute =
301-
element.getAttribute('aria-level') &&
302-
Number(element.getAttribute('aria-level'))
303342

304-
return ariaLevelAttribute || implicitHeadingLevels[element.tagName]
343+
return implicitHeadingLevels[element.tagName]
305344
}
306345

307346
export {
@@ -316,5 +355,5 @@ export {
316355
computeAriaPressed,
317356
computeAriaCurrent,
318357
computeAriaExpanded,
319-
computeHeadingLevel,
358+
computeAriaLevel,
320359
}

0 commit comments

Comments
 (0)