Skip to content
Permalink
Browse files

Core: Partial fix for #3559: Proper implementation for :focusable and…

… :tabbable selectors.
  • Loading branch information
scottgonzalez committed Jan 21, 2009
1 parent debb342 commit f80d9eb465e428b3900bc0b741392f14ecd859f0
Showing with 269 additions and 107 deletions.
  1. +70 −34 tests/unit/core/core.html
  2. +0 −50 tests/unit/core/core.js
  3. +184 −0 tests/unit/core/selector.js
  4. +15 −23 ui/ui.core.js
@@ -12,50 +12,86 @@
<script type="text/javascript" src="../../../external/simulate/jquery.simulate.js"></script>

<script type="text/javascript" src="core.js"></script>
<script type="text/javascript" src="selector.js"></script>
</head>
<body>

<div id="main">
<div id="wrap1">
<input id="input1-1" />
<input type="text" id="input1-2" />
<input type="checkbox" id="input1-3" />
<input type="radio" id="input1-4" />
<input type="button" id="input1-5" />
<input type="hidden" id="input1-6" />
<select id="input1-7"></select>
<textarea id="input1-8"></textarea>
<a href="#" id="anchor1-1">anchor</a>
<a id="anchor1-2">anchor</a>
<div>
<input id="visibleAncestor-inputTypeNone" />
<input type="text" id="visibleAncestor-inputTypeText" />
<input type="checkbox" id="visibleAncestor-inputTypeCheckbox" />
<input type="radio" id="visibleAncestor-inputTypeRadio" />
<input type="button" id="visibleAncestor-inputTypeButton" />
<input type="hidden" id="visibleAncestor-inputTypeHidden" />
<button id="visibleAncestor-button"></button>
<select id="visibleAncestor-select">
<option>option</option>
</select>
<textarea id="visibleAncestor-textarea"></textarea>
<object id="visibleAncestor-object"></object>
<a href="#" id="visibleAncestor-anchorWithHref">anchor</a>
<a id="visibleAncestor-anchorWithoutHref">anchor</a>
<map>
<area href="#" id="visibleAncestor-areaWithHref" alt="" />
<area id="visibleAncestor-areaWithoutHref" alt="" />
</map>
<span id="visibleAncestor-span"></span>
<div id="visibleAncestor-div"></div>
<span id="visibleAncestor-spanWithTabindex" tabindex="1"></span>
<div id="visibleAncestor-divWithNegativeTabindex" tabindex="-1"></div>
</div>
<div id="wrap2">
<input id="input2-1" disabled="disabled" />
<input type="text" id="input2-2" disabled="disabled" />
<input type="checkbox" id="input2-3" disabled="disabled" />
<input type="radio" id="input2-4" disabled="disabled" />
<input type="button" id="input2-5" disabled="disabled" />
<input type="hidden" id="input2-6" disabled="disabled" />
<select id="input2-7" disabled="disabled"></select>
<textarea id="input2-8" disabled="disabled"></textarea>

<div>
<input id="disabledElement-inputTypeNone" disabled="disabled" />
<input type="text" id="disabledElement-inputTypeText" disabled="disabled" />
<input type="checkbox" id="disabledElement-inputTypeCheckbox" disabled="disabled" />
<input type="radio" id="disabledElement-inputTypeRadio" disabled="disabled" />
<input type="button" id="disabledElement-inputTypeButton" disabled="disabled" />
<input type="hidden" id="disabledElement-inputTypeHidden" disabled="disabled" />
<button id="disabledElement-button" disabled="disabled"></button>
<select id="disabledElement-select" disabled="disabled"></select>
<textarea id="disabledElement-textarea" disabled="disabled"></textarea>
</div>
<div id="wrap3">
<div id="wrap3-1" style="display: none;">
<input id="input3-1" />
<a href="#" id="anchor3-1">anchor</a>

<div>
<div id="displayNoneAncestor" style="display: none;">
<input id="displayNoneAncestor-input" />
<span tabindex="1" id="displayNoneAncestor-span"></span>
</div>
<div id="wrap3-2" style="visibility: hidden;">
<input id="input3-2" />
<a href="#" id="anchor3-2">anchor</a>

