Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5c98a44

Browse files
committed
Extract getRole into its own helper
1 parent 247eea2 commit 5c98a44

File tree

2 files changed

+96
-37
lines changed

2 files changed

+96
-37
lines changed

lib/rules/role-supports-aria-props.js

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @ts-check
22
const {aria, elementRoles, roles} = require('aria-query')
3-
const {getProp, getPropValue, propName} = require('jsx-ast-utils')
4-
const {getElementType} = require('../utils/get-element-type')
3+
const {getPropValue, propName} = require('jsx-ast-utils')
4+
const {getRole} = require('../utils/get-role')
55
const ObjectMap = require('../utils/object-map')
66

77
// Clean-up `elementRoles` from `aria-query`
@@ -36,42 +36,8 @@ module.exports = {
3636
create(context) {
3737
return {
3838
JSXOpeningElement(node) {
39-
// Assemble a key for looking-up the element’s role in the `elementRolesMap`
40-
// - Get the element’s name
41-
const key = {name: getElementType(context, node)}
42-
// - Get the element’s disambiguating attributes
43-
for (const prop of [
44-
'aria-label',
45-
'aria-labelledby',
46-
'alt',
47-
'type',
48-
'size',
49-
'role',
50-
'href',
51-
'multiple',
52-
'scope',
53-
'name',
54-
]) {
55-
if ((prop === 'aria-labelledby' || prop === 'aria-label') && !['section', 'aside', 'form'].includes(key.name))
56-
continue
57-
if (prop === 'name' && key.name !== 'form') continue
58-
59-
const value = getPropValue(getProp(node.attributes, prop))
60-
if (value) {
61-
if (!('attributes' in key)) {
62-
key.attributes = []
63-
}
64-
if (prop === 'href' || prop === 'aria-labelledby' || prop === 'aria-label') {
65-
key.attributes.push({name: prop, constraints: ['set']})
66-
} else if (prop === 'alt' && value !== '') {
67-
key.attributes.push({name: prop, constraints: ['set']})
68-
} else {
69-
key.attributes.push({name: prop, value})
70-
}
71-
}
72-
}
7339
// Get the element’s explicit or implicit role
74-
const role = getPropValue(getProp(node.attributes, 'role')) ?? elementRolesMap.get(key)?.[0]
40+
const role = getRole(context, node)
7541

7642
// Return early if role could not be determined
7743
if (!role) return

lib/utils/get-role.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
const {getProp, getPropValue} = require('jsx-ast-utils')
2+
const {elementRoles} = require('aria-query')
3+
const {getElementType} = require('./get-element-type')
4+
const ObjectMap = require('./object-map')
5+
6+
// Clean-up `elementRoles` from `aria-query`
7+
const elementRolesMap = new ObjectMap()
8+
for (const [key, value] of elementRoles.entries()) {
9+
// - Remove unused `constraints` key
10+
delete key.constraints
11+
// - Remove empty `attributes` key
12+
if (!key.attributes || key.attributes?.length === 0) {
13+
delete key.attributes
14+
}
15+
elementRolesMap.set(key, value)
16+
}
17+
// - Remove insufficiently-disambiguated `menuitem` entry
18+
elementRolesMap.delete({name: 'menuitem'})
19+
// - Disambiguate `menuitem` and `menu` roles by `type`
20+
elementRolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'command'}]}, ['menuitem'])
21+
elementRolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'radio'}]}, ['menuitemradio'])
22+
elementRolesMap.set({name: 'menuitem', attributes: [{name: 'type', value: 'toolbar'}]}, ['toolbar'])
23+
elementRolesMap.set({name: 'menu', attributes: [{name: 'type', value: 'toolbar'}]}, ['toolbar'])
24+
25+
/*
26+
Determine role of an element, based on its name and attributes.
27+
*/
28+
function getRole(context, node) {
29+
// Early return if role is explicitly set
30+
const explicitRole = getPropValue(getProp(node.attributes, 'role'))
31+
if (explicitRole) {
32+
return explicitRole
33+
}
34+
35+
// Assemble a key for looking-up the element’s role in the `elementRolesMap`
36+
// - Get the element’s name
37+
const key = {name: getElementType(context, node)}
38+
39+
for (const prop of [
40+
'aria-label',
41+
'aria-labelledby',
42+
'alt',
43+
'type',
44+
'size',
45+
'role',
46+
'href',
47+
'multiple',
48+
'scope',
49+
'name',
50+
]) {
51+
if ((prop === 'aria-labelledby' || prop === 'aria-label') && !['section', 'aside', 'form'].includes(key.name))
52+
continue
53+
if (prop === 'name' && key.name !== 'form') continue
54+
if (prop === 'href' && key.name !== 'a' && key.name !== 'area') continue
55+
if (prop === 'alt' && key.name !== 'img') continue
56+
57+
const propOnNode = getProp(node.attributes, prop)
58+
59+
if (!('attributes' in key)) {
60+
key.attributes = []
61+
}
62+
// Disambiguate "undefined" props
63+
if (propOnNode === undefined && prop === 'alt' && key.name === 'img') {
64+
key.attributes.push({name: prop, constraints: ['undefined']})
65+
continue
66+
}
67+
68+
const value = getPropValue(propOnNode)
69+
if (value || (value === '' && prop === 'alt')) {
70+
if (
71+
prop === 'href' ||
72+
prop === 'aria-labelledby' ||
73+
prop === 'aria-label' ||
74+
prop === 'name' ||
75+
(prop === 'alt' && value !== '')
76+
) {
77+
key.attributes.push({name: prop, constraints: ['set']})
78+
} else {
79+
key.attributes.push({name: prop, value})
80+
}
81+
}
82+
}
83+
84+
// - Remove empty `attributes` key
85+
if (!key.attributes || key.attributes?.length === 0) {
86+
delete key.attributes
87+
}
88+
89+
// Get the element’s implicit role
90+
return elementRolesMap.get(key)?.[0]
91+
}
92+
93+
module.exports = {getRole}

0 commit comments

Comments
 (0)