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

fix(aria-required-children): allow combobox to own a searchbox #1708

Merged
merged 10 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/checks/aria/required-children.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const requiredOwned = axe.commons.aria.requiredOwned;
const implicitNodes = axe.commons.aria.implicitNodes;
const subclassImplicitNodes = axe.commons.aria.subclassImplicitNodes;
const matchesSelector = axe.utils.matchesSelector;
const idrefs = axe.commons.dom.idrefs;
const reviewEmpty =
Expand All @@ -10,11 +11,15 @@ function owns(node, virtualTree, role, ariaOwned) {
return false;
}
var implicit = implicitNodes(role),
straker marked this conversation as resolved.
Show resolved Hide resolved
subclass = subclassImplicitNodes(role),
selector = ['[role="' + role + '"]'];

if (implicit) {
selector = selector.concat(implicit);
}
if (subclass) {
selector = selector.concat(subclass);
}

selector = selector.join(',');
return ariaOwned
Expand Down
1 change: 1 addition & 0 deletions lib/commons/aria/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -1732,6 +1732,7 @@ lookupTable.role = {
nameFrom: ['author'],
context: null,
implicit: ['input[type="search"]'],
superclassRole: 'textbox',
unsupported: false,
allowedElements: {
nodeName: 'input',
Expand Down
29 changes: 29 additions & 0 deletions lib/commons/aria/subclass-implicit-nodes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/* global aria */

/**
* Get a list of CSS selectors of subclass nodes that have an implicit role
* @method subclassImplicitNodes
* @memberof axe.commons.aria
* @instance
* @param {String} role The role to check
* @return {Mixed} Either an Array of CSS selectors or `null` if there are none
*/
aria.subclassImplicitNodes = function(role) {
straker marked this conversation as resolved.
Show resolved Hide resolved
const roles = aria.lookupTable.role;
let subclassRoles = [];

for (const roleName in roles) {
if (roles[roleName].superclassRole === role) {
const implicit = aria.implicitNodes(roleName);
if (implicit) {
subclassRoles = subclassRoles.concat(implicit);
}
}
}

if (subclassRoles.length > 0) {
return subclassRoles;
}

return null;
};
9 changes: 9 additions & 0 deletions test/checks/aria/required-children.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,15 @@ describe('aria-required-children', function() {
);
});

it('should pass role comboxbox when child is native "search" input type', function() {
var params = checkSetup(
'<div role="combobox" id="target"><input type="search"><p role="listbox">Textbox</p></div>'
);
assert.isTrue(
checks['aria-required-children'].evaluate.apply(checkContext, params)
);
});

describe('options', function() {
it('should return undefined instead of false when the role is in options.reviewEmpty', function() {
var params = checkSetup('<div role="grid" id="target"></div>');
Expand Down
60 changes: 60 additions & 0 deletions test/commons/aria/subclass-implicit-nodes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
describe('aria.subclassImplicitNodes', function() {
'use strict';

var orig = axe.commons.aria.lookupTable.role;
afterEach(function() {
axe.commons.aria.lookupTable.role = orig;
});

it('should return an array of implicit CSS selectors', function() {
axe.commons.aria.lookupTable.role = {
trains: {
superclassRole: 'planes',
implicit: ['[class=trains]']
}
};

var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes');
assert.deepEqual(subclassRoles, ['[class=trains]']);
});

it('should return CSS selectors for all subclass roles', function() {
axe.commons.aria.lookupTable.role = {
trains: {
superclassRole: 'planes',
implicit: ['[class=trains]']
},
automobiles: {
superclassRole: 'planes',
implicit: ['[class=automobiles]']
}
};

var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes');
assert.deepEqual(subclassRoles, ['[class=trains]', '[class=automobiles]']);
});

it("should not add roles that don't have implicit nodes", function() {
axe.commons.aria.lookupTable.role = {
trains: {
superclassRole: 'planes'
},
automobiles: {
superclassRole: 'planes',
implicit: ['[class=automobiles]']
}
};

var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes');
assert.deepEqual(subclassRoles, ['[class=automobiles]']);
});

it('should return null if the role does not have a subclass role', function() {
axe.commons.aria.lookupTable.role = {
planes: {}
};

var subclassRoles = axe.commons.aria.subclassImplicitNodes('planes');
assert.isNull(subclassRoles);
});
});