Skip to content

Commit

Permalink
refactor(commons/dom): change isFocusable to expose isNativelyFocusab…
Browse files Browse the repository at this point in the history
…le (#638)

Break up isFocusable into two parts: 1: isNativelyFocusable and 2:
Tabindex checks. Expose isNativelyFocusable for convenience, add tests
for isNativelyFocusable.
  • Loading branch information
0ddfell0w authored and WilcoFiers committed Dec 15, 2017
1 parent c11b442 commit fd94b64
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 7 deletions.
31 changes: 24 additions & 7 deletions lib/commons/dom/is-focusable.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@
dom.isFocusable = function (el) {
'use strict';

if (dom.isNativelyFocusable(el)) {
return true;
}
// check if the tabindex is specified and a parseable number
var tabindex = el.getAttribute('tabindex');
if (tabindex && !isNaN(parseInt(tabindex, 10))) {
return true;
}

return false;
};

/**
* Determines if an element is focusable without considering its tabindex
* @method isNativelyFocusable
* @memberof axe.commons.dom
* @instance
* @param {HTMLElement} el The HTMLElement
* @return {Boolean} True if the element is in the focus order but wouldn't be
* if its tabindex were removed. Else, false.
*/
dom.isNativelyFocusable = function(el) {
'use strict';

if (!el ||
el.disabled ||
(!dom.isVisible(el) && el.nodeName.toUpperCase() !== 'AREA')) {
Expand All @@ -33,12 +57,5 @@ dom.isFocusable = function (el) {
case 'BUTTON':
return true;
}

// check if the tabindex is specified and a parseable number
var tabindex = el.getAttribute('tabindex');
if (tabindex && !isNaN(parseInt(tabindex, 10))) {
return true;
}

return false;
};
138 changes: 138 additions & 0 deletions test/commons/dom/is-natively-focusable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
describe('dom.isNativelyFocusable', function () {
'use strict';

var fixture = document.getElementById('fixture');

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


it('should return true for buttons with redundant tabindex', function () {
fixture.innerHTML = '<button tabindex="0" id="target"></button>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for buttons with tabindex -1', function () {
fixture.innerHTML = '<button tabindex="-1" id="target"></button>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for visible, enabled textareas', function () {
fixture.innerHTML = '<textarea id="target"></textarea>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for visible, enabled selects', function () {
fixture.innerHTML = '<select id="target"></select>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for visible, enabled buttons', function () {
fixture.innerHTML = '<button id="target"></button>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for visible, enabled, non-hidden inputs', function () {
fixture.innerHTML = '<input type="text" id="target">';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for disabled elements', function () {
fixture.innerHTML = '<input type="text" id="target" disabled>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for hidden inputs', function () {
fixture.innerHTML = '<input type="hidden" id="target">';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for non-visible elements', function () {
fixture.innerHTML = '<input type="text" id="target" style="display: none">';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for an anchor with an href', function () {
fixture.innerHTML = '<a href="something.html" id="target"></a>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for an anchor with no href', function () {
fixture.innerHTML = '<a name="anchor" id="target"></a>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for a div with a tabindex with spaces', function () {
fixture.innerHTML = '<div id="target" tabindex=" 0 "></div>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for a div with a tabindex', function () {
fixture.innerHTML = '<div id="target" tabindex="0"></div>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for a div with a non-numeric tabindex', function () {
fixture.innerHTML = '<div id="target" tabindex="x"></div>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});

it('should return true for a details element', function () {
fixture.innerHTML = '<details id="target"><p>Detail</p></details>';
var el = document.getElementById('target');

assert.isTrue(axe.commons.dom.isNativelyFocusable(el));

});

it('should return false for a div with no tabindex', function () {
fixture.innerHTML = '<div id="target"></div>';
var el = document.getElementById('target');

assert.isFalse(axe.commons.dom.isNativelyFocusable(el));

});
});

0 comments on commit fd94b64

Please sign in to comment.