Skip to content

Commit 03457f9

Browse files
authored
[A11y] Made Package Details page's tabs navigable with left/right arrow keys (#9077)
* [A11y] Made package details tabs navigable with left/right arrow keys * Made changes to another tab instance that would have been affected by previous javascript changes * Changed key trigger to key down rather than key up * Added key code constants, changed key trigger back to keyUp * Normalized key code
1 parent 77b7b1d commit 03457f9

4 files changed

Lines changed: 111 additions & 7 deletions

File tree

src/Bootstrap/dist/js/bootstrap.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2344,12 +2344,14 @@ if (typeof jQuery === 'undefined') {
23442344
.removeClass('active')
23452345
.end()
23462346
.find('[data-toggle="tab"]')
2347+
.attr('tabindex', "-1")
23472348
.attr('aria-expanded', false)
23482349
.attr('aria-selected', false)
23492350

23502351
element
23512352
.addClass('active')
23522353
.find('[data-toggle="tab"]')
2354+
.attr('tabindex', "0")
23532355
.attr('aria-expanded', true)
23542356
.attr('aria-selected', true)
23552357

@@ -2381,6 +2383,25 @@ if (typeof jQuery === 'undefined') {
23812383
$active.removeClass('in')
23822384
}
23832385

2386+
Tab.prototype.navigateTabLeft = function () {
2387+
var $this = this.element
2388+
2389+
if ($this.parent('li').is($this.closest('ul').children().first())) return
2390+
2391+
var $target = $this.parent('li').prev().children('a')[0]
2392+
2393+
$target.focus()
2394+
}
2395+
2396+
Tab.prototype.navigateTabRight = function () {
2397+
var $this = this.element
2398+
2399+
if ($this.parent('li').is($this.closest('ul').children().last())) return
2400+
2401+
var $target = $this.parent('li').next().children('a')[0]
2402+
2403+
$target.focus()
2404+
}
23842405

23852406
// TAB PLUGIN DEFINITION
23862407
// =====================
@@ -2413,14 +2434,38 @@ if (typeof jQuery === 'undefined') {
24132434
// TAB DATA-API
24142435
// ============
24152436

2437+
var keys = {
2438+
left: 37,
2439+
right: 39,
2440+
up: 38,
2441+
down: 40
2442+
}
2443+
24162444
var clickHandler = function (e) {
24172445
e.preventDefault()
24182446
Plugin.call($(this), 'show')
24192447
}
24202448

2449+
var keyUpHandler = function (e) {
2450+
e.preventDefault()
2451+
2452+
// normalized for broswer compatibility
2453+
var code = e.keyCode || e.which;
2454+
2455+
switch (code) {
2456+
case keys.left:
2457+
Plugin.call($(this), 'navigateTabLeft')
2458+
break;
2459+
case keys.right:
2460+
Plugin.call($(this), 'navigateTabRight')
2461+
break;
2462+
}
2463+
}
2464+
24212465
$(document)
24222466
.on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
24232467
.on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
2468+
.on('keyup', '[data-toggle="tab"]', keyUpHandler)
24242469

24252470
}(jQuery);
24262471

src/Bootstrap/js/tab.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,14 @@
7676
.removeClass('active')
7777
.end()
7878
.find('[data-toggle="tab"]')
79+
.attr('tabindex', "-1")
7980
.attr('aria-expanded', false)
8081
.attr('aria-selected', false)
8182

8283
element
8384
.addClass('active')
8485
.find('[data-toggle="tab"]')
86+
.attr('tabindex', "0")
8587
.attr('aria-expanded', true)
8688
.attr('aria-selected', true)
8789

@@ -113,6 +115,25 @@
113115
$active.removeClass('in')
114116
}
115117

118+
Tab.prototype.navigateTabLeft = function () {
119+
var $this = this.element
120+
121+
if ($this.parent('li').is($this.closest('ul').children().first())) return
122+
123+
var $target = $this.parent('li').prev().children('a')[0]
124+
125+
$target.focus()
126+
}
127+
128+
Tab.prototype.navigateTabRight = function () {
129+
var $this = this.element
130+
131+
if ($this.parent('li').is($this.closest('ul').children().last())) return
132+
133+
var $target = $this.parent('li').next().children('a')[0]
134+
135+
$target.focus()
136+
}
116137

117138
// TAB PLUGIN DEFINITION
118139
// =====================
@@ -145,13 +166,37 @@
145166
// TAB DATA-API
146167
// ============
147168

169+
var keys = {
170+
left: 37,
171+
right: 39,
172+
up: 38,
173+
down: 40
174+
}
175+
148176
var clickHandler = function (e) {
149177
e.preventDefault()
150178
Plugin.call($(this), 'show')
151179
}
152180

181+
var keyUpHandler = function (e) {
182+
e.preventDefault()
183+
184+
// normalized for broswer compatibility
185+
var code = e.keyCode || e.which;
186+
187+
switch (code) {
188+
case keys.left:
189+
Plugin.call($(this), 'navigateTabLeft')
190+
break;
191+
case keys.right:
192+
Plugin.call($(this), 'navigateTabRight')
193+
break;
194+
}
195+
}
196+
153197
$(document)
154198
.on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler)
155199
.on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler)
200+
.on('keyup', '[data-toggle="tab"]', keyUpHandler)
156201

