Skip to content

Commit

Permalink
fix(required-children): add combobox > listbox exception (#559)
Browse files Browse the repository at this point in the history
* fix(required-children): add combobox > listbox exception

No longer requires a collapsed combobox to own a child listbox

#548

* fix(required-children): add combobox type=search > textbox exception

Allows role=textbox child to be missing from <input type=search role=combobox> parent

Closes #549

* fix(required-children): add exception for combobox type=email|url|tel > textbox

Allows role=textbox to be missing from <input type=email|url|tel role=combobox>

Related to #549
  • Loading branch information
isner authored and WilcoFiers committed Oct 18, 2017
1 parent 42b46d9 commit 8d0991f
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 11 deletions.
23 changes: 16 additions & 7 deletions lib/checks/aria/required-children.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ function ariaOwns(nodes, role) {
return false;
}

function missingRequiredChildren(node, childRoles, all) {
//jshint maxstatements: 19
function missingRequiredChildren(node, childRoles, all, role) {
//jshint maxstatements: 22, maxcomplexity: 13
var i,
l = childRoles.length,
missing = [],
Expand All @@ -46,13 +46,22 @@ function missingRequiredChildren(node, childRoles, all) {
}
}

// combobox > textbox exception:
// remove textbox from missing roles if node is a native input
if (node.tagName === 'INPUT' && node.type === 'text') {
// combobox exceptions
if (role === 'combobox') {

// remove 'textbox' from missing roles if combobox is a native text-type input
var textboxIndex = missing.indexOf('textbox');
if (textboxIndex >= 0) {
var textTypeInputs = ['text', 'search', 'email', 'url', 'tel'];
if (textboxIndex >= 0 && node.tagName === 'INPUT' && textTypeInputs.includes(node.type)) {
missing.splice(textboxIndex, 1);
}

// remove 'listbox' from missing roles if combobox is collapsed
var listboxIndex = missing.indexOf('listbox');
var expanded = node.getAttribute('aria-expanded');
if (listboxIndex >= 0 && (!expanded || expanded === 'false')) {
missing.splice(listboxIndex, 1);
}
}

if (missing.length) { return missing; }
Expand All @@ -72,7 +81,7 @@ if (!childRoles) {
childRoles = required.all;
}

var missing = missingRequiredChildren(node, childRoles, all);
var missing = missingRequiredChildren(node, childRoles, all, role);

if (!missing) { return true; }

Expand Down
33 changes: 29 additions & 4 deletions test/checks/aria/required-children.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ describe('aria-required-children', function () {
});

it('should detect multiple missing required children when all required', function () {
var params = checkSetup('<div role="combobox" id="target"><p>Nothing here.</p></div>');
var params = checkSetup('<div role="combobox" id="target" aria-expanded="true"><p>Nothing here.</p></div>');
assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['listbox', 'textbox']);
});

it('should detect single missing required child when all required', function () {
var params = checkSetup('<div role="combobox" id="target"><p role="listbox">Nothing here.</p></div>');
var params = checkSetup('<div role="combobox" id="target" aria-expanded="true"><p role="listbox">Nothing here.</p></div>');
assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params));
assert.deepEqual(checkContext._data, ['textbox']);
});
Expand All @@ -97,8 +97,33 @@ describe('aria-required-children', function () {
assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params));
});

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

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

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

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

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

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

Expand Down

0 comments on commit 8d0991f

Please sign in to comment.