<div id="visibilityHiddenAncestor" style="visibility: hidden;">
<input id="visibilityHiddenAncestor-input" />
<span tabindex="1" id="visibilityHiddenAncestor-span"></span>
</div>
<input id="input3-3" style="display: none;">
<input id="input3-4" style="visibility: hidden;">

<input id="displayNone-input" style="display: none;" />
<input id="visibilityHidden-input" style="visibility: hidden;" />

<span tabindex="1" id="displayNone-span" style="display: none;"></span>
<span tabindex="1" id="visibilityHidden-span" style="visibility: hidden;"></span>
</div>
<div id="wrap4">
<input id="input4-1" tabindex="0" />
<input id="input4-2" tabindex="10" />
<input id="input4-3" tabindex="-1" />
<input id="input4-4" tabindex="-50" />

<div>
<input id="inputTabindex0" tabindex="0" />
<input id="inputTabindex10" tabindex="10" />
<input id="inputTabindex-1" tabindex="-1" />
<input id="inputTabindex-50" tabindex="-50" />

<span id="spanTabindex0" tabindex="0"></span>
<span id="spanTabindex10" tabindex="10"></span>
<span id="spanTabindex-1" tabindex="-1"></span>
<span id="spanTabindex-50" tabindex="-50"></span>
</div>

<div>
<input id="inputTabindexfoo" tabindex="foo" />
<input id="inputTabindex3foo" tabindex="3foo" />

<span id="spanTabindexfoo" tabindex="foo"></span>
<span id="spanTabindex3foo" tabindex="3foo"></span>
</div>

<div id="aria"></div>
</div>

