Skip to content

Commit

Permalink
refactor(check): split no-focusable-content rule into three.
Browse files Browse the repository at this point in the history
That rule is now:
no-focusable-content-for-aria-text,
no-focusable-content-for-nested-interactive, and
no-focusable-content-for-frame

These three are all copy-and-pastes of each other, so far.
  • Loading branch information
dan-tripp committed Oct 7, 2021
1 parent 64aeda8 commit 11d8c97
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 19 deletions.
35 changes: 35 additions & 0 deletions lib/checks/keyboard/no-focusable-content-for-aria-text-evaluate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import isFocusable from '../../commons/dom/is-focusable';

function focusableDescendants(vNode) {
if (isFocusable(vNode)) {
return true;
}

if (!vNode.children) {
if (vNode.props.nodeType === 1) {
throw new Error('Cannot determine children');
}

return false;
}

return vNode.children.some(child => {
return focusableDescendants(child);
});
}

function noFocusableContentForAriaTextEvaluate(node, options, virtualNode) {
if (!virtualNode.children) {
return undefined;
}

try {
return !virtualNode.children.some(child => {
return focusableDescendants(child);
});
} catch (e) {
return undefined;
}
}

export default noFocusableContentForAriaTextEvaluate;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "no-focusable-content",
"evaluate": "no-focusable-content-evaluate",
"id": "no-focusable-content-for-aria-text",
"evaluate": "no-focusable-content-for-aria-text-evaluate",
"metadata": {
"impact": "serious",
"messages": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function focusableDescendants(vNode) {
});
}

