Skip to content
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

Merged
merged 38 commits into from
Aug 16, 2018
Merged

feat(rule): aria-allowed-role #945

merged 38 commits into from
Aug 16, 2018

Conversation

jeeyyy
Copy link
Contributor

@jeeyyy jeeyyy commented Jun 7, 2018

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)

  • Follows the commit message policy, appropriate for next version
  • Has documentation updated, a DU ticket, or requires no documentation change
  • Includes new tests, or was unnecessary
  • Code is reviewed for security by: @WilcoFiers

PS. @WilcoFiers (please update the above)... I had to do this myself after your approval...

@jeeyyy jeeyyy changed the title feat: new rule aria-allowed-role feat(rule): aria-allowed-role Jun 7, 2018
@jeeyyy

This comment has been minimized.

@jeeyyy jeeyyy self-assigned this Jun 7, 2018
@jeeyyy jeeyyy added the rules Issue or false result from an axe-core rule label Jun 7, 2018
}
if (node.href) {
return axe.commons.aria.isAllowedSubsetRole('aWithHref', role);
} else {

This comment was marked as resolved.

if (node.alt) {
// any role except the roles used by empty alt values
return !hasAllowedEmptyAltRole;
} else {

This comment was marked as resolved.

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.

const hasImplicitListitemRole = node.matches('ol li, ul li');
if (hasImplicitListitemRole) {
return axe.commons.aria.isAllowedSubsetRole('li', role);
} else {

This comment was marked as resolved.

@@ -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 resolved.

* @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.

@stephenmathieson

This comment has been minimized.

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.

@jeeyyy

This comment has been minimized.

@jeeyyy jeeyyy requested a review from dylanb June 8, 2018 17:22
dylanb
dylanb previously requested changes Jun 8, 2018
Copy link
Contributor

@dylanb dylanb left a 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.

@@ -13,19 +13,33 @@ lookupTable.attributes = {
},
'aria-atomic': {
type: 'boolean',
values: ['true', 'false']
values: [

This comment was marked as resolved.

@@ -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.

@jeeyyy jeeyyy changed the title feat(rule): aria-allowed-role [WIP] feat(rule): aria-allowed-role Jun 20, 2018
@jeeyyy jeeyyy dismissed WilcoFiers’s stale review August 6, 2018 13:15

PR Updated. Review again.

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 => {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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]'
Copy link
Contributor

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.

Copy link
Contributor Author

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');
Copy link
Contributor

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 => {
Copy link
Contributor

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.

@dylanb
Copy link
Contributor

dylanb commented Aug 7, 2018

Does it make sense to start this pull request over with a single commit?

@jeeyyy
Copy link
Contributor Author

jeeyyy commented Aug 7, 2018

@dylanb - Probably worth taking into a new PR, or squash and merge. I will speak to @WilcoFiers to make a decision. Thanks.

@jeeyyy jeeyyy dismissed WilcoFiers’s stale review August 8, 2018 09:07

PR Updated. Review again.

@stephenmathieson
Copy link
Member

Does it make sense to start this pull request over with a single commit?

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.

@jeeyyy jeeyyy changed the title feat(rule): aria-allowed-role [WIP] feat(rule): aria-allowed-role Aug 9, 2018
@dylanb
Copy link
Contributor

dylanb commented Aug 10, 2018

I agree with @stephenmathieson : squash

@jeeyyy jeeyyy changed the title [WIP] feat(rule): aria-allowed-role feat(rule): aria-allowed-role Aug 12, 2018
@jeeyyy
Copy link
Contributor Author

jeeyyy commented Aug 12, 2018

@dylanb - yup, will be squashing it. Cheers.

@jeeyyy
Copy link
Contributor Author

jeeyyy commented Aug 12, 2018

@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(
Copy link
Contributor

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.

@jeeyyy jeeyyy mentioned this pull request Aug 15, 2018
4 tasks
@jeeyyy jeeyyy requested a review from a team as a code owner August 16, 2018 13:39
@jeeyyy
Copy link
Contributor Author

jeeyyy commented Aug 16, 2018

@WilcoFiers

  • Conflicts resolve. Review/ Approve.

const tagName = node.nodeName.toUpperCase();

// by pass custom elements
if (!axe.utils.isHtmlElement(node)) {
Copy link
Contributor

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]",
Copy link
Contributor

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 = [
Copy link
Contributor

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?

@jeeyyy jeeyyy merged commit c270a46 into develop Aug 16, 2018
@jeeyyy jeeyyy deleted the new-rule-aria-allowed-role branch August 16, 2018 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rules Issue or false result from an axe-core rule
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants