diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 7453eee32d..47fcfb405e 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -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 = [], @@ -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; } @@ -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; } diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js index c522b9f4cf..de489077ae 100644 --- a/test/checks/aria/required-children.js +++ b/test/checks/aria/required-children.js @@ -66,13 +66,13 @@ describe('aria-required-children', function () { }); it('should detect multiple missing required children when all required', function () { - var params = checkSetup('
Nothing here.
Nothing here.
Nothing here.
Nothing here.
Nothing here.
'); + it('should pass a native "text" type input with role comboxbox when missing child is role textbox', function () { + var params = checkSetup('Nothing here.
'); + 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('Nothing here.
'); + 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('Nothing here.
'); + 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('Nothing here.
'); + 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('Nothing here.
'); + 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('Textbox