Skip to content

Commit

Permalink
fix(scrollable-region-focusable): pass for elements with contentedita…
Browse files Browse the repository at this point in the history
…ble (#2133)

* fix(scrollable-region-focusable): pass for elements with contenteditable

* change order

* full test cases

* fix ie11
  • Loading branch information
straker authored Apr 1, 2020
1 parent 493dd22 commit 1012dfe
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 4 deletions.
30 changes: 28 additions & 2 deletions lib/checks/keyboard/focusable-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,35 @@
* - if element is focusable
* - if element is in focus order via `tabindex`
*/
const isFocusable = virtualNode.isFocusable;
if (virtualNode.hasAttr('contenteditable') && isContenteditable(virtualNode)) {
return true;
}

let tabIndex = parseInt(virtualNode.actualNode.getAttribute('tabindex'), 10);
const isFocusable = virtualNode.isFocusable;
let tabIndex = parseInt(virtualNode.attr('tabindex'), 10);
tabIndex = !isNaN(tabIndex) ? tabIndex : null;

return tabIndex ? isFocusable && tabIndex >= 0 : isFocusable;

// contenteditable is focusable when it is an empty string (whitespace
// is not considered empty) or "true". if the value is "false"
// you can't edit it, but if it's anything else it inherits the value
// from the first valid ancestor
// @see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/contenteditable
function isContenteditable(vNode) {
const contenteditable = vNode.attr('contenteditable');
if (contenteditable === 'true' || contenteditable === '') {
return true;
}

if (contenteditable === 'false') {
return false;
}

const ancestor = axe.utils.closest(virtualNode.parent, '[contenteditable]');
if (!ancestor) {
return false;
}

return isContenteditable(ancestor);
}
48 changes: 48 additions & 0 deletions test/checks/keyboard/focusable-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,52 @@ describe('focusable-element tests', function() {
var actual = check.evaluate.apply(checkContext, params);
assert.isTrue(actual);
});

it('returns true when element made focusable by contenteditable', function() {
var params = checkSetup(
'<p id="target" contenteditable>I hold some text </p>'
);
var actual = check.evaluate.apply(checkContext, params);
assert.isTrue(actual);
});

it('returns true when element made focusable by contenteditable="true"', function() {
var params = checkSetup(
'<p id="target" contenteditable="true">I hold some text </p>'
);
var actual = check.evaluate.apply(checkContext, params);
assert.isTrue(actual);
});

it('returns false when element made focusable by contenteditable="false"', function() {
var params = checkSetup(
'<p id="target" contenteditable="false">I hold some text </p>'
);
var actual = check.evaluate.apply(checkContext, params);
assert.isFalse(actual);
});

it('returns true when element made focusable by contenteditable="invalid" and parent is contenteditable', function() {
var params = checkSetup(
'<div contenteditable><p id="target" contenteditable="invalid">I hold some text </p></div>'
);
var actual = check.evaluate.apply(checkContext, params);
assert.isTrue(actual);
});

it('returns false when element made focusable by contenteditable="invalid" and parent is not contenteditable', function() {
var params = checkSetup(
'<div><p id="target" contenteditable="invalid">I hold some text </p></div>'
);
var actual = check.evaluate.apply(checkContext, params);
assert.isFalse(actual);
});

it('returns false when element made focusable by contenteditable="invalid" and parent is contenteditable="false"', function() {
var params = checkSetup(
'<div contenteditable="false"><p id="target" contenteditable="invalid">I hold some text </p></div>'
);
var actual = check.evaluate.apply(checkContext, params);
assert.isFalse(actual);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,26 @@
<p style="height: 200px;" tabindex="0"></p>
</div>

<div id="pass4" style="height: 200px; overflow-y: auto" contenteditable="true">
<div style="height: 2000px">
<p>Content</p>
</div>
</div>

<div id="pass5" style="height: 200px; overflow-y: auto" contenteditable="true">
<div style="height: 2000px">
<div
id="pass6"
style="height: 200px; overflow-y: auto"
contenteditable="invalid"
>
<div style="height: 2000px">
<p>Content</p>
</div>
</div>
</div>
</div>

<!-- fail -->
<div id="fail1" style="height: 5px; overflow: auto;">
<input type="text" tabindex="-1" />
Expand All @@ -27,6 +47,12 @@
<textarea tabindex="-1"></textarea>
</div>

<div id="fail3" style="height: 200px; overflow-y: auto" contenteditable="false">
<div style="height: 2000px">
<p>Content</p>
</div>
</div>

<!-- inapplicable -->
<section id="inapplicable1">This element is not scrollable</section>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
{
"description": "scrollable-region-focusable tests",
"rule": "scrollable-region-focusable",
"violations": [["#fail1"], ["#fail2"]],
"passes": [["#pass1"], ["#pass2"], ["#pass3"]]
"violations": [["#fail1"], ["#fail2"], ["#fail3"]],
"passes": [
["#pass1"],
["#pass2"],
["#pass3"],
["#pass4"],
["#pass5"],
["#pass6"]
]
}

0 comments on commit 1012dfe

Please sign in to comment.