Skip to content

Commit f1d6a52

Browse files
committed
Add overflow menu accessibility support
1 parent 4ad2656 commit f1d6a52

File tree

3 files changed

+76
-5
lines changed

3 files changed

+76
-5
lines changed

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

Lines changed: 61 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.
@@ -12,8 +13,7 @@ import { position } from "../../utils/position";
1213
template: `
1314
<ul
1415
#dialog
15-
class="bx--overflow-menu-options bx--overflow-menu-options--open"
16-
tabindex="-1">
16+
class="bx--overflow-menu-options bx--overflow-menu-options--open">
1717
<ng-template
1818
[ngTemplateOutlet]="dialogConfig.content"
1919
[ngTemplateOutletContext]="{overflowMenu: this}">
@@ -22,6 +22,8 @@ import { position } from "../../utils/position";
2222
`
2323
})
2424
export class OverflowMenuPane extends Dialog {
25+
@ViewChild("dialog") dialog;
26+
2527
onDialogInit() {
2628
/**
2729
* -20 shifts the menu up to compensate for the
@@ -32,5 +34,61 @@ export class OverflowMenuPane extends Dialog {
3234
* so we need to add some compensation)
3335
*/
3436
this.addGap["bottom"] = pos => position.addOffset(pos, -20, 60);
37+
38+
setTimeout(() => {
39+
this.listItems()[0].focus();
40+
});
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 "ArrowDown": {
50+
event.preventDefault();
51+
if (!isFocusInLastItem(event, listItems)) {
52+
const index = listItems.findIndex(item => item === event.target);
53+
listItems[index + 1].focus();
54+
} else {
55+
listItems[0].focus();
56+
}
57+
break;
58+
}
59+
60+
case "ArrowUp": {
61+
event.preventDefault();
62+
if (!isFocusInFirstItem(event, listItems)) {
63+
const index = listItems.findIndex(item => item === event.target);
64+
listItems[index - 1].focus();
65+
} else {
66+
listItems[listItems.length - 1].focus();
67+
}
68+
break;
69+
}
70+
71+
case "Home": {
72+
event.preventDefault();
73+
listItems[0].focus();
74+
break;
75+
}
76+
77+
case"End": {
78+
event.preventDefault();
79+
listItems[listItems.length - 1].focus();
80+
break;
81+
}
82+
case "Enter":
83+
case " ": {
84+
this.doClose();
85+
}
86+
}
87+
}
88+
89+
private listItems() {
90+
const buttonClass = ".bx--overflow-menu-options__btn";
91+
const disabledClass = ".bx--overflow-menu-options__option--disabled";
92+
return Array.prototype.slice.call(this.dialog.nativeElement.querySelectorAll(`${buttonClass}:not(${disabledClass})`));
3593
}
3694
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import { Component } from "@angular/core";
2121
placement="bottom"
2222
class="bx--overflow-menu"
2323
style="display: block;">
24-
<svg class="bx--overflow-menu__icon" width="3" height="15" viewBox="0 0 3 15">
24+
<svg class="bx--overflow-menu__icon" width="3" height="15" viewBox="0 0 3 15" tabindex="0">
2525
<g fill-rule="evenodd">
2626
<circle cx="1.5" cy="1.5" r="1.5" />
2727
<circle cx="1.5" cy="7.5" r="1.5" />

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

Lines changed: 14 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,16 @@ export class OverflowMenuDirective extends DialogDirective {
5152
onDialogInit() {
5253
this.dialogConfig.content = this.ibmOverflowMenu;
5354
}
55+
56+
@HostListener("keydown", ["$event"])
57+
hostkeys(event: KeyboardEvent) {
58+
59+
switch (event.key) {
60+
case "Enter":
61+
case " ": {
62+
this.toggle();
63+
break;
64+
}
65+
}
66+
}
5467
}

0 commit comments

Comments
 (0)