Skip to content

Commit 5b44798

Browse files
authored
fix(menu-item): Improve keyboard navigability when href populated (#8408)
**Related Issue:** #8135 ## Summary
1 parent cd92586 commit 5b44798

File tree

3 files changed

+64
-31
lines changed

3 files changed

+64
-31
lines changed

packages/calcite-components/src/components/menu-item/menu-item.e2e.ts

+33
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,39 @@ describe("calcite-menu-item", () => {
5050
expect(eventSpy).toHaveReceivedEventTimes(1);
5151
});
5252

53+
describe("href support", () => {
54+
const testHref = "#nature";
55+
const testEl = `<calcite-menu><calcite-menu-item id="Nature" text="Nature" href="${testHref}"></calcite-menu-item></calcite-menu>`;
56+
57+
it("should navigate to a new url when href provided and user interacts with click", async () => {
58+
const page = await newE2EPage();
59+
await page.setContent(html`${testEl}`);
60+
const originalUrl = page.url();
61+
await page.waitForChanges();
62+
63+
const menuItem = await page.find("calcite-menu-item");
64+
await page.waitForChanges();
65+
await menuItem.click();
66+
await page.waitForChanges();
67+
const newUrl = page.url();
68+
expect(newUrl).toEqual(originalUrl + testHref);
69+
});
70+
71+
it("should navigate to a new url when href provided and user interacts with `enter` key", async () => {
72+
const page = await newE2EPage();
73+
await page.setContent(html`${testEl}`);
74+
const originalUrl = page.url();
75+
await page.waitForChanges();
76+
77+
await page.keyboard.press("Tab");
78+
await page.waitForChanges();
79+
await page.keyboard.press("Enter");
80+
await page.waitForChanges();
81+
const newUrl = page.url();
82+
expect(newUrl).toEqual(originalUrl + testHref);
83+
});
84+
});
85+
5386
it("should emit calciteMenuItemSelect event when user select the text area of the component using Enter or Space key", async () => {
5487
const page = await newE2EPage();
5588
await page.setContent(html`

packages/calcite-components/src/components/menu-item/menu-item.tsx

+24-31
Original file line numberDiff line numberDiff line change
@@ -266,61 +266,54 @@ export class CalciteMenuItem implements LoadableComponent, T9nComponent, Localiz
266266
};
267267

268268
private keyDownHandler = async (event: KeyboardEvent): Promise<void> => {
269-
// opening and closing of submenu is handled here. Any other functionality is bubbled to parent menu.
270-
if (event.key === " " || event.key === "Enter") {
271-
this.selectMenuItem(event);
272-
if (
273-
this.hasSubmenu &&
274-
(!this.href || (this.href && event.target === this.dropdownActionEl))
275-
) {
276-
this.open = !this.open;
269+
const { hasSubmenu, href, layout, open, submenuItems } = this;
270+
const key = event.key;
271+
const targetIsDropdown = event.target === this.dropdownActionEl;
272+
if (key === " " || key === "Enter") {
273+
if (hasSubmenu && (!href || (href && targetIsDropdown))) {
274+
this.open = !open;
277275
}
278-
event.preventDefault();
279-
} else if (event.key === "Escape") {
280-
if (this.open) {
276+
if (!(href && targetIsDropdown) && key !== "Enter") {
277+
this.selectMenuItem(event);
278+
}
279+
if (key === " " || (href && targetIsDropdown)) {
280+
event.preventDefault();
281+
}
282+
} else if (key === "Escape") {
283+
if (open) {
281284
this.open = false;
282285
return;
283286
}
284287
this.calciteInternalMenuItemKeyEvent.emit({ event });
285288
event.preventDefault();
286-
} else if (event.key === "ArrowDown" || event.key === "ArrowUp") {
289+
} else if (key === "ArrowDown" || key === "ArrowUp") {
287290
event.preventDefault();
288-
if (
289-
(event.target === this.dropdownActionEl || !this.href) &&
290-
this.hasSubmenu &&
291-
!this.open &&
292-
this.layout === "horizontal"
293-
) {
291+
if ((targetIsDropdown || !href) && hasSubmenu && !open && layout === "horizontal") {
294292
this.open = true;
295293
return;
296294
}
297295
this.calciteInternalMenuItemKeyEvent.emit({
298296
event,
299-
children: this.submenuItems,
300-
isSubmenuOpen: this.open && this.hasSubmenu,
297+
children: submenuItems,
298+
isSubmenuOpen: open && hasSubmenu,
301299
});
302-
} else if (event.key === "ArrowLeft") {
300+
} else if (key === "ArrowLeft") {
303301
event.preventDefault();
304302
this.calciteInternalMenuItemKeyEvent.emit({
305303
event,
306-
children: this.submenuItems,
304+
children: submenuItems,
307305
isSubmenuOpen: true,
308306
});
309-
} else if (event.key === "ArrowRight") {
307+
} else if (key === "ArrowRight") {
310308
event.preventDefault();
311-
if (
312-
(event.target === this.dropdownActionEl || !this.href) &&
313-
this.hasSubmenu &&
314-
!this.open &&
315-
this.layout === "vertical"
316-
) {
309+
if ((targetIsDropdown || !href) && hasSubmenu && !open && layout === "vertical") {
317310
this.open = true;
318311
return;
319312
}
320313
this.calciteInternalMenuItemKeyEvent.emit({
321314
event,
322-
children: this.submenuItems,
323-
isSubmenuOpen: this.open && this.hasSubmenu,
315+
children: submenuItems,
316+
isSubmenuOpen: open && hasSubmenu,
324317
});
325318
}
326319
};

packages/calcite-components/src/demos/menu.html

+7
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,11 @@ <h3>
440440
<calcite-panel heading="Example"><calcite-block heading="Content" open></calcite-block></calcite-panel>
441441
</calcite-shell>
442442
</body>
443+
<script>
444+
document.querySelectorAll("calcite-menu-item").forEach((item) => {
445+
item.addEventListener("calciteMenuItemSelect", (event) => {
446+
console.log(event);
447+
});
448+
});
449+
</script>
443450
</html>

0 commit comments

Comments
 (0)