-
Notifications
You must be signed in to change notification settings - Fork 779
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(rule): aria-allowed-role #945
Conversation
This comment has been minimized.
This comment has been minimized.
lib/checks/aria/aria-allowed-role.js
Outdated
} | ||
if (node.href) { | ||
return axe.commons.aria.isAllowedSubsetRole('aWithHref', role); | ||
} else { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/checks/aria/aria-allowed-role.js
Outdated
if (node.alt) { | ||
// any role except the roles used by empty alt values | ||
return !hasAllowedEmptyAltRole; | ||
} else { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/checks/aria/aria-allowed-role.js
Outdated
case 'button': | ||
return axe.commons.aria.isAllowedSubsetRole('inputTypeButton', role); | ||
case 'checkbox': | ||
if (role === 'button' && node.hasAttribute('aria-pressed')) { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/checks/aria/aria-allowed-role.js
Outdated
const hasImplicitListitemRole = node.matches('ol li, ul li'); | ||
if (hasImplicitListitemRole) { | ||
return axe.commons.aria.isAllowedSubsetRole('li', role); | ||
} else { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/commons/aria/roles.js
Outdated
@@ -123,9 +122,8 @@ aria.implicitNodes = function (role) { | |||
* @memberof axe.commons.aria | |||
* @instance | |||
* @param {HTMLElement} node The node to test | |||
* @return {Mixed} Either the role or `null` if there is none | |||
* @return {Mixed} Either the role or `null` if there is none |
This comment was marked as outdated.
This comment was marked as outdated.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/commons/aria/roles.js
Outdated
* @memberof axe.commons.aria | ||
* @instance | ||
* @param {String} tagName tag name of a node | ||
* @param {String} role aria role to check |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment has been minimized.
This comment has been minimized.
lib/checks/aria/aria-allowed-role.js
Outdated
const implicitRole = axe.commons.aria.implicitRole(node); | ||
|
||
/** | ||
* Helper object with functions to computed is role allowed for a given tag |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bad API design for the functions aria.isAllowedRole
and aria.isAllowedSubsetRole
and the fact that much of the logic for which role is allowed is buried inside the check and not in the utility function.
I would like to see that logic taken out of the check and put into the utility function so that the utility function can be re-used atomically. I.e. the only public API should be aria.isRoleAllowed
and this function should embed all the logic to know when to call things like aria.isAllowedSubsetRole
- which should be renamed so its clear its an internal function.
lib/commons/aria/index.js
Outdated
@@ -13,19 +13,33 @@ lookupTable.attributes = { | |||
}, | |||
'aria-atomic': { | |||
type: 'boolean', | |||
values: ['true', 'false'] | |||
values: [ |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
lib/commons/aria/roles.js
Outdated
@@ -5,7 +5,7 @@ | |||
* @method isValidRole | |||
* @memberof axe.commons.aria | |||
* @instance | |||
* @param {String} role The role to check | |||
* @param {String} role The role to check |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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 | ||
return roleSegments.filter(role => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why aren't you returning booleans in this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have discussed this in the past, the result of this filter is an array of unallowed roles for a given element.
This array is returned to aria-allowed-role
check, which needs to populate the data for doT templating with an listing of roles for fail message.
Hence we need to return role, than a boolean.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry Jey. I meant inside the callback. You're doing return;
and return role;
instead of return false
/ return true
. It's nitpicky, I know. But returning undefined rather than false, and returning a string rather than true makes it look like this code is supposed to do something that it doesn't actually do.
|
||
// by pass other elements that may not be a known html element | ||
if ( | ||
document.createElement(tagName).toString() === '[object HTMLUnknownElement]' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unnecessary due to line 47. Please remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Custom components that are written to override generic html tags still do not change the type of tag. Tested here - https://jsfiddle.net/Jey/bprv8fqe/7/
Hence removing this check.
|
||
it('should pass when A has namespace as svg', function() { | ||
var node = document.createElement('a'); | ||
node.setAttribute('xmlns', 'http://www.w3.org/2000/svg'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't set the namespace of an element after its created. This test doesn't work. You need to do document.createElementNS('http://www.w3.org/2000/svg', 'a')
instead.
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 | ||
return roleSegments.filter(role => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry Jey. I meant inside the callback. You're doing return;
and return role;
instead of return false
/ return true
. It's nitpicky, I know. But returning undefined rather than false, and returning a string rather than true makes it look like this code is supposed to do something that it doesn't actually do.
Does it make sense to start this pull request over with a single commit? |
@dylanb - Probably worth taking into a new PR, or squash and merge. I will speak to @WilcoFiers to make a decision. Thanks. |
Since this PR only adds a single feature, I think it makes much more sense to just squash it rather than doing any "extra" work. |
I agree with @stephenmathieson : squash |
@dylanb - yup, will be squashing it. Cheers. |
@WilcoFiers - Please review again. Have made changes based on our previous chat. Have enhanced no role and any role as per https://www.w3.org/TR/html-aria/ and enhanced tests. @dylanb @marcysutton - reviews welcome |
* @param {Array<String|Object>} constraintsArray an array containing TAGNAME or an OBJECT abstraction with conditions and attributes | ||
* @return {Boolean} true/ false based on if node passes the constraints expected | ||
*/ | ||
aria.validateNodeAndAttributes = function validateNodeAndAttributes( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this one would probably be a little easier if instead of passing in an array of constraints, you had passed it one constraint. It simplifies the function quite a bit and it makes it more generic. I think something like a "nodeMatchesDefinition" where we can pass it different things could be useful in other places as well.
I don't think this needs to change now, but it's something to think about.
|
const tagName = node.nodeName.toUpperCase(); | ||
|
||
// by pass custom elements | ||
if (!axe.utils.isHtmlElement(node)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we shouldn't move this into the matcher instead; make these nodes inapplicable instead of passing them. Seems slightly more appropriate. Maybe just create a ticket for it?
{ | ||
"id": "aria-allowed-role", | ||
"excludeHidden": false, | ||
"selector": "[role]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We haven't disabled the dpub rule yet. Include or open a new issue for it.
|
||
it('ensure that the lookupTable.elementsAllowedNoRole is invoked', function() { | ||
var overrideInvoked = false; | ||
axe.commons.aria.lookupTable.elementsAllowedNoRole = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are you overriding the lookup table at all here? You could just pass in this array, no?
This rule is a taken over PR from the community contribution (PR: #623, @lemnis - thanks for the initial work).
The rule computes if a given role on a node is an allowed aria role.
Closes issue:
Reviewer checks
Required fields, to be filled out by PR reviewer(s)
PS. @WilcoFiers (please update the above)... I had to do this myself after your approval...