-
Notifications
You must be signed in to change notification settings - Fork 742
/
get-element-unallowed-roles.js
113 lines (96 loc) · 2.73 KB
/
get-element-unallowed-roles.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
/* global aria */
// dpub roles which are subclassing roles that are implicit on some native
// HTML elements (img, link, etc.)
const dpubRoles = [
'doc-backlink',
'doc-biblioentry',
'doc-biblioref',
'doc-cover',
'doc-endnote',
'doc-glossref',
'doc-noteref'
];
/**
* Returns all roles applicable to element in a list
*
* @method getRoleSegments
* @private
* @param {Element} node
* @returns {Array} Roles list or empty list
*/
function getRoleSegments(node) {
let roles = [];
if (!node) {
return roles;
}
if (node.hasAttribute('role')) {
const nodeRoles = axe.utils.tokenList(
node.getAttribute('role').toLowerCase()
);
roles = roles.concat(nodeRoles);
}
if (node.hasAttributeNS('http://www.idpf.org/2007/ops', 'type')) {
const epubRoles = axe.utils
.tokenList(
node
.getAttributeNS('http://www.idpf.org/2007/ops', 'type')
.toLowerCase()
)
.map(role => `doc-${role}`);
roles = roles.concat(epubRoles);
}
// filter invalid roles
roles = roles.filter(role => axe.commons.aria.isValidRole(role));
return roles;
}
/**
* gets all unallowed roles for a given node
* @method getElementUnallowedRoles
* @param {Object} node HTMLElement to validate
* @param {String} tagName tag name of a node
* @param {String} allowImplicit option to allow implicit roles, defaults to true
* @return {Array<String>} retruns an array of roles that are not allowed on the given node
*/
aria.getElementUnallowedRoles = function getElementUnallowedRoles(
node,
allowImplicit = true
) {
const tagName = node.nodeName.toUpperCase();
// by pass custom elements
if (!axe.utils.isHtmlElement(node)) {
return [];
}
const roleSegments = getRoleSegments(node);
const implicitRole = axe.commons.aria.implicitRole(node);
// stores all roles that are not allowed for a specific element most often an element only has one explicit role
const unallowedRoles = roleSegments.filter(role => {
// if role and implicit role are same, when allowImplicit: true
// ignore as it is a redundant role
if (allowImplicit && role === implicitRole) {
return false;
}
// if role is a dpub role make sure it's used on an element with a valid
// implicit role fallback
if (allowImplicit && dpubRoles.includes(role)) {
const roleType = axe.commons.aria.getRoleType(role);
if (implicitRole !== roleType) {
return true;
}
}
// Edge case:
// setting implicit role row on tr element is allowed when child of table[role='grid']
if (
!allowImplicit &&
!(
role === 'row' &&
tagName === 'TR' &&
axe.utils.matchesSelector(node, 'table[role="grid"] > tr')
)
) {
return true;
}
// check if role is allowed on element
return !aria.isAriaRoleAllowedOnElement(node, role);
});
return unallowedRoles;
};