function noFocusbleContentEvaluate(node, options, virtualNode) {
function noFocusableContentForFrameEvaluate(node, options, virtualNode) {
if (!virtualNode.children) {
return undefined;
}
Expand All @@ -32,4 +32,4 @@ function noFocusbleContentEvaluate(node, options, virtualNode) {
}
}

export default noFocusbleContentEvaluate;
export default noFocusableContentForFrameEvaluate;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "frame-focusable-content",
"evaluate": "no-focusable-content-evaluate",
"id": "no-focusable-content-for-frame",
"evaluate": "no-focusable-content-for-frame-evaluate",
"metadata": {
"impact": "serious",
"messages": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import isFocusable from '../../commons/dom/is-focusable';

function focusableDescendants(vNode) {
if (isFocusable(vNode)) {
return true;
}

if (!vNode.children) {
if (vNode.props.nodeType === 1) {
throw new Error('Cannot determine children');
}

return false;
}

return vNode.children.some(child => {
return focusableDescendants(child);
});
}

function noFocusableContentForNestedInteractiveEvaluate(node, options, virtualNode) {
if (!virtualNode.children) {
return undefined;
}

try {
return !virtualNode.children.some(child => {
return focusableDescendants(child);
});
} catch (e) {
return undefined;
}
}

export default noFocusableContentForNestedInteractiveEvaluate;
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"id": "no-focusable-content-for-nested-interactive",
"evaluate": "no-focusable-content-for-nested-interactive-evaluate",
"metadata": {
"impact": "serious",
"messages": {
"pass": "Element does not have focusable descendants",
"fail": "Element has focusable descendants",
"incomplete": "Could not determine if element has descendants"
}
}
}
8 changes: 6 additions & 2 deletions lib/core/base/metadata-function-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ import focusableModalOpenEvaluate from '../../checks/keyboard/focusable-modal-op
import focusableNoNameEvaluate from '../../checks/keyboard/focusable-no-name-evaluate';
import focusableNotTabbableEvaluate from '../../checks/keyboard/focusable-not-tabbable-evaluate';
import landmarkIsTopLevelEvaluate from '../../checks/keyboard/landmark-is-top-level-evaluate';
import noFocusableContentEvaluate from '../../checks/keyboard/no-focusable-content-evaluate';
import noFocusableContentForFrameEvaluate from '../../checks/keyboard/no-focusable-content-for-frame-evaluate';
import noFocusableContentForAriaTextEvaluate from '../../checks/keyboard/no-focusable-content-for-aria-text-evaluate';
import noFocusableContentForNestedInteractiveEvaluate from '../../checks/keyboard/no-focusable-content-for-nested-interactive-evaluate';
import tabindexEvaluate from '../../checks/keyboard/tabindex-evaluate';

// label
Expand Down Expand Up @@ -273,7 +275,9 @@ const metadataFunctionMap = {
'focusable-no-name-evaluate': focusableNoNameEvaluate,
'focusable-not-tabbable-evaluate': focusableNotTabbableEvaluate,
'landmark-is-top-level-evaluate': landmarkIsTopLevelEvaluate,
'no-focusable-content-evaluate': noFocusableContentEvaluate,
'no-focusable-content-for-frame-evaluate': noFocusableContentForFrameEvaluate,
'no-focusable-content-for-aria-text-evaluate': noFocusableContentForAriaTextEvaluate,
'no-focusable-content-for-nested-interactive-evaluate': noFocusableContentForNestedInteractiveEvaluate,
'tabindex-evaluate': tabindexEvaluate,

// label
Expand Down
2 changes: 1 addition & 1 deletion lib/rules/aria-text.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
"help": "\"role=text\" should have no focusable descendants"
},
"all": [],
"any": ["no-focusable-content"],
"any": ["no-focusable-content-for-aria-text"],
"none": []
}
2 changes: 1 addition & 1 deletion lib/rules/frame-focusable-content.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"help": "Frames with focusable content must not have tabindex=-1"
},
"all": [],
"any": ["frame-focusable-content"],
"any": ["no-focusable-content-for-frame"],
"none": []
}
2 changes: 1 addition & 1 deletion lib/rules/nested-interactive.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
"help": "Ensure interactive controls are not nested"
},
"all": [],
"any": ["no-focusable-content"],
"any": ["no-focusable-content-for-nested-interactive"],
"none": []
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
describe('no-focusable-content tests', function() {
describe('no-focusable-content-for-aria-text tests', function() {
var fixture = document.querySelector('#fixture');
var queryFixture = axe.testUtils.queryFixture;
var noFocusableContent = axe.testUtils.getCheckEvaluate(
'no-focusable-content'
var noFocusableContentForAriaText = axe.testUtils.getCheckEvaluate(
'no-focusable-content-for-aria-text'
);

afterEach(function() {
Expand All @@ -11,30 +11,31 @@ describe('no-focusable-content tests', function() {

it('should return true if element has no focusable content', function() {
var vNode = queryFixture('<button id="target"><span>Hello</span></button>');
assert.isTrue(noFocusableContent(null, null, vNode));
assert.isTrue(noFocusableContentForAriaText(null, null, vNode));
});

it('should return true if element is empty', function() {
var vNode = queryFixture('<button id="target"></button>');
assert.isTrue(noFocusableContent(null, null, vNode));
assert.isTrue(noFocusableContentForAriaText(null, null, vNode));
});

it('should return true if element only has text content', function() {
var vNode = queryFixture('<button id="target">Hello</button>');
assert.isTrue(noFocusableContent(null, null, vNode));
assert.isTrue(noFocusableContentForAriaText(null, null, vNode));
});

it('should return false if element has focusable content', function() {
var vNode = queryFixture(
'<button id="target"><span tabindex="0">Hello</span></button>'
);
assert.isFalse(noFocusableContent(null, null, vNode));
assert.isFalse(noFocusableContentForAriaText(null, null, vNode));
});

it('should return false if element has natively focusable content', function() {
var vNode = queryFixture(
'<button id="target"><a href="foo.html">Hello</a></button>'
);
assert.isFalse(noFocusableContent(null, null, vNode));
assert.isFalse(noFocusableContentForAriaText(null, null, vNode));
});

});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
describe('no-focusable-content-for-nested-interactive tests', function() {
var fixture = document.querySelector('#fixture');
var queryFixture = axe.testUtils.queryFixture;
var noFocusableContentForNestedInteractive = axe.testUtils.getCheckEvaluate(
'no-focusable-content-for-nested-interactive'
);

afterEach(function() {
fixture.innerHTML = '';
});

it('should return true if element has no focusable content', function() {
var vNode = queryFixture('<button id="target"><span>Hello</span></button>');
assert.isTrue(noFocusableContentForNestedInteractive(null, null, vNode));
});

it('should return true if element is empty', function() {
var vNode = queryFixture('<button id="target"></button>');
assert.isTrue(noFocusableContentForNestedInteractive(null, null, vNode));
});

it('should return true if element only has text content', function() {
var vNode = queryFixture('<button id="target">Hello</button>');
assert.isTrue(noFocusableContentForNestedInteractive(null, null, vNode));
});

it('should return false if element has focusable content', function() {
var vNode = queryFixture(
'<button id="target"><span tabindex="0">Hello</span></button>'
);
assert.isFalse(noFocusableContentForNestedInteractive(null, null, vNode));
});

it('should return false if element has natively focusable content', function() {
var vNode = queryFixture(
'<button id="target"><a href="foo.html">Hello</a></button>'
);
assert.isFalse(noFocusableContentForNestedInteractive(null, null, vNode));
});

});

0 comments on commit 11d8c97

Please sign in to comment.