From 30f0e01506979bb92600289bd242ab01d25cb8ec Mon Sep 17 00:00:00 2001 From: Dan Tripp Date: Thu, 23 Sep 2021 21:12:17 -0400 Subject: [PATCH] fix(rule): allow "tabindex=-1" for rules "aria-text" and "nested-interactive" Closes issue #2934 --- .../keyboard/no-focusable-content-evaluate.js | 6 ++++- test/checks/keyboard/no-focusable-content.js | 22 +++++++++++++++++++ .../rules/aria-text/aria-text.html | 6 ++--- .../rules/aria-text/aria-text.json | 4 ++-- .../nested-interactive.html | 12 +++++----- .../nested-interactive.json | 4 +++- .../virtual-rules/nested-interactive.js | 20 +++++++++++++++++ 7 files changed, 62 insertions(+), 12 deletions(-) diff --git a/lib/checks/keyboard/no-focusable-content-evaluate.js b/lib/checks/keyboard/no-focusable-content-evaluate.js index e45bdfde9f..0f6842405f 100644 --- a/lib/checks/keyboard/no-focusable-content-evaluate.js +++ b/lib/checks/keyboard/no-focusable-content-evaluate.js @@ -1,7 +1,11 @@ import isFocusable from '../../commons/dom/is-focusable'; function focusableDescendants(vNode) { - if (isFocusable(vNode)) { + const isNodeFocusable = isFocusable(vNode); + let tabIndex = parseInt(vNode.attr('tabindex'), 10); + tabIndex = !isNaN(tabIndex) ? tabIndex : null; + + if(tabIndex ? isNodeFocusable && tabIndex >= 0 : isNodeFocusable) { return true; } diff --git a/test/checks/keyboard/no-focusable-content.js b/test/checks/keyboard/no-focusable-content.js index 1e1a3a8474..9959fda468 100644 --- a/test/checks/keyboard/no-focusable-content.js +++ b/test/checks/keyboard/no-focusable-content.js @@ -37,4 +37,26 @@ describe('no-focusable-content tests', function() { ); assert.isFalse(noFocusableContent(null, null, vNode)); }); + + it('should return true on span with tabindex=-1', function() { + var vNode = queryFixture(' some text ' + +'JavaScript is able to focus this ' + +''); + assert.isTrue(noFocusableContent(null, null, vNode)); + }); + + it('should return true on aria-hidden span with tabindex=-1', function() { + var vNode = queryFixture(' some text ' + +' ' + +''); + assert.isTrue(noFocusableContent(null, null, vNode)); + }); + + it('should return false on span with tabindex=0', function() { + var vNode = queryFixture(' some text ' + +'anyone is able to focus this ' + +''); + assert.isFalse(noFocusableContent(null, null, vNode)); + }); + }); diff --git a/test/integration/rules/aria-text/aria-text.html b/test/integration/rules/aria-text/aria-text.html index 28252c0bce..4059f1e53b 100644 --- a/test/integration/rules/aria-text/aria-text.html +++ b/test/integration/rules/aria-text/aria-text.html @@ -15,8 +15,8 @@

Flattened text because of the explicit role.
-
+
Flattened text because of the - explicit role. + explicit role. Considered non-focusable because of tabindex=-1...
-

+

diff --git a/test/integration/rules/aria-text/aria-text.json b/test/integration/rules/aria-text/aria-text.json index f6c3f1d2d9..3aa00ff23b 100644 --- a/test/integration/rules/aria-text/aria-text.json +++ b/test/integration/rules/aria-text/aria-text.json @@ -1,6 +1,6 @@ { "description": "aria-text tests", "rule": "aria-text", - "violations": [["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"]], - "passes": [["#pass1"], ["#pass2"], ["#pass3"]] + "violations": [["#fail1"], ["#fail2"], ["#fail3"]], + "passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"]] } diff --git a/test/integration/rules/nested-interactive/nested-interactive.html b/test/integration/rules/nested-interactive/nested-interactive.html index beb7fbdf37..0baf57f5bb 100644 --- a/test/integration/rules/nested-interactive/nested-interactive.html +++ b/test/integration/rules/nested-interactive/nested-interactive.html @@ -1,12 +1,14 @@ -
pass
- - - + +
pass
+ + + +
- + diff --git a/test/integration/rules/nested-interactive/nested-interactive.json b/test/integration/rules/nested-interactive/nested-interactive.json index 78d3b43176..9195a46ad1 100644 --- a/test/integration/rules/nested-interactive/nested-interactive.json +++ b/test/integration/rules/nested-interactive/nested-interactive.json @@ -8,6 +8,8 @@ ["#pass3"], ["#pass4"], ["#pass5"], - ["#pass6"] + ["#pass6"], + ["#pass7"], + ["#pass8"] ] } diff --git a/test/integration/virtual-rules/nested-interactive.js b/test/integration/virtual-rules/nested-interactive.js index ba963b99b5..cd364e2b22 100644 --- a/test/integration/virtual-rules/nested-interactive.js +++ b/test/integration/virtual-rules/nested-interactive.js @@ -38,6 +38,26 @@ describe('nested-interactive virtual-rule', function() { assert.lengthOf(results.incomplete, 0); }); + it('should pass for element with content with tabindex=-1', function() { + var node = new axe.SerialVirtualNode({ + nodeName: 'button' + }); + var child = new axe.SerialVirtualNode({ + nodeName: 'span', + attributes: { + tabindex: -1 + } + }); + child.children = []; + node.children = [child]; + + var results = axe.runVirtualRule('nested-interactive', node); + + assert.lengthOf(results.passes, 1); + assert.lengthOf(results.violations, 0); + assert.lengthOf(results.incomplete, 0); + }); + it('should pass for empty element without', function() { var node = new axe.SerialVirtualNode({ nodeName: 'div',