157202
}(jQuery);

src/NuGetGallery/Views/Packages/DisplayPackage.cshtml

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
<li role="presentation" class="@(active ? "active" : string.Empty)">
169169
<a href="#@packageManager.Id" aria-expanded="@(active ? "true" : "false")"
170170
id="@packageManager.Id-tab" class="package-manager-tab"
171-
aria-selected="@(active ? "true" : "false")"
171+
aria-selected="@(active ? "true" : "false")" tabindex="@(active ? "0" : "-1")"
172172
aria-controls="@packageManager.Id" role="tab" data-toggle="tab"
173173
title="Switch to tab panel which contains package installation command for @packageManager.Name">
174174
@packageManager.Name
@@ -515,7 +515,8 @@
515515
class="body-tab"
516516
aria-controls="readme-tab"
517517
aria-expanded="@(activeBodyTab == "readme" ? "true" : "false")"
518-
aria-selected="@(activeBodyTab == "readme" ? "true" : "false")">
518+
aria-selected="@(activeBodyTab == "readme" ? "true" : "false")"
519+
tabindex="@(activeBodyTab == "readme" ? "0" : "-1")">
519520
<i class="ms-Icon ms-Icon--Dictionary" aria-hidden="true"></i>
520521
README
521522
</a>
@@ -532,7 +533,8 @@
532533
class="body-tab"
533534
aria-controls="supportedframeworks-tab"
534535
aria-expanded="@(activeBodyTab == "supportedframeworks" ? "true" : "false")"
535-
aria-selected="@(activeBodyTab == "supportedframeworks" ? "true" : "false")">
536+
aria-selected="@(activeBodyTab == "supportedframeworks" ? "true" : "false")"
537+
tabindex="@(activeBodyTab == "supportedframeworks" ? "0" : "-1")">
536538
<i class="ms-Icon ms-Icon--Package" aria-hidden="true"></i>
537539
Frameworks
538540
</a>
@@ -548,7 +550,8 @@
548550
class="body-tab"
549551
aria-controls="dependencies-tab"
550552
aria-expanded="@(activeBodyTab == "dependencies" ? "true" : "false")"
551-
aria-selected="@(activeBodyTab == "dependencies" ? "true" : "false")">
553+
aria-selected="@(activeBodyTab == "dependencies" ? "true" : "false")"
554+
tabindex="@(activeBodyTab == "dependencies" ? "0" : "-1")">
552555
<i class="ms-Icon ms-Icon--Packages" aria-hidden="true"></i>
553556
Dependencies
554557
</a>
@@ -566,7 +569,8 @@
566569
class="body-tab"
567570
aria-controls="usedby-tab"
568571
aria-expanded="@(activeBodyTab == "usedby" ? "true" : "false")"
569-
aria-selected="@(activeBodyTab == "usedby" ? "true" : "false")">
572+
aria-selected="@(activeBodyTab == "usedby" ? "true" : "false")"
573+
tabindex="@(activeBodyTab == "usedby" ? "0" : "-1")">
570574
<i class="ms-Icon ms-Icon--BranchFork2" aria-hidden="true"></i>
571575
Used By
572576
</a>
@@ -582,7 +586,8 @@
582586
class="body-tab"
583587
aria-controls="versions-tab"
584588
aria-expanded="@(activeBodyTab == "versions" ? "true" : "false")"
585-
aria-selected="@(activeBodyTab == "versions" ? "true" : "false")">
589+
aria-selected="@(activeBodyTab == "versions" ? "true" : "false")"
590+
tabindex="@(activeBodyTab == "versions" ? "0" : "-1")">
586591
<i class="ms-Icon ms-Icon--Stopwatch" aria-hidden="true"></i>
587592
Versions
588593
</a>
@@ -592,7 +597,15 @@
592597
{
593598
activeBodyTab = activeBodyTab ?? "releasenotes";
594599
<li role="presentation">
595-
<a href="#releasenotes-tab" aria-controls="releasenotes-tab" role="tab" data-toggle="tab" id="release-body-tab" class="body-tab">
600+
<a href="#releasenotes-tab"
601+
role="tab"
602+
data-toggle="tab"
603+
id="release-body-tab"
604+
class="body-tab"
605+
aria-controls="releasenotes-tab"
606+
aria-expanded="@(activeBodyTab == "releasenotes" ? "true" : "false")"
607+
aria-selected="@(activeBodyTab == "releasenotes" ? "true" : "false")"
608+
tabindex="@(activeBodyTab == "releasenotes" ? "0" : "-1")">
596609
<i class="ms-Icon ms-Icon--ReadingMode" aria-hidden="true"></i>
597610
Release Notes
598611
</a>

src/NuGetGallery/Views/Packages/_ImportReadMe.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<li role="presentation" class="@(active ? "active" : string.Empty)">
44
<a href="#@id" aria-expanded="@(active ? "true" : "false")"
55
aria-selected="@(active ? "true" : "false")"
6+
tabindex="@(active ? "0" : "-1")"
67
aria-controls="@id" role="tab" data-toggle="tab"
78
data-source-type="@sourceType"
89
data-bind="event: { 'show.bs.tab': OnReadmeTabChange }"

0 commit comments

Comments
 (0)