diff --git a/lib/checks/keyboard/focusable-disabled-evaluate.js b/lib/checks/keyboard/focusable-disabled-evaluate.js
index 5ac5955f14..d588071584 100644
--- a/lib/checks/keyboard/focusable-disabled-evaluate.js
+++ b/lib/checks/keyboard/focusable-disabled-evaluate.js
@@ -2,11 +2,11 @@ import { isModalOpen } from '../../commons/dom';
function focusableDisabledEvaluate(node, options, virtualNode) {
const elementsThatCanBeDisabled = [
- 'BUTTON',
- 'FIELDSET',
- 'INPUT',
- 'SELECT',
- 'TEXTAREA'
+ 'button',
+ 'fieldset',
+ 'input',
+ 'select',
+ 'textarea'
];
const tabbableElements = virtualNode.tabbableElements;
@@ -15,21 +15,28 @@ function focusableDisabledEvaluate(node, options, virtualNode) {
return true;
}
- const relatedNodes = tabbableElements.reduce((out, { actualNode: el }) => {
- const nodeName = el.nodeName.toUpperCase();
- // populate nodes that can be disabled
- if (elementsThatCanBeDisabled.includes(nodeName)) {
- out.push(el);
- }
- return out;
- }, []);
+ const relatedNodes = tabbableElements.filter(vNode => {
+ return elementsThatCanBeDisabled.includes(vNode.props.nodeName);
+ });
- this.relatedNodes(relatedNodes);
+ this.relatedNodes(relatedNodes.map(vNode => vNode.actualNode));
if (relatedNodes.length === 0 || isModalOpen()) {
return true;
}
- return relatedNodes.every(related => related.onfocus) ? undefined : false
+
+ return relatedNodes.every(vNode => {
+ const pointerEvents = vNode.getComputedStylePropertyValue('pointer-events');
+ const width = parseInt(vNode.getComputedStylePropertyValue('width'));
+ const height = parseInt(vNode.getComputedStylePropertyValue('height'));
+
+ return (
+ vNode.actualNode.onfocus ||
+ ((width === 0 || height === 0) && pointerEvents === 'none')
+ );
+ })
+ ? undefined
+ : false;
}
export default focusableDisabledEvaluate;
diff --git a/lib/checks/keyboard/focusable-not-tabbable-evaluate.js b/lib/checks/keyboard/focusable-not-tabbable-evaluate.js
index 8607d29bfe..b5a5d2965c 100644
--- a/lib/checks/keyboard/focusable-not-tabbable-evaluate.js
+++ b/lib/checks/keyboard/focusable-not-tabbable-evaluate.js
@@ -2,11 +2,11 @@ import { isModalOpen } from '../../commons/dom';
function focusableNotTabbableEvaluate(node, options, virtualNode) {
const elementsThatCanBeDisabled = [
- 'BUTTON',
- 'FIELDSET',
- 'INPUT',
- 'SELECT',
- 'TEXTAREA'
+ 'button',
+ 'fieldset',
+ 'input',
+ 'select',
+ 'textarea'
];
const tabbableElements = virtualNode.tabbableElements;
@@ -15,21 +15,28 @@ function focusableNotTabbableEvaluate(node, options, virtualNode) {
return true;
}
- const relatedNodes = tabbableElements.reduce((out, { actualNode: el }) => {
- const nodeName = el.nodeName.toUpperCase();
- // populate nodes that cannot be disabled
- if (!elementsThatCanBeDisabled.includes(nodeName)) {
- out.push(el);
- }
- return out;
- }, []);
+ const relatedNodes = tabbableElements.filter(vNode => {
+ return !elementsThatCanBeDisabled.includes(vNode.props.nodeName);
+ });
- this.relatedNodes(relatedNodes);
+ this.relatedNodes(relatedNodes.map(vNode => vNode.actualNode));
if (relatedNodes.length === 0 || isModalOpen()) {
return true;
}
- return relatedNodes.every(related => related.onfocus) ? undefined : false
+
+ return relatedNodes.every(vNode => {
+ const pointerEvents = vNode.getComputedStylePropertyValue('pointer-events');
+ const width = parseInt(vNode.getComputedStylePropertyValue('width'));
+ const height = parseInt(vNode.getComputedStylePropertyValue('height'));
+
+ return (
+ vNode.actualNode.onfocus ||
+ ((width === 0 || height === 0) && pointerEvents === 'none')
+ );
+ })
+ ? undefined
+ : false;
}
export default focusableNotTabbableEvaluate;
diff --git a/test/checks/keyboard/focusable-disabled.js b/test/checks/keyboard/focusable-disabled.js
index 96f9537dee..ffe1536f94 100644
--- a/test/checks/keyboard/focusable-disabled.js
+++ b/test/checks/keyboard/focusable-disabled.js
@@ -1,4 +1,4 @@
-describe('focusable-disabled', function() {
+describe('focusable-disabled', function () {
'use strict';
var check;
@@ -8,24 +8,24 @@ describe('focusable-disabled', function() {
var checkContext = axe.testUtils.MockCheckContext();
var checkSetup = axe.testUtils.checkSetup;
- before(function() {
+ before(function () {
check = checks['focusable-disabled'];
});
- afterEach(function() {
+ afterEach(function () {
fixture.innerHTML = '';
axe._tree = undefined;
axe._selectorData = undefined;
checkContext.reset();
});
- it('returns true when content not focusable by default (no tabbable elements)', function() {
+ it('returns true when content not focusable by default (no tabbable elements)', function () {
var params = checkSetup('
Some text
');
var actual = check.evaluate.apply(checkContext, params);
assert.isTrue(actual);
});
- it('returns true when content hidden through CSS (no tabbable elements)', function() {
+ it('returns true when content hidden through CSS (no tabbable elements)', function () {
var params = checkSetup(
''
);
@@ -33,7 +33,7 @@ describe('focusable-disabled', function() {
assert.isTrue(actual);
});
- it('returns true when content made unfocusable through disabled (no tabbable elements)', function() {
+ it('returns true when content made unfocusable through disabled (no tabbable elements)', function () {
var params = checkSetup(
' '
);
@@ -41,7 +41,7 @@ describe('focusable-disabled', function() {
assert.isTrue(actual);
});
- it('returns true when content made unfocusable through disabled fieldset', function() {
+ it('returns true when content made unfocusable through disabled fieldset', function () {
var params = checkSetup(
' '
);
@@ -51,7 +51,7 @@ describe('focusable-disabled', function() {
(shadowSupported ? it : xit)(
'returns false when content is in a disabled fieldset but in another shadow tree',
- function() {
+ function () {
var fieldset = document.createElement('fieldset');
fieldset.setAttribute('disabled', 'true');
fieldset.setAttribute('aria-hidden', 'true');
@@ -70,7 +70,7 @@ describe('focusable-disabled', function() {
}
);
- it('returns false when content is in the legend of a disabled fieldset', function() {
+ it('returns false when content is in the legend of a disabled fieldset', function () {
var params = checkSetup(
' '
);
@@ -78,7 +78,7 @@ describe('focusable-disabled', function() {
assert.isFalse(actual);
});
- it('returns false when content is in an aria-hidden but not disabled fieldset', function() {
+ it('returns false when content is in an aria-hidden but not disabled fieldset', function () {
var params = checkSetup(
' '
);
@@ -86,7 +86,7 @@ describe('focusable-disabled', function() {
assert.isFalse(actual);
});
- it('returns true when focusable off screen link (cannot be disabled)', function() {
+ it('returns true when focusable off screen link (cannot be disabled)', function () {
var params = checkSetup(
''
);
@@ -95,7 +95,7 @@ describe('focusable-disabled', function() {
assert.lengthOf(checkContext._relatedNodes, 0);
});
- it('returns false when focusable form field only disabled through ARIA', function() {
+ it('returns false when focusable form field only disabled through ARIA', function () {
var params = checkSetup(
'
'
);
@@ -108,7 +108,7 @@ describe('focusable-disabled', function() {
);
});
- it('returns false when focusable SELECT element that can be disabled', function() {
+ it('returns false when focusable SELECT element that can be disabled', function () {
var params = checkSetup(
'' +
'
Choose:' +
@@ -128,7 +128,7 @@ describe('focusable-disabled', function() {
);
});
- it('returns true when focusable AREA element (cannot be disabled)', function() {
+ it('returns true when focusable AREA element (cannot be disabled)', function () {
var params = checkSetup(
'' +
'' +
@@ -143,7 +143,7 @@ describe('focusable-disabled', function() {
(shadowSupported ? it : xit)(
'returns false when focusable content inside shadowDOM, that can be disabled',
- function() {
+ function () {
// Note:
// `testUtils.checkSetup` does not work for shadowDOM
// as `axe._tree` and `axe._selectorData` needs to be updated after shadowDOM construction
@@ -159,7 +159,7 @@ describe('focusable-disabled', function() {
}
);
- it('returns true when focusable target that cannot be disabled', function() {
+ it('returns true when focusable target that cannot be disabled', function () {
var params = checkSetup(
''
);
@@ -167,7 +167,7 @@ describe('focusable-disabled', function() {
assert.isTrue(actual);
});
- it('returns false when focusable target that can be disabled', function() {
+ it('returns false when focusable target that can be disabled', function () {
var params = checkSetup(
''
);
@@ -175,7 +175,7 @@ describe('focusable-disabled', function() {
assert.isFalse(actual);
});
- it('returns true if there is a focusable element and modal is open', function() {
+ it('returns true if there is a focusable element and modal is open', function () {
var params = checkSetup(
'' +
'
Some button ' +
@@ -194,19 +194,28 @@ describe('focusable-disabled', function() {
});
it('returns undefined when all focusable controls have onfocus events', function () {
- var params = checkSetup('
' +
- ' button ' +
- '
'
+ var params = checkSetup(
+ '
' +
+ ' button ' +
+ '
'
);
assert.isUndefined(check.evaluate.apply(checkContext, params));
});
it('returns false when some, but not all focusable controls have onfocus events', function () {
- var params = checkSetup('
' +
- ' button ' +
- ' button ' +
- '
'
+ var params = checkSetup(
+ '
' +
+ ' button ' +
+ ' button ' +
+ '
'
);
assert.isFalse(check.evaluate.apply(checkContext, params));
});
+
+ it('returns undefined when control has 0 width and height and pointer events: none (focus trap bumper)', () => {
+ var params = checkSetup(
+ '
'
+ );
+ assert.isUndefined(check.evaluate.apply(checkContext, params));
+ });
});
diff --git a/test/checks/keyboard/focusable-not-tabbable.js b/test/checks/keyboard/focusable-not-tabbable.js
index e8bcc89d68..60bd891d1e 100644
--- a/test/checks/keyboard/focusable-not-tabbable.js
+++ b/test/checks/keyboard/focusable-not-tabbable.js
@@ -1,4 +1,4 @@
-describe('focusable-not-tabbable', function() {
+describe('focusable-not-tabbable', function () {
'use strict';
var check;
@@ -8,18 +8,18 @@ describe('focusable-not-tabbable', function() {
var checkContext = axe.testUtils.MockCheckContext();
var checkSetup = axe.testUtils.checkSetup;
- before(function() {
+ before(function () {
check = checks['focusable-not-tabbable'];
});
- afterEach(function() {
+ afterEach(function () {
fixture.innerHTML = '';
axe._tree = undefined;
axe._selectorData = undefined;
checkContext.reset();
});
- it('returns true when BUTTON removed from tab order through tabindex', function() {
+ it('returns true when BUTTON removed from tab order through tabindex', function () {
var params = checkSetup(
'
' +
'
Some button ' +
@@ -29,7 +29,7 @@ describe('focusable-not-tabbable', function() {
assert.isTrue(actual);
});
- it('returns false when LINK is in tab order', function() {
+ it('returns false when LINK is in tab order', function () {
var params = checkSetup(
'
' +
'
ABC Site ' +
@@ -44,7 +44,7 @@ describe('focusable-not-tabbable', function() {
);
});
- it('returns true when focusable SUMMARY element, that cannot be disabled', function() {
+ it('returns true when focusable SUMMARY element, that cannot be disabled', function () {
var params = checkSetup(
'
Some button Some details
'
);
@@ -57,7 +57,7 @@ describe('focusable-not-tabbable', function() {
);
});
- it('returns true when TEXTAREA is in tab order, but 0 related nodes as TEXTAREA can be disabled', function() {
+ it('returns true when TEXTAREA is in tab order, but 0 related nodes as TEXTAREA can be disabled', function () {
var params = checkSetup(
'
' +
'
Enter your comments:' +
@@ -70,7 +70,7 @@ describe('focusable-not-tabbable', function() {
assert.isTrue(actual);
});
- it('returns false when focusable AREA element', function() {
+ it('returns false when focusable AREA element', function () {
var params = checkSetup(
'' +
'' +
@@ -83,7 +83,7 @@ describe('focusable-not-tabbable', function() {
assert.isFalse(actual);
});
- it('returns true when aria-hidden=false does not negate aria-hidden true', function() {
+ it('returns true when aria-hidden=false does not negate aria-hidden true', function () {
// Note: aria-hidden can't be reset once you've set it to true on an ancestor
var params = checkSetup(
''
@@ -94,7 +94,7 @@ describe('focusable-not-tabbable', function() {
(shadowSupported ? it : xit)(
'returns false when focusable text in shadowDOM',
- function() {
+ function () {
// Note:
// `testUtils.checkSetup` does not work for shadowDOM
// as `axe._tree` and `axe._selectorData` needs to be updated after shadowDOM construction
@@ -110,7 +110,7 @@ describe('focusable-not-tabbable', function() {
}
);
- it('returns false when focusable content through tabindex', function() {
+ it('returns false when focusable content through tabindex', function () {
var params = checkSetup(
'Some text
'
);
@@ -118,7 +118,7 @@ describe('focusable-not-tabbable', function() {
assert.isFalse(actual);
});
- it('returns false when focusable target that cannot be disabled', function() {
+ it('returns false when focusable target that cannot be disabled', function () {
var params = checkSetup(
''
);
@@ -126,7 +126,7 @@ describe('focusable-not-tabbable', function() {
assert.isFalse(actual);
});
- it('returns true when focusable target that can be disabled', function() {
+ it('returns true when focusable target that can be disabled', function () {
var params = checkSetup(
''
);
@@ -134,7 +134,7 @@ describe('focusable-not-tabbable', function() {
assert.isTrue(actual);
});
- it('returns true if there is a focusable element and modal is open', function() {
+ it('returns true if there is a focusable element and modal is open', function () {
var params = checkSetup(
'' +
'
foo ' +
@@ -153,20 +153,29 @@ describe('focusable-not-tabbable', function() {
});
it('returns undefined when all focusable controls have onfocus events', function () {
- var params = checkSetup('
'
+ var params = checkSetup(
+ '
'
);
assert.isUndefined(check.evaluate.apply(checkContext, params));
});
it('returns false when some, but not all focusable controls have onfocus events', function () {
- var params = checkSetup('
'
+ var params = checkSetup(
+ '
'
);
assert.isFalse(check.evaluate.apply(checkContext, params));
});
+
+ it('returns undefined when control has 0 width and height and pointer events: none (focus trap bumper)', () => {
+ var params = checkSetup(
+ '
'
+ );
+ assert.isUndefined(check.evaluate.apply(checkContext, params));
+ });
});
diff --git a/test/integration/rules/aria-hidden-focus/aria-hidden-focus.html b/test/integration/rules/aria-hidden-focus/aria-hidden-focus.html
index 71ed4a53e9..59d9532886 100644
--- a/test/integration/rules/aria-hidden-focus/aria-hidden-focus.html
+++ b/test/integration/rules/aria-hidden-focus/aria-hidden-focus.html
@@ -1,12 +1,10 @@
-
- Some text
-
+
Some text
@@ -33,7 +31,7 @@
@@ -72,3 +70,25 @@
+
+
+
+
+
diff --git a/test/integration/rules/aria-hidden-focus/aria-hidden-focus.json b/test/integration/rules/aria-hidden-focus/aria-hidden-focus.json
index 4110afedfd..1c6640ebd9 100644
--- a/test/integration/rules/aria-hidden-focus/aria-hidden-focus.json
+++ b/test/integration/rules/aria-hidden-focus/aria-hidden-focus.json
@@ -17,5 +17,6 @@
["#pass4"],
["#pass5"],
["#pass6"]
- ]
+ ],
+ "incomplete": [["#incomplete1"], ["#incomplete2"]]
}