From 5f032f211ec68ee0d6303ab99cdddad35b0cfa3f Mon Sep 17 00:00:00 2001 From: Matt Isner Date: Wed, 4 Oct 2017 15:08:20 -0400 Subject: [PATCH 1/3] fix(required-children): add combobox > listbox exception No longer requires a collapsed combobox to own a child listbox https://github.com/dequelabs/axe-core/issues/548 --- lib/checks/aria/required-children.js | 22 +++++++++++++++------- test/checks/aria/required-children.js | 9 +++++++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 7453eee32d..3a28ba017a 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,21 @@ 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 input var textboxIndex = missing.indexOf('textbox'); - if (textboxIndex >= 0) { + if (textboxIndex >= 0 && node.tagName === 'INPUT' && node.type === 'text') { 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 +80,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..bb61667e0d 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.

'); + var params = checkSetup('

Nothing here.

'); 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('

Nothing here.

'); + var params = checkSetup('

Nothing here.

'); assert.isFalse(checks['aria-required-children'].evaluate.apply(checkContext, params)); assert.deepEqual(checkContext._data, ['textbox']); }); @@ -102,6 +102,11 @@ describe('aria-required-children', function () { 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

'); + assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params)); + }); + it('should pass one indirectly aria-owned child when one required', function () { var params = checkSetup('
Nothing here.
'); assert.isTrue(checks['aria-required-children'].evaluate.apply(checkContext, params)); From 7b0bfe524329d79922ca3c5b4ae0a0ec8046f5be Mon Sep 17 00:00:00 2001 From: Matt Isner Date: Fri, 6 Oct 2017 10:49:37 -0400 Subject: [PATCH 2/3] fix(required-children): add combobox type=search > textbox exception Allows role=textbox child to be missing from parent Closes https://github.com/dequelabs/axe-core/issues/549 --- lib/checks/aria/required-children.js | 2 +- test/checks/aria/required-children.js | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 3a28ba017a..3a932d7eb0 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -51,7 +51,7 @@ function missingRequiredChildren(node, childRoles, all, role) { // remove 'textbox' from missing roles if combobox is a native input var textboxIndex = missing.indexOf('textbox'); - if (textboxIndex >= 0 && node.tagName === 'INPUT' && node.type === 'text') { + if (textboxIndex >= 0 && node.tagName === 'INPUT' && ['text', 'search'].indexOf(node.type) >= 0) { missing.splice(textboxIndex, 1); } diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js index bb61667e0d..0d6f67c573 100644 --- a/test/checks/aria/required-children.js +++ b/test/checks/aria/required-children.js @@ -97,8 +97,13 @@ 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('

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)); }); From e80466b1050c2215b484f0a48729c78262244f87 Mon Sep 17 00:00:00 2001 From: Matt Isner Date: Tue, 10 Oct 2017 14:12:28 -0400 Subject: [PATCH 3/3] fix(required-children): add exception for combobox type=email|url|tel > textbox Allows role=textbox to be missing from Related to https://github.com/dequelabs/axe-core/issues/549 --- lib/checks/aria/required-children.js | 5 +++-- test/checks/aria/required-children.js | 17 ++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/checks/aria/required-children.js b/lib/checks/aria/required-children.js index 3a932d7eb0..47fcfb405e 100644 --- a/lib/checks/aria/required-children.js +++ b/lib/checks/aria/required-children.js @@ -49,9 +49,10 @@ function missingRequiredChildren(node, childRoles, all, role) { // combobox exceptions if (role === 'combobox') { - // remove 'textbox' from missing roles if combobox is a native input + // remove 'textbox' from missing roles if combobox is a native text-type input var textboxIndex = missing.indexOf('textbox'); - if (textboxIndex >= 0 && node.tagName === 'INPUT' && ['text', 'search'].indexOf(node.type) >= 0) { + var textTypeInputs = ['text', 'search', 'email', 'url', 'tel']; + if (textboxIndex >= 0 && node.tagName === 'INPUT' && textTypeInputs.includes(node.type)) { missing.splice(textboxIndex, 1); } diff --git a/test/checks/aria/required-children.js b/test/checks/aria/required-children.js index 0d6f67c573..de489077ae 100644 --- a/test/checks/aria/required-children.js +++ b/test/checks/aria/required-children.js @@ -103,7 +103,22 @@ describe('aria-required-children', function () { }); it('should pass a native "search" type input with role comboxbox when missing child is role textbox', function () { - var params = checkSetup('

Nothing here.

'); + 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)); });