-
-
Notifications
You must be signed in to change notification settings - Fork 424
fix(core/ui): add aria-label and arrow key menu navigation #5243
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a471bf3
39c2529
a5e5158
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ const menu = html`<ul | |
| ></ul>`; | ||
| const closeButton = html`<button | ||
| class="close-button" | ||
| aria-label="Close" | ||
| onclick=${() => ui.closeModal()} | ||
| title="Close" | ||
| > | ||
|
|
@@ -59,6 +60,8 @@ window.addEventListener("load", () => trapFocus(menu)); | |
| let modal; | ||
| /** @type {HTMLElement | null} */ | ||
| let overlay; | ||
| /** @type {HTMLElement | null} */ | ||
| let currentModalOwner; | ||
| /** @type {any[]} */ | ||
| const errors = []; | ||
| /** @type {any[]} */ | ||
|
|
@@ -77,14 +80,15 @@ respecPill.addEventListener( | |
| e.stopPropagation(); | ||
| respecPill.setAttribute("aria-expanded", String(menu.hidden)); | ||
| toggleMenu(); | ||
| menu.querySelector("li:first-child button").focus(); | ||
| menu.querySelector("li:first-child button")?.focus(); | ||
| } | ||
| ); | ||
|
|
||
| document.documentElement.addEventListener("click", () => { | ||
| if (!menu.hidden) { | ||
| toggleMenu(); | ||
| } | ||
| respecPill.setAttribute("aria-expanded", "false"); | ||
| }); | ||
| respecUI.appendChild(menu); | ||
|
|
||
|
|
@@ -95,6 +99,35 @@ menu.addEventListener( | |
| respecPill.setAttribute("aria-expanded", String(menu.hidden)); | ||
| toggleMenu(); | ||
| respecPill.focus(); | ||
| return; | ||
|
Comment on lines
99
to
+102
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Already handled at line 91: the document-level click handler unconditionally sets |
||
| } | ||
| const items = /** @type {HTMLElement[]} */ ([ | ||
| ...menu.querySelectorAll("button:not([disabled])"), | ||
| ]); | ||
| const currentIndex = items.indexOf( | ||
| /** @type {HTMLElement} */ (document.activeElement) | ||
| ); | ||
| switch (e.key) { | ||
| case "ArrowDown": { | ||
| e.preventDefault(); | ||
| const next = items[(currentIndex + 1) % items.length]; | ||
| next?.focus(); | ||
| break; | ||
| } | ||
| case "ArrowUp": { | ||
| e.preventDefault(); | ||
| const prev = items[(currentIndex - 1 + items.length) % items.length]; | ||
| prev?.focus(); | ||
| break; | ||
| } | ||
| case "Home": | ||
| e.preventDefault(); | ||
| items[0]?.focus(); | ||
| break; | ||
| case "End": | ||
| e.preventDefault(); | ||
| items[items.length - 1]?.focus(); | ||
|
Comment on lines
+104
to
+129
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Already covered: see the |
||
| break; | ||
| } | ||
| } | ||
| ); | ||
|
|
@@ -247,6 +280,7 @@ export const ui = { | |
| }, | ||
| /** @param {Element} [owner] */ | ||
| closeModal(owner) { | ||
| const effectiveOwner = owner || currentModalOwner; | ||
| if (overlay) { | ||
| const overlayElem = overlay; | ||
| overlayElem.classList.remove("respec-show-overlay"); | ||
|
|
@@ -256,12 +290,13 @@ export const ui = { | |
| overlay = null; | ||
| }); | ||
| } | ||
| if (owner) { | ||
| owner.setAttribute("aria-expanded", "false"); | ||
| if (effectiveOwner) { | ||
| effectiveOwner.setAttribute("aria-expanded", "false"); | ||
| } | ||
| if (!modal) return; | ||
| modal.remove(); | ||
| modal = null; | ||
| currentModalOwner = null; | ||
| respecPill.focus(); | ||
| }, | ||
| /** | ||
|
|
@@ -272,6 +307,10 @@ export const ui = { | |
| freshModal(title, content, currentOwner) { | ||
| if (modal) modal.remove(); | ||
| if (overlay) overlay.remove(); | ||
| if (currentModalOwner && currentModalOwner !== currentOwner) { | ||
| currentModalOwner.setAttribute("aria-expanded", "false"); | ||
| } | ||
| currentModalOwner = currentOwner; | ||
| overlay = html`<div id="respec-overlay" class="removeOnSave"></div>`; | ||
| const id = `${currentOwner.id}-modal`; | ||
| const headingId = `${id}-heading`; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
currentModalOwnervariable (line 64) is set infreshModal()(line 313) when a modal opens.closeModal()at line 283 usesowner || currentModalOwneras the effective owner, so callingcloseModal()without an argument correctly resets the opener'saria-expandedvia the fallback.