@@ -3,56 +3,6 @@
*/
(function($) {

module("selectors");

test("tabbable - enabled elements", function() {
expect(10);

ok( $('#input1-1').is(':tabbable'), 'input, no type');
ok( $('#input1-2').is(':tabbable'), 'input, type text');
ok( $('#input1-3').is(':tabbable'), 'input, type checkbox');
ok( $('#input1-4').is(':tabbable'), 'input, type radio');
ok( $('#input1-5').is(':tabbable'), 'input, type button');
ok(!$('#input1-6').is(':tabbable'), 'input, type hidden');
ok( $('#input1-7').is(':tabbable'), 'select');
ok( $('#input1-8').is(':tabbable'), 'textarea');
ok( $('#anchor1-1').is(':tabbable'), 'anchor with href');
ok(!$('#anchor1-2').is(':tabbable'), 'anchor without href');
});

test("tabbable - disabled elements", function() {
expect(8);

ok(!$('#input2-1').is(':tabbable'), 'input, no type');
ok(!$('#input2-2').is(':tabbable'), 'input, type text');
ok(!$('#input2-3').is(':tabbable'), 'input, type checkbox');
ok(!$('#input2-4').is(':tabbable'), 'input, type radio');
ok(!$('#input2-5').is(':tabbable'), 'input, type button');
ok(!$('#input2-6').is(':tabbable'), 'input, type hidden');
ok(!$('#input2-7').is(':tabbable'), 'select');
ok(!$('#input2-8').is(':tabbable'), 'textarea');
});

test("tabbable - hidden styles", function() {
expect(6);

ok(!$('#input3-1').is(':tabbable'), 'input, hidden wrapper - display: none');
ok(!$('#anchor3-1').is(':tabbable'), 'anchor, hidden wrapper - display: none');
ok(!$('#input3-2').is(':tabbable'), 'input, hidden wrapper - visibility: hidden');
ok(!$('#anchor3-2').is(':tabbable'), 'anchor, hidden wrapper - visibility: hidden');
ok(!$('#input3-3').is(':tabbable'), 'input, display: none');
ok(!$('#input3-4').is(':tabbable'), 'input, visibility: hidden');
});

test("tabbable - tabindex", function() {
expect(4);

ok( $('#input4-1').is(':tabbable'), 'input, tabindex 0');
ok( $('#input4-2').is(':tabbable'), 'input, tabindex 10');
ok(!$('#input4-3').is(':tabbable'), 'input, tabindex -1');
ok(!$('#input4-4').is(':tabbable'), 'input, tabindex -50');
});

module('jQuery extensions');

test("attr - aria", function() {
@@ -0,0 +1,184 @@
/*
* core unit tests
*/
(function($) {

module("selectors");

function isFocusable(selector, msg) {
ok($(selector).is(':focusable'), msg);
}

function isNotFocusable(selector, msg) {
ok($(selector).length && !$(selector).is(':focusable'), msg);
}

function isTabbable(selector, msg) {
ok($(selector).is(':tabbable'), msg);
}

function isNotTabbable(selector, msg) {
ok($(selector).length && !$(selector).is(':tabbable'), msg);
}

test("focusable - visible, enabled elements", function() {
expect(18);

isFocusable('#visibleAncestor-inputTypeNone', 'input, no type');
isFocusable('#visibleAncestor-inputTypeText', 'input, type text');
isFocusable('#visibleAncestor-inputTypeCheckbox', 'input, type checkbox');
isFocusable('#visibleAncestor-inputTypeRadio', 'input, type radio');
isFocusable('#visibleAncestor-inputTypeButton', 'input, type button');
isNotFocusable('#visibleAncestor-inputTypeHidden', 'input, type hidden');
isFocusable('#visibleAncestor-button', 'button');
isFocusable('#visibleAncestor-select', 'select');
isFocusable('#visibleAncestor-textarea', 'textarea');
isFocusable('#visibleAncestor-object', 'object');
isFocusable('#visibleAncestor-anchorWithHref', 'anchor with href');
isNotFocusable('#visibleAncestor-anchorWithoutHref', 'anchor without href');
isFocusable('#visibleAncestor-areaWithHref', 'area with href');
isNotFocusable('#visibleAncestor-areaWithoutHref', 'area without href');
isNotFocusable('#visibleAncestor-span', 'span');
isNotFocusable('#visibleAncestor-div', 'div');
isFocusable("#visibleAncestor-spanWithTabindex", 'span with tabindex');
isFocusable("#visibleAncestor-divWithNegativeTabindex", 'div with tabindex');
});

test("focusable - disabled elements", function() {
expect(9);

isNotFocusable('#disabledElement-inputTypeNone', 'input, no type');
isNotFocusable('#disabledElement-inputTypeText', 'input, type text');
isNotFocusable('#disabledElement-inputTypeCheckbox', 'input, type checkbox');
isNotFocusable('#disabledElement-inputTypeRadio', 'input, type radio');
isNotFocusable('#disabledElement-inputTypeButton', 'input, type button');
isNotFocusable('#disabledElement-inputTypeHidden', 'input, type hidden');
isNotFocusable('#disabledElement-button', 'button');
isNotFocusable('#disabledElement-select', 'select');
isNotFocusable('#disabledElement-textarea', 'textarea');
});

test("focusable - hidden styles", function() {
expect(8);

isNotFocusable('#displayNoneAncestor-input', 'input, display: none parent');
isNotFocusable('#displayNoneAncestor-span', 'span with tabindex, display: none parent');

isNotFocusable('#visibilityHiddenAncestor-input', 'input, visibility: hidden parent');
isNotFocusable('#visibilityHiddenAncestor-span', 'span with tabindex, visibility: hidden parent');

isNotFocusable('#displayNone-input', 'input, display: none');
isNotFocusable('#visibilityHidden-input', 'input, visibility: hidden');

isNotFocusable('#displayNone-span', 'span with tabindex, display: none');
isNotFocusable('#visibilityHidden-span', 'span with tabindex, visibility: hidden');
});

test("focusable - natively tabbable with various tabindex", function() {
expect(4);

isFocusable('#inputTabindex0', 'input, tabindex 0');
isFocusable('#inputTabindex10', 'input, tabindex 10');
isFocusable('#inputTabindex-1', 'input, tabindex -1');
isFocusable('#inputTabindex-50', 'input, tabindex -50');
});

test("focusable - not natively tabbable with various tabindex", function() {
expect(4);

isFocusable('#spanTabindex0', 'span, tabindex 0');
isFocusable('#spanTabindex10', 'span, tabindex 10');
isFocusable('#spanTabindex-1', 'span, tabindex -1');
isFocusable('#spanTabindex-50', 'span, tabindex -50');
});

test("focusable - invalid tabindex", function() {
expect(4);

isFocusable('#inputTabindexfoo', 'input, tabindex foo');
isFocusable('#inputTabindex3foo', 'input, tabindex 3foo');
isNotFocusable('#spanTabindexfoo', 'span tabindex foo');
isNotFocusable('#spanTabindex3foo', 'span, tabindex 3foo');
});

test("tabbable - visible, enabled elements", function() {
expect(18);

isTabbable('#visibleAncestor-inputTypeNone', 'input, no type');
isTabbable('#visibleAncestor-inputTypeText', 'input, type text');
isTabbable('#visibleAncestor-inputTypeCheckbox', 'input, type checkbox');
isTabbable('#visibleAncestor-inputTypeRadio', 'input, type radio');
isTabbable('#visibleAncestor-inputTypeButton', 'input, type button');
isNotTabbable('#visibleAncestor-inputTypeHidden', 'input, type hidden');
isTabbable('#visibleAncestor-button', 'button');
isTabbable('#visibleAncestor-select', 'select');
isTabbable('#visibleAncestor-textarea', 'textarea');
isTabbable('#visibleAncestor-object', 'object');
isTabbable('#visibleAncestor-anchorWithHref', 'anchor with href');
isNotTabbable('#visibleAncestor-anchorWithoutHref', 'anchor without href');
isTabbable('#visibleAncestor-areaWithHref', 'area with href');
isNotTabbable('#visibleAncestor-areaWithoutHref', 'area without href');
isNotTabbable('#visibleAncestor-span', 'span');
isNotTabbable('#visibleAncestor-div', 'div');
isTabbable("#visibleAncestor-spanWithTabindex", 'span with tabindex');
isNotTabbable("#visibleAncestor-divWithNegativeTabindex", 'div with tabindex');
});

test("Tabbable - disabled elements", function() {
expect(9);

isNotTabbable('#disabledElement-inputTypeNone', 'input, no type');
isNotTabbable('#disabledElement-inputTypeText', 'input, type text');
isNotTabbable('#disabledElement-inputTypeCheckbox', 'input, type checkbox');
isNotTabbable('#disabledElement-inputTypeRadio', 'input, type radio');
isNotTabbable('#disabledElement-inputTypeButton', 'input, type button');
isNotTabbable('#disabledElement-inputTypeHidden', 'input, type hidden');
isNotTabbable('#disabledElement-button', 'button');
isNotTabbable('#disabledElement-select', 'select');
isNotTabbable('#disabledElement-textarea', 'textarea');
});

test("Tabbable - hidden styles", function() {
expect(8);

isNotTabbable('#displayNoneAncestor-input', 'input, display: none parent');
isNotTabbable('#displayNoneAncestor-span', 'span with tabindex, display: none parent');

isNotTabbable('#visibilityHiddenAncestor-input', 'input, visibility: hidden parent');
isNotTabbable('#visibilityHiddenAncestor-span', 'span with tabindex, visibility: hidden parent');

isNotTabbable('#displayNone-input', 'input, display: none');
isNotTabbable('#visibilityHidden-input', 'input, visibility: hidden');

isNotTabbable('#displayNone-span', 'span with tabindex, display: none');
isNotTabbable('#visibilityHidden-span', 'span with tabindex, visibility: hidden');
});

test("Tabbable - natively tabbable with various tabindex", function() {
expect(4);

isTabbable('#inputTabindex0', 'input, tabindex 0');
isTabbable('#inputTabindex10', 'input, tabindex 10');
isNotTabbable('#inputTabindex-1', 'input, tabindex -1');
isNotTabbable('#inputTabindex-50', 'input, tabindex -50');
});

test("Tabbable - not natively tabbable with various tabindex", function() {
expect(4);

isTabbable('#spanTabindex0', 'span, tabindex 0');
isTabbable('#spanTabindex10', 'span, tabindex 10');
isNotTabbable('#spanTabindex-1', 'span, tabindex -1');
isNotTabbable('#spanTabindex-50', 'span, tabindex -50');
});

test("Tabbable - invalid tabindex", function() {
expect(4);

isTabbable('#inputTabindexfoo', 'input, tabindex foo');
isTabbable('#inputTabindex3foo', 'input, tabindex 3foo');
isNotTabbable('#spanTabindexfoo', 'span tabindex foo');
isNotTabbable('#spanTabindex3foo', 'span, tabindex 3foo');
});

})(jQuery);

0 comments on commit f80d9eb

Please sign in to comment.
You can’t perform that action at this time.