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(nested-interactive/aria-text): allow "tabindex=-1" on elements with no role #3165

Merged
merged 43 commits into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
2467b3a
refactor(checks/navigation): improve `internal-link-present-evaluate`
dan-tripp Aug 20, 2021
bde2684
Merge branch 'dequelabs:develop' into develop
dan-tripp Sep 2, 2021
9f996bc
test commit 1
dan-tripp Sep 3, 2021
c011d2c
test commit 2
dan-tripp Sep 3, 2021
a386166
test commit 3
dan-tripp Sep 3, 2021
428e015
Merge branch 'dan-test-branch-1' into develop
dan-tripp Sep 3, 2021
31b19a3
Revert "Merge branch 'dan-test-branch-1' into develop"
dan-tripp Sep 3, 2021
ac32146
Revert "test commit 1"
dan-tripp Sep 3, 2021
6e389a1
Merge branch 'dequelabs:develop' into develop
dan-tripp Sep 10, 2021
739f035
Merge branch 'dequelabs:develop' into develop
dan-tripp Sep 15, 2021
30f0e01
fix(rule): allow "tabindex=-1" for rules "aria-text" and "nested-inte…
dan-tripp Sep 24, 2021
9caefd7
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 1, 2021
41fd8e2
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 3, 2021
7aeac06
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 8, 2021
c37be71
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 9, 2021
2896478
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 14, 2021
02cfb18
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 22, 2021
f2ae004
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 23, 2021
b687d45
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 27, 2021
0729846
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 28, 2021
b257208
Merge branch 'dequelabs:develop' into develop
dan-tripp Oct 31, 2021
6c5a5ed
Merge branch 'develop' of https://github.com/dequelabs/axe-core into …
dan-tripp Nov 27, 2021
0d92cf2
Merge pull request #4 from dan-tripp/dequelabs-develop-2
dan-tripp Nov 27, 2021
5baa656
Merge branch 'dequelabs:develop' into develop
dan-tripp Nov 27, 2021
40025ad
work in progress
dan-tripp Nov 28, 2021
f12379e
Merge pull request #5 from dan-tripp/dequelabs-develop-2
dan-tripp Nov 28, 2021
92dc637
work in progress
dan-tripp Nov 28, 2021
2666eeb
Merge pull request #6 from dan-tripp/dequelabs-develop-2
dan-tripp Nov 28, 2021
7e692b4
Merge branch 'dequelabs:develop' into develop
dan-tripp Nov 30, 2021
6d8a0f6
test commit 1
dan-tripp Sep 3, 2021
9373c58
Revert "test commit 1"
dan-tripp Sep 3, 2021
0a4fd4f
fix(rule): allow "tabindex=-1" for rules "aria-text" and "nested-inte…
dan-tripp Sep 24, 2021
d50b56a
work in progress
dan-tripp Nov 28, 2021
74d5656
work in progress
dan-tripp Nov 28, 2021
54329dd
fix whitespace
dan-tripp Nov 30, 2021
499768f
add new case to test test/checks/keyboard/no-focusable-content.js
dan-tripp Nov 30, 2021
8c32f03
change "disabled" test case in test/checks/keyboard/no-focusable-cont…
dan-tripp Nov 30, 2021
ed9da3e
Merge branch 'develop' into dequelabs-develop-2
dan-tripp Dec 5, 2021
99000d4
Merge branch 'dequelabs-develop-2' into develop
dan-tripp Dec 5, 2021
7ad504f
Merge branch 'develop' of https://github.com/dan-tripp/axe-core into …
dan-tripp Dec 5, 2021
e69a0f5
Merge branch 'dequelabs:develop' into develop
dan-tripp Dec 5, 2021
d6a5573
fix merge problem
dan-tripp Dec 5, 2021
9a61752
Merge remote-tracking branch 'remotes/origin/develop' into develop
dan-tripp Dec 5, 2021
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
4 changes: 3 additions & 1 deletion lib/checks/keyboard/no-focusable-content-evaluate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import isFocusable from '../../commons/dom/is-focusable';
import { getRole, getRoleType } from '../../commons/aria';

