Skip to content

Commit bdba4a2

Browse files
authored
Merge pull request #131 from marthacryan/aria-menus
Add aria roles to menus
2 parents 7fa5145 + a84632d commit bdba4a2

File tree

3 files changed

+118
-6
lines changed

3 files changed

+118
-6
lines changed

packages/virtualdom/src/index.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,67 @@ type ElementAttrNames = (
126126
);
127127

128128

129+
130+
/**
131+
* The names of ARIA attributes for HTML elements.
132+
*
133+
* The attribute names are collected from
134+
* https://www.w3.org/TR/html5/infrastructure.html#element-attrdef-aria-role
135+
*/
136+
export
137+
type ARIAAttrNames = (
138+
'aria-activedescendant' |
139+
'aria-atomic' |
140+
'aria-autocomplete' |
141+
'aria-busy' |
142+
'aria-checked' |
143+
'aria-colcount' |
144+
'aria-colindex' |
145+
'aria-colspan' |
146+
'aria-controls' |
147+
'aria-current' |
148+
'aria-describedby' |
149+
'aria-details' |
150+
'aria-dialog' |
151+
'aria-disabled' |
152+
'aria-dropeffect' |
153+
'aria-errormessage' |
154+
'aria-expanded' |
155+
'aria-flowto' |
156+
'aria-grabbed' |
157+
'aria-haspopup' |
158+
'aria-hidden' |
159+
'aria-invalid' |
160+
'aria-keyshortcuts' |
161+
'aria-label' |
162+
'aria-labelledby' |
163+
'aria-level' |
164+
'aria-live' |
165+
'aria-multiline' |
166+
'aria-multiselectable' |
167+
'aria-orientation' |
168+
'aria-owns' |
169+
'aria-placeholder' |
170+
'aria-posinset' |
171+
'aria-pressed' |
172+
'aria-readonly' |
173+
'aria-relevant' |
174+
'aria-required' |
175+
'aria-roledescription' |
176+
'aria-rowcount' |
177+
'aria-rowindex' |
178+
'aria-rowspan' |
179+
'aria-selected' |
180+
'aria-setsize' |
181+
'aria-sort' |
182+
'aria-valuemax' |
183+
'aria-valuemin' |
184+
'aria-valuenow' |
185+
'aria-valuetext' |
186+
'role'
187+
);
188+
189+
129190
/**
130191
* The names of the supported HTML5 CSS property names.
131192
*
@@ -585,6 +646,19 @@ type ElementInlineStyle = {
585646
};
586647

587648

649+
/**
650+
* The ARIA attributes for a virtual element node.
651+
*
652+
* These are the attributes which are applied to a real DOM element via
653+
* `element.setAttribute()`. The supported attribute names are defined
654+
* by the `ARIAAttrNames` type.
655+
*/
656+
export
657+
type ElementARIAAttrs = {
658+
readonly [T in ARIAAttrNames]?: string;
659+
};
660+
661+
588662
/**
589663
* The base attributes for a virtual element node.
590664
*
@@ -657,12 +731,13 @@ type ElementSpecialAttrs = {
657731
/**
658732
* The full set of attributes supported by a virtual element node.
659733
*
660-
* This is the combination of the base element attributes, the inline
661-
* element event listeners, and the special element attributes.
734+
* This is the combination of the base element attributes, the the ARIA attributes,
735+
* the inline element event listeners, and the special element attributes.
662736
*/
663737
export
664738
type ElementAttrs = (
665739
ElementBaseAttrs &
740+
ElementARIAAttrs &
666741
ElementEventAttrs &
667742
ElementSpecialAttrs
668743
);

packages/widgets/src/menu.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
} from '@lumino/signaling';
3737

3838
import {
39-
ElementDataset, VirtualDOM, VirtualElement, h
39+
ARIAAttrNames, ElementARIAAttrs, ElementDataset, VirtualDOM, VirtualElement, h
4040
} from '@lumino/virtualdom';
4141

4242
import {
@@ -1149,8 +1149,9 @@ namespace Menu {
11491149
renderItem(data: IRenderData): VirtualElement {
11501150
let className = this.createItemClass(data);
11511151
let dataset = this.createItemDataset(data);
1152+
let aria = this.createItemARIA(data);
11521153
return (
1153-
h.li({ className, dataset },
1154+
h.li({ className, dataset, ...aria },
11541155
this.renderIcon(data),
11551156
this.renderLabel(data),
11561157
this.renderShortcut(data),
@@ -1318,6 +1319,28 @@ namespace Menu {
13181319
let extra = data.item.iconClass;
13191320
return extra ? `${name} ${extra}` : name;
13201321
}
1322+
1323+
/**
1324+
* Create the aria attributes for menu item.
1325+
*
1326+
* @param data - The data to use for the aria attributes.
1327+
*
1328+
* @returns The aria attributes object for the item.
1329+
*/
1330+
createItemARIA(data: IRenderData): ElementARIAAttrs {
1331+
let aria: {[T in ARIAAttrNames]?: string} = {};
1332+
switch (data.item.type) {
1333+
case 'separator':
1334+
aria.role = 'presentation';
1335+
break;
1336+
case 'submenu':
1337+
aria['aria-haspopup'] = 'true';
1338+
break;
1339+
default:
1340+
aria.role = 'menuitem';
1341+
}
1342+
return aria;
1343+
}
13211344

13221345
/**
13231346
* Create the render content for the label node.
@@ -1401,6 +1424,7 @@ namespace Private {
14011424
content.classList.add('p-Menu-content');
14021425
/* </DEPRECATED> */
14031426
node.appendChild(content);
1427+
content.setAttribute('role', 'menu');
14041428
node.tabIndex = -1;
14051429
return node;
14061430
}

packages/widgets/src/menubar.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
} from '@lumino/messaging';
2525

2626
import {
27-
ElementDataset, VirtualDOM, VirtualElement, h
27+
ElementARIAAttrs, ElementDataset, VirtualDOM, VirtualElement, h
2828
} from '@lumino/virtualdom';
2929

3030
import {
@@ -770,8 +770,9 @@ namespace MenuBar {
770770
renderItem(data: IRenderData): VirtualElement {
771771
let className = this.createItemClass(data);
772772
let dataset = this.createItemDataset(data);
773+
let aria = this.createItemARIA(data);
773774
return (
774-
h.li({ className, dataset },
775+
h.li({ className, dataset, ...aria },
775776
this.renderIcon(data),
776777
this.renderLabel(data)
777778
)
@@ -850,6 +851,17 @@ namespace MenuBar {
850851
return data.title.dataset;
851852
}
852853

854+
/**
855+
* Create the aria attributes for menu bar item.
856+
*
857+
* @param data - The data to use for the aria attributes.
858+
*
859+
* @returns The aria attributes object for the item.
860+
*/
861+
createItemARIA(data: IRenderData): ElementARIAAttrs {
862+
return {role: 'menuitem', 'aria-haspopup': 'true'};
863+
}
864+
853865
/**
854866
* Create the class name for the menu bar item icon.
855867
*
@@ -924,6 +936,7 @@ namespace Private {
924936
content.classList.add('p-MenuBar-content');
925937
/* </DEPRECATED> */
926938
node.appendChild(content);
939+
content.setAttribute('role', 'menubar');
927940
node.tabIndex = -1;
928941
return node;
929942
}

0 commit comments

Comments
 (0)