Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 296ed22

Browse files
authored
Use semantic list elements for menu lists and tab lists (#10902)
* Use semantic list elements for pop up menu lists * Use semantic list elements for tab lists * Fix tests * Update snapshot
1 parent 8b7eb8b commit 296ed22

File tree

9 files changed

+92
-82
lines changed

9 files changed

+92
-82
lines changed

res/css/views/context_menus/_IconizedContextMenu.pcss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ limitations under the License.
1818
.mx_IconizedContextMenu {
1919
min-width: 146px;
2020
width: max-content;
21+
// override default ul styles
22+
margin: 0;
23+
padding: 0;
2124

2225
.mx_IconizedContextMenu_optionList {
2326
& > * {

src/components/structures/TabbedView.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ export default class TabbedView<T extends string> extends React.Component<IProps
121121
role="tab"
122122
aria-selected={isActive}
123123
aria-controls={id}
124+
element="li"
124125
>
125126
{tabIcon}
126127
<span className="mx_TabbedView_tabLabel_text" id={`${id}_label`}>
@@ -166,14 +167,14 @@ export default class TabbedView<T extends string> extends React.Component<IProps
166167
handleUpDown={this.props.tabLocation == TabLocation.LEFT}
167168
>
168169
{({ onKeyDownHandler }) => (
169-
<div
170+
<ul
170171
className="mx_TabbedView_tabLabels"
171172
role="tablist"
172173
aria-orientation={this.props.tabLocation == TabLocation.LEFT ? "vertical" : "horizontal"}
173174
onKeyDown={onKeyDownHandler}
174175
>
175176
{labels}
176-
</div>
177+
</ul>
177178
)}
178179
</RovingTabIndexProvider>
179180
{panel}

src/components/views/context_menus/IconizedContextMenu.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ export const IconizedContextMenuOption: React.FC<IOptionProps> = ({
126126
}) => {
127127
return (
128128
<MenuItem
129+
element="li"
129130
{...props}
130131
className={classNames(className, {
131132
mx_IconizedContextMenu_item: true,
@@ -171,7 +172,9 @@ const IconizedContextMenu: React.FC<React.PropsWithChildren<IProps>> = ({ classN
171172

172173
return (
173174
<ContextMenu chevronFace={ChevronFace.None} {...props}>
174-
<div className={classes}>{children}</div>
175+
<ul role="none" className={classes}>
176+
{children}
177+
</ul>
175178
</ContextMenu>
176179
);
177180
};

test/components/structures/__snapshots__/TabbedView-test.tsx.snap

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ exports[`<TabbedView /> renders tabs 1`] = `
55
<div
66
class="mx_TabbedView mx_TabbedView_tabsOnLeft"
77
>
8-
<div
8+
<ul
99
aria-orientation="vertical"
1010
class="mx_TabbedView_tabLabels"
1111
role="tablist"
1212
>
13-
<div
13+
<li
1414
aria-controls="mx_tabpanel_GENERAL"
1515
aria-selected="true"
1616
class="mx_AccessibleButton mx_TabbedView_tabLabel mx_TabbedView_tabLabel_active"
@@ -27,8 +27,8 @@ exports[`<TabbedView /> renders tabs 1`] = `
2727
>
2828
General
2929
</span>
30-
</div>
31-
<div
30+
</li>
31+
<li
3232
aria-controls="mx_tabpanel_LABS"
3333
aria-selected="false"
3434
class="mx_AccessibleButton mx_TabbedView_tabLabel"
@@ -45,8 +45,8 @@ exports[`<TabbedView /> renders tabs 1`] = `
4545
>
4646
Labs
4747
</span>
48-
</div>
49-
<div
48+
</li>
49+
<li
5050
aria-controls="mx_tabpanel_SECURITY"
5151
aria-selected="false"
5252
class="mx_AccessibleButton mx_TabbedView_tabLabel"
@@ -63,8 +63,8 @@ exports[`<TabbedView /> renders tabs 1`] = `
6363
>
6464
Security
6565
</span>
66-
</div>
67-
</div>
66+
</li>
67+
</ul>
6868
<div
6969
aria-labelledby="mx_tabpanel_GENERAL_label"
7070
class="mx_TabbedView_tabPanel"

test/components/views/context_menus/MessageContextMenu-test.tsx

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ describe("MessageContextMenu", () => {
9999

100100
createMenu(event, {}, {}, undefined, room);
101101

102-
expect(document.querySelector('div[aria-label="Pin"]')).toBeFalsy();
102+
expect(document.querySelector('li[aria-label="Pin"]')).toBeFalsy();
103103
});
104104

105105
it("does not show pin option for beacon_info event", () => {
@@ -111,7 +111,7 @@ describe("MessageContextMenu", () => {
111111

112112
createMenu(deadBeaconEvent, {}, {}, undefined, room);
113113

114-
expect(document.querySelector('div[aria-label="Pin"]')).toBeFalsy();
114+
expect(document.querySelector('li[aria-label="Pin"]')).toBeFalsy();
115115
});
116116

117117
it("does not show pin option when pinning feature is disabled", () => {
@@ -130,7 +130,7 @@ describe("MessageContextMenu", () => {
130130

131131
createMenu(pinnableEvent, {}, {}, undefined, room);
132132

133-
expect(document.querySelector('div[aria-label="Pin"]')).toBeFalsy();
133+
expect(document.querySelector('li[aria-label="Pin"]')).toBeFalsy();
134134
});
135135

136136
it("shows pin option when pinning feature is enabled", () => {
@@ -147,7 +147,7 @@ describe("MessageContextMenu", () => {
147147

148148
createMenu(pinnableEvent, {}, {}, undefined, room);
149149

150-
expect(document.querySelector('div[aria-label="Pin"]')).toBeTruthy();
150+
expect(document.querySelector('li[aria-label="Pin"]')).toBeTruthy();
151151
});
152152

153153
it("pins event on pin option click", () => {
@@ -171,7 +171,7 @@ describe("MessageContextMenu", () => {
171171

172172
createMenu(pinnableEvent, { onFinished }, {}, undefined, room);
173173

174-
fireEvent.click(document.querySelector('div[aria-label="Pin"]')!);
174+
fireEvent.click(document.querySelector('li[aria-label="Pin"]')!);
175175

176176
// added to account data
177177
expect(client.setRoomAccountData).toHaveBeenCalledWith(roomId, ReadPinsEventId, {
@@ -225,7 +225,7 @@ describe("MessageContextMenu", () => {
225225

226226
createMenu(pinnableEvent, {}, {}, undefined, room);
227227

228-
fireEvent.click(document.querySelector('div[aria-label="Unpin"]')!);
228+
fireEvent.click(document.querySelector('li[aria-label="Unpin"]')!);
229229

230230
expect(client.setRoomAccountData).not.toHaveBeenCalled();
231231

@@ -244,13 +244,13 @@ describe("MessageContextMenu", () => {
244244
it("allows forwarding a room message", () => {
245245
const eventContent = createMessageEventContent("hello");
246246
createMenuWithContent(eventContent);
247-
expect(document.querySelector('div[aria-label="Forward"]')).toBeTruthy();
247+
expect(document.querySelector('li[aria-label="Forward"]')).toBeTruthy();
248248
});
249249

250250
it("does not allow forwarding a poll", () => {
251251
const eventContent = PollStartEvent.from("why?", ["42"], M_POLL_KIND_DISCLOSED);
252252
createMenuWithContent(eventContent);
253-
expect(document.querySelector('div[aria-label="Forward"]')).toBeFalsy();
253+
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
254254
});
255255

256256
it("should not allow forwarding a voice broadcast", () => {
@@ -261,7 +261,7 @@ describe("MessageContextMenu", () => {
261261
"ABC123",
262262
);
263263
createMenu(broadcastStartEvent);
264-
expect(document.querySelector('div[aria-label="Forward"]')).toBeFalsy();
264+
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
265265
});
266266

267267
describe("forwarding beacons", () => {
@@ -273,7 +273,7 @@ describe("MessageContextMenu", () => {
273273
const beacons = new Map<BeaconIdentifier, Beacon>();
274274
beacons.set(getBeaconInfoIdentifier(deadBeaconEvent), beacon);
275275
createMenu(deadBeaconEvent, {}, {}, beacons);
276-
expect(document.querySelector('div[aria-label="Forward"]')).toBeFalsy();
276+
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
277277
});
278278

279279
it("does not allow forwarding a beacon that is not live but has a latestLocation", () => {
@@ -288,7 +288,7 @@ describe("MessageContextMenu", () => {
288288
const beacons = new Map<BeaconIdentifier, Beacon>();
289289
beacons.set(getBeaconInfoIdentifier(deadBeaconEvent), beacon);
290290
createMenu(deadBeaconEvent, {}, {}, beacons);
291-
expect(document.querySelector('div[aria-label="Forward"]')).toBeFalsy();
291+
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
292292
});
293293

294294
it("does not allow forwarding a live beacon that does not have a latestLocation", () => {
@@ -298,7 +298,7 @@ describe("MessageContextMenu", () => {
298298
const beacons = new Map<BeaconIdentifier, Beacon>();
299299
beacons.set(getBeaconInfoIdentifier(beaconEvent), beacon);
300300
createMenu(beaconEvent, {}, {}, beacons);
301-
expect(document.querySelector('div[aria-label="Forward"]')).toBeFalsy();
301+
expect(document.querySelector('li[aria-label="Forward"]')).toBeFalsy();
302302
});
303303

304304
it("allows forwarding a live beacon that has a location", () => {
@@ -313,7 +313,7 @@ describe("MessageContextMenu", () => {
313313
const beacons = new Map<BeaconIdentifier, Beacon>();
314314
beacons.set(getBeaconInfoIdentifier(liveBeaconEvent), beacon);
315315
createMenu(liveBeaconEvent, {}, {}, beacons);
316-
expect(document.querySelector('div[aria-label="Forward"]')).toBeTruthy();
316+
expect(document.querySelector('li[aria-label="Forward"]')).toBeTruthy();
317317
});
318318

319319
it("opens forward dialog with correct event", () => {
@@ -330,7 +330,7 @@ describe("MessageContextMenu", () => {
330330
beacons.set(getBeaconInfoIdentifier(liveBeaconEvent), beacon);
331331
createMenu(liveBeaconEvent, {}, {}, beacons);
332332

333-
fireEvent.click(document.querySelector('div[aria-label="Forward"]')!);
333+
fireEvent.click(document.querySelector('li[aria-label="Forward"]')!);
334334

335335
// called with forwardableEvent, not beaconInfo event
336336
expect(dispatchSpy).toHaveBeenCalledWith(
@@ -395,7 +395,7 @@ describe("MessageContextMenu", () => {
395395
mocked(getSelectedText).mockReturnValue(text);
396396

397397
createRightClickMenuWithContent(eventContent);
398-
const copyButton = document.querySelector('div[aria-label="Copy"]')!;
398+
const copyButton = document.querySelector('li[aria-label="Copy"]')!;
399399
fireEvent.mouseDown(copyButton);
400400
expect(copyPlaintext).toHaveBeenCalledWith(text);
401401
});
@@ -406,7 +406,7 @@ describe("MessageContextMenu", () => {
406406
mocked(getSelectedText).mockReturnValue("");
407407

408408
createRightClickMenuWithContent(eventContent);
409-
const copyButton = document.querySelector('div[aria-label="Copy"]');
409+
const copyButton = document.querySelector('li[aria-label="Copy"]');
410410
expect(copyButton).toBeFalsy();
411411
});
412412

@@ -415,7 +415,7 @@ describe("MessageContextMenu", () => {
415415
mocked(canEditContent).mockReturnValue(true);
416416

417417
createRightClickMenuWithContent(eventContent);
418-
const editButton = document.querySelector('div[aria-label="Edit"]');
418+
const editButton = document.querySelector('li[aria-label="Edit"]');
419419
expect(editButton).toBeTruthy();
420420
});
421421

@@ -424,7 +424,7 @@ describe("MessageContextMenu", () => {
424424
mocked(canEditContent).mockReturnValue(false);
425425

426426
createRightClickMenuWithContent(eventContent);
427-
const editButton = document.querySelector('div[aria-label="Edit"]');
427+
const editButton = document.querySelector('li[aria-label="Edit"]');
428428
expect(editButton).toBeFalsy();
429429
});
430430

@@ -435,7 +435,7 @@ describe("MessageContextMenu", () => {
435435
};
436436

437437
createRightClickMenuWithContent(eventContent, context);
438-
const replyButton = document.querySelector('div[aria-label="Reply"]');
438+
const replyButton = document.querySelector('li[aria-label="Reply"]');
439439
expect(replyButton).toBeTruthy();
440440
});
441441

@@ -449,7 +449,7 @@ describe("MessageContextMenu", () => {
449449
unsentMessage.setStatus(EventStatus.QUEUED);
450450

451451
createMenu(unsentMessage, {}, context);
452-
const replyButton = document.querySelector('div[aria-label="Reply"]');
452+
const replyButton = document.querySelector('li[aria-label="Reply"]');
453453
expect(replyButton).toBeFalsy();
454454
});
455455

@@ -460,7 +460,7 @@ describe("MessageContextMenu", () => {
460460
};
461461

462462
createRightClickMenuWithContent(eventContent, context);
463-
const reactButton = document.querySelector('div[aria-label="React"]');
463+
const reactButton = document.querySelector('li[aria-label="React"]');
464464
expect(reactButton).toBeTruthy();
465465
});
466466

@@ -471,7 +471,7 @@ describe("MessageContextMenu", () => {
471471
};
472472

473473
createRightClickMenuWithContent(eventContent, context);
474-
const reactButton = document.querySelector('div[aria-label="React"]');
474+
const reactButton = document.querySelector('li[aria-label="React"]');
475475
expect(reactButton).toBeFalsy();
476476
});
477477

@@ -487,15 +487,15 @@ describe("MessageContextMenu", () => {
487487
};
488488

489489
createMenu(mxEvent, props, context);
490-
const reactButton = document.querySelector('div[aria-label="View in room"]');
490+
const reactButton = document.querySelector('li[aria-label="View in room"]');
491491
expect(reactButton).toBeTruthy();
492492
});
493493

494494
it("does not show view in room button when the event is not a thread root", () => {
495495
const eventContent = createMessageEventContent("hello");
496496

497497
createRightClickMenuWithContent(eventContent);
498-
const reactButton = document.querySelector('div[aria-label="View in room"]');
498+
const reactButton = document.querySelector('li[aria-label="View in room"]');
499499
expect(reactButton).toBeFalsy();
500500
});
501501

@@ -511,7 +511,7 @@ describe("MessageContextMenu", () => {
511511

512512
createRightClickMenu(mxEvent, context);
513513

514-
const replyInThreadButton = document.querySelector('div[aria-label="Reply in thread"]')!;
514+
const replyInThreadButton = document.querySelector('li[aria-label="Reply in thread"]')!;
515515
fireEvent.click(replyInThreadButton);
516516

517517
expect(dispatcher.dispatch).toHaveBeenCalledWith({

test/components/views/context_menus/__snapshots__/RoomGeneralContextMenu-test.tsx.snap

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@ exports[`RoomGeneralContextMenu renders an empty context menu for archived rooms
1616
<div
1717
class="mx_ContextualMenu_chevron_left"
1818
/>
19-
<div
19+
<ul
2020
class="mx_IconizedContextMenu mx_RoomGeneralContextMenu mx_IconizedContextMenu_compact"
21+
role="none"
2122
>
2223
<div
2324
class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst"
2425
/>
2526
<div
2627
class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst mx_IconizedContextMenu_optionList_red"
2728
>
28-
<div
29+
<li
2930
aria-label="Forget Room"
3031
class="mx_AccessibleButton mx_IconizedContextMenu_option_red mx_IconizedContextMenu_item"
3132
role="menuitem"
@@ -39,9 +40,9 @@ exports[`RoomGeneralContextMenu renders an empty context menu for archived rooms
3940
>
4041
Forget Room
4142
</span>
42-
</div>
43+
</li>
4344
</div>
44-
</div>
45+
</ul>
4546
</div>
4647
</div>
4748
</div>
@@ -63,16 +64,17 @@ exports[`RoomGeneralContextMenu renders the default context menu 1`] = `
6364
<div
6465
class="mx_ContextualMenu_chevron_left"
6566
/>
66-
<div
67+
<ul
6768
class="mx_IconizedContextMenu mx_RoomGeneralContextMenu mx_IconizedContextMenu_compact"
69+
role="none"
6870
>
6971
<div
7072
class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst"
7173
/>
7274
<div
7375
class="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst mx_IconizedContextMenu_optionList_red"
7476
>
75-
<div
77+
<li
7678
aria-label="Forget Room"
7779
class="mx_AccessibleButton mx_IconizedContextMenu_option_red mx_IconizedContextMenu_item"
7880
role="menuitem"
@@ -86,9 +88,9 @@ exports[`RoomGeneralContextMenu renders the default context menu 1`] = `
8688
>
8789
Forget Room
8890
</span>
89-
</div>
91+
</li>
9092
</div>
91-
</div>
93+
</ul>
9294
</div>
9395
</div>
9496
</div>

0 commit comments

Comments
 (0)