export default function noFocusableContentEvaluate(node, options, virtualNode) {
if (!virtualNode.children) {
Expand Down Expand Up @@ -40,7 +41,8 @@ function getFocusableDescendants(vNode) {

const retVal = [];
vNode.children.forEach(child => {
if (isFocusable(child)) {
const role = getRole(child);
if(getRoleType(role) === 'widget' && isFocusable(child)) {
dan-tripp marked this conversation as resolved.
Show resolved Hide resolved
retVal.push(child);
} else {
retVal.push(...getFocusableDescendants(child));
Expand Down
48 changes: 41 additions & 7 deletions test/checks/keyboard/no-focusable-content.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,22 @@ describe('no-focusable-content tests', function() {
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return false if element has focusable content', function() {
it('should return true if element has content which is focusable (tabindex=0) and does not have a widget role', function() {
var params = checkSetup(
'<button id="target"><span tabindex="0">Hello</span></button>'
);

assert.isFalse(noFocusableContent.apply(checkContext, params));
assert.deepEqual(checkContext._data, null);
assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
assert.isTrue(noFocusableContent.apply(checkContext, params));
});

it('should return false if element has natively focusable content', function() {
it('should return true if element has content which has negative tabindex and non-widget role', function() {
dan-tripp marked this conversation as resolved.
Show resolved Hide resolved
var vNode = queryFixture(
'<button id="target"><span tabindex="-1">Hello</span></button>'
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return false if element has content which is natively focusable and has a widget role', function() {
var params = checkSetup(
'<button id="target"><a href="foo.html">Hello</a></button>'
);
Expand All @@ -50,7 +55,7 @@ describe('no-focusable-content tests', function() {

it('should add each focusable child as related nodes', function() {
var params = checkSetup(
'<button id="target"><span tabindex="0">Hello</span><a href="foo.html">Hello</a></button>'
'<button id="target"><input type="checkbox"><a href="foo.html">Hello</a></button>'
);

assert.isFalse(noFocusableContent.apply(checkContext, params));
Expand All @@ -61,7 +66,7 @@ describe('no-focusable-content tests', function() {
]);
});

it('should return false if element has natively focusable content with negative tabindex', function() {
it('should return false if element has natively focusable widget role content with negative tabindex', function() {
var params = checkSetup(
'<button id="target"><a href="foo.html" tabindex="-1">Hello</a></button>'
);
Expand All @@ -70,4 +75,33 @@ describe('no-focusable-content tests', function() {
assert.deepEqual(checkContext._data, { messageKey: 'notHidden' });
assert.deepEqual(checkContext._relatedNodes, [params[2].children[0]]);
});

it('should return true if element has content which is natively focusable and has a widget role but is disabled', function() {
var vNode = queryFixture(
'<button id="target"><a href="foo.html" disabled>Hello</a></button>'
dan-tripp marked this conversation as resolved.
Show resolved Hide resolved
);
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true on span with negative tabindex (focusable, does not have a widget role)', function() {
var vNode = queryFixture('<span id="target" role="text"> some text '
+'<span tabIndex="-1">JavaScript is able to focus this</span> '
+'</span>');
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true on aria-hidden span with negative tabindex (focusable, does not have a widget role)', function() {
var vNode = queryFixture('<span id="target" role="text"> some text '
+'<span tabIndex="-1" aria-hidden="true">JavaScript is able to focus this</span> '
+'</span>');
assert.isTrue(noFocusableContent(null, null, vNode));
});

it('should return true on nested span with tabindex=0 (focusable, does not have a widget role)', function() {
var vNode = queryFixture('<span id="target" role="text"> some text '
+'<span tabIndex="0">anyone is able to focus this</span> '
+'</span>');
assert.isTrue(noFocusableContent(null, null, vNode));
});

});
8 changes: 8 additions & 0 deletions test/integration/rules/aria-text/aria-text.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,11 @@ <h1>
explicit role.
</div>
<p role="text" id="fail4"><button>Hello</button></p>
<p role="text" id="fail5"><button tabindex="-1">Hello</button></p>
<p role="text" id="pass4"><span tabindex="-1">Hello</span></p>
<div role="text" id="pass5">
<a tabindex="-1">passes because no href makes this not have the implicit role of 'link'</a>
</div>
<div role="text" id="pass6">
<a>passes because no href makes this not have the implicit role of 'link'</a>
</div>
4 changes: 2 additions & 2 deletions test/integration/rules/aria-text/aria-text.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"description": "aria-text tests",
"rule": "aria-text",
"violations": [["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"]],
"passes": [["#pass1"], ["#pass2"], ["#pass3"]]
"violations": [["#fail1"], ["#fail2"], ["#fail3"], ["#fail4"], ["#fail5"]],
"passes": [["#pass1"], ["#pass2"], ["#pass3"], ["#pass4"], ["#pass5"], ["#pass6"]]
}
24 changes: 13 additions & 11 deletions test/integration/rules/nested-interactive/nested-interactive.html
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
<button id="pass1">pass</button>
<div role="button" id="pass2">pass</div>
<div role="tab" id="pass3">pass</div>
<div role="checkbox" id="pass4">pass</div>
<div role="radio" id="pass5"><span>pass</span></div>
<button id="pass2"><span tabindex="-1">pass</span></button>
<div role="button" id="pass3">pass</div>
<div role="tab" id="pass4">pass</div>
<div role="checkbox" id="pass5">pass</div>
<div role="radio" id="pass6"><span>pass</span></div>
<div role="radio" id="pass7"><span tabindex="-1">pass</span></div>

<button id="fail1"><span tabindex="0">fail</span></button>
<div role="button" id="fail2"><input /></div>
<div role="tab" id="fail3"><button id="pass6">fail</button></div>
<div role="checkbox" id="fail4"><a href="foo.html">fail</a></div>
<div role="radio" id="fail5"><span tabindex="0">fail</span></div>
<div role="radio" id="fail6"><button id="pass7" tabindex="-1">not really hidden</button></div>
<div role="radio" id="fail7"><button aria-hidden="true" tabindex="-1">not really hidden</button></div>
<button id="pass8"><span tabindex="0">pass</span></button>
<div role="button" id="fail1"><input /></div>
<div role="tab" id="fail2"><button id="pass9">div fails, button passes</button></div>
<div role="checkbox" id="fail3"><a href="foo.html">fail</a></div>
<div role="radio" id="pass10"><span tabindex="0">pass</span></div>
<div role="radio" id="fail4"><button id="pass11" tabindex="-1">not really hidden</button></div>
<div role="radio" id="fail5"><button aria-hidden="true" tabindex="-1">not really hidden</button></div>

<a id="ignored1" href="foo.html">ignored</a>
<span id="ignored2">ignored</span>
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
["#fail2"],
["#fail3"],
["#fail4"],
["#fail5"],
["#fail6"],
["#fail7"]
["#fail5"]
],
"passes": [
["#pass1"],
Expand All @@ -17,6 +15,10 @@
["#pass4"],
["#pass5"],
["#pass6"],
["#pass7"]
["#pass7"],
["#pass8"],
["#pass9"],
["#pass10"],
["#pass11"]
]
}
28 changes: 24 additions & 4 deletions test/integration/virtual-rules/nested-interactive.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,26 @@ describe('nested-interactive virtual-rule', function() {
assert.lengthOf(results.incomplete, 0);
});

it('should pass for element with non-widget content which has negative tabindex', 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',
Expand All @@ -54,7 +74,7 @@ describe('nested-interactive virtual-rule', function() {
assert.lengthOf(results.incomplete, 0);
});

it('should fail for element with focusable content', function() {
it('should pass for element with non-widget content', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'button'
});
Expand All @@ -69,12 +89,12 @@ describe('nested-interactive virtual-rule', function() {

var results = axe.runVirtualRule('nested-interactive', node);

assert.lengthOf(results.passes, 0);
assert.lengthOf(results.violations, 1);
assert.lengthOf(results.passes, 1);
assert.lengthOf(results.violations, 0);
assert.lengthOf(results.incomplete, 0);
});

it('should fail for element with natively focusable content', function() {
it('should fail for element with native widget content', function() {
var node = new axe.SerialVirtualNode({
nodeName: 'div',
attributes: {
Expand Down