Skip to content

Commit ef1035f

Browse files
authored
Merge pull request #94 from youda97/master
Add overflow menu accessibility support
2 parents f5e8c34 + 8ed1470 commit ef1035f

File tree

5 files changed

+75
-7
lines changed

5 files changed

+75
-7
lines changed

src/dialog/dialog.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ export class Dialog implements OnInit, AfterViewInit, OnDestroy {
241241
@HostListener("keydown", ["$event"])
242242
escapeClose(event: KeyboardEvent) {
243243
switch (event.key) {
244+
case "Esc": // IE specific value
244245
case "Escape": {
245246
event.stopImmediatePropagation();
246247
this.doClose();

src/dialog/overflow-menu/overflow-menu-option.component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
template: `
2323
<button
2424
class="bx--overflow-menu-options__btn"
25+
role="menuitem"
2526
[ngClass]="{
2627
'bx--overflow-menu-options__option--danger': type === 'danger',
2728
'bx--overflow-menu-options__option--disabled': disabled
@@ -34,7 +35,7 @@ import {
3435
})
3536
export class OverflowMenuOption {
3637
@HostBinding("class") optionClass = "bx--overflow-menu-options__option";
37-
@HostBinding("attr.role") role = "list-item";
38+
@HostBinding("attr.role") role = "presentation";
3839

3940
/**
4041
* toggles between `normal` and `danger` states

src/dialog/overflow-menu/overflow-menu-pane.component.ts

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Component } from "@angular/core";
1+
import { Component, ViewChild, HostListener } from "@angular/core";
22
import { Dialog } from "../dialog.component";
33
import { position } from "../../utils/position";
4+
import { isFocusInLastItem, isFocusInFirstItem } from "./../../common/tab.service";
45

56
/**
67
* Extend the `Dialog` component to create an overflow menu.
@@ -11,9 +12,10 @@ import { position } from "../../utils/position";
1112
selector: "ibm-overflow-menu-pane",
1213
template: `
1314
<ul
15+
role="menu"
16+
attr.aria-label="{{'OVERFLOW_MENU.OVERFLOW' | translate}}"
1417
#dialog
15-
class="bx--overflow-menu-options bx--overflow-menu-options--open"
16-
tabindex="-1">
18+
class="bx--overflow-menu-options bx--overflow-menu-options--open">
1719
<ng-template
1820
[ngTemplateOutlet]="dialogConfig.content"
1921
[ngTemplateOutletContext]="{overflowMenu: this}">
@@ -22,6 +24,8 @@ import { position } from "../../utils/position";
2224
`
2325
})
2426
export class OverflowMenuPane extends Dialog {
27+
@ViewChild("dialog") dialog;
28+
2529
onDialogInit() {
2630
/**
2731
* -20 shifts the menu up to compensate for the
@@ -32,5 +36,53 @@ export class OverflowMenuPane extends Dialog {
3236
* so we need to add some compensation)
3337
*/
3438
this.addGap["bottom"] = pos => position.addOffset(pos, -20, 60);
39+
40+
setTimeout(() => this.listItems()[0].focus());
41+
}
42+
43+
@HostListener("keydown", ["$event"])
44+
hostkeys(event: KeyboardEvent) {
45+
this.escapeClose(event);
46+
const listItems = this.listItems();
47+
48+
switch (event.key) {
49+
case "Down": // IE specific value
50+
case "ArrowDown":
51+
event.preventDefault();
52+
if (!isFocusInLastItem(event, listItems)) {
53+
const index = listItems.findIndex(item => item === event.target);
54+
listItems[index + 1].focus();
55+
} else {
56+
listItems[0].focus();
57+
}
58+
break;
59+
60+
case "Up": // IE specific value
61+
case "ArrowUp":
62+
event.preventDefault();
63+
if (!isFocusInFirstItem(event, listItems)) {
64+
const index = listItems.findIndex(item => item === event.target);
65+
listItems[index - 1].focus();
66+
} else {
67+
listItems[listItems.length - 1].focus();
68+
}
69+
break;
70+
71+
case "Home":
72+
event.preventDefault();
73+
listItems[0].focus();
74+
break;
75+
76+
case "End":
77+
event.preventDefault();
78+
listItems[listItems.length - 1].focus();
79+
break;
80+
}
81+
}
82+
83+
private listItems() {
84+
const buttonClass = ".bx--overflow-menu-options__btn";
85+
const disabledClass = ".bx--overflow-menu-options__option--disabled";
86+
return Array.from<any>(this.dialog.nativeElement.querySelectorAll(`${buttonClass}:not(${disabledClass})`));
3587
}
3688
}

src/dialog/overflow-menu/overflow-menu.component.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ import { Component } from "@angular/core";
1818
<div
1919
[ibmOverflowMenu]="options"
2020
[appendToBody]="true"
21-
placement="bottom"
21+
attr.aria-label="{{'OVERFLOW_MENU.OVERFLOW' | translate}}"
2222
class="bx--overflow-menu"
23-
style="display: block;">
23+
role="button"
24+
placement="bottom"
25+
style="display: block;"
26+
tabindex="0">
2427
<svg class="bx--overflow-menu__icon" width="3" height="15" viewBox="0 0 3 15">
2528
<g fill-rule="evenodd">
2629
<circle cx="1.5" cy="1.5" r="1.5" />

src/dialog/overflow-menu/overflow-menu.directive.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import {
33
ElementRef,
44
ViewContainerRef,
55
Input,
6-
TemplateRef
6+
TemplateRef,
7+
HostListener
78
} from "@angular/core";
89
import { DialogDirective } from "./../dialog.directive";
910
import { DialogService } from "./../dialog.service";
@@ -51,4 +52,14 @@ export class OverflowMenuDirective extends DialogDirective {
5152
onDialogInit() {
5253
this.dialogConfig.content = this.ibmOverflowMenu;
5354
}
55+
56+
@HostListener("keydown", ["$event"])
57+
hostkeys(event: KeyboardEvent) {
58+
switch (event.key) {
59+
case "Enter":
60+
case " ":
61+
this.toggle();
62+
break;
63+
}
64+
}
5465
}

0 commit comments

Comments
 (0)