A11Y: close sidebar "more..." menu on esc and focusOut
This commit is contained in:
parent
252dcfbfa6
commit
bffb9b25e3
|
@ -4,6 +4,7 @@ import { on } from "@ember/modifier";
|
|||
import { action } from "@ember/object";
|
||||
import didInsert from "@ember/render-modifiers/modifiers/did-insert";
|
||||
import willDestroy from "@ember/render-modifiers/modifiers/will-destroy";
|
||||
import { scheduleOnce } from "@ember/runloop";
|
||||
import { service } from "@ember/service";
|
||||
import { isEmpty } from "@ember/utils";
|
||||
import icon from "discourse-common/helpers/d-icon";
|
||||
|
@ -11,13 +12,14 @@ import i18n from "discourse-common/helpers/i18n";
|
|||
import { bind } from "discourse-common/utils/decorators";
|
||||
import MoreSectionLink from "./more-section-link";
|
||||
import SectionLinkButton from "./section-link-button";
|
||||
|
||||
export default class SidebarMoreSectionLinks extends Component {
|
||||
@service router;
|
||||
|
||||
@tracked activeSectionLink;
|
||||
@tracked open = false;
|
||||
|
||||
#lastFocusEvent;
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.#setActiveSectionLink();
|
||||
|
@ -26,7 +28,7 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||
|
||||
willDestroy() {
|
||||
super.willDestroy(...arguments);
|
||||
this.#removeClickEventListener();
|
||||
this.#removeEventListeners();
|
||||
this.router.off("routeDidChange", this, this.#setActiveSectionLink);
|
||||
}
|
||||
|
||||
|
@ -69,28 +71,75 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
registerClickListener() {
|
||||
this.#addClickEventListener();
|
||||
@bind
|
||||
closeOnEscape(event) {
|
||||
if (event.key === "Escape" && this.open) {
|
||||
this.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
unregisterClickListener() {
|
||||
this.#removeClickEventListener();
|
||||
@bind
|
||||
closeOnFocusOut(event) {
|
||||
this.#lastFocusEvent = event;
|
||||
scheduleOnce("afterRender", this, this.#handleFocusOut);
|
||||
}
|
||||
|
||||
#handleFocusOut() {
|
||||
const container = document.querySelector(
|
||||
".sidebar-more-section-links-details-content"
|
||||
);
|
||||
const toggleButton = document.querySelector(
|
||||
".sidebar-more-section-links-details-summary"
|
||||
);
|
||||
|
||||
if (
|
||||
container &&
|
||||
!container.contains(this.#lastFocusEvent.relatedTarget) &&
|
||||
toggleButton !== this.#lastFocusEvent.relatedTarget // focusing out to the toggle shouldn't close the menu
|
||||
) {
|
||||
this.open = false;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSectionLinks(event) {
|
||||
event.stopPropagation();
|
||||
this.open = !this.open;
|
||||
|
||||
if (this.open) {
|
||||
scheduleOnce("afterRender", this, this.#focusFirstLink);
|
||||
}
|
||||
}
|
||||
|
||||
#removeClickEventListener() {
|
||||
document.removeEventListener("click", this.closeDetails);
|
||||
#focusFirstLink() {
|
||||
const firstLink = document.querySelector(
|
||||
".sidebar-more-section-links-details-content-wrapper a"
|
||||
);
|
||||
if (firstLink) {
|
||||
firstLink.focus();
|
||||
}
|
||||
}
|
||||
|
||||
#addClickEventListener() {
|
||||
#addEventListeners() {
|
||||
document.addEventListener("click", this.closeDetails);
|
||||
document.addEventListener("keydown", this.closeOnEscape);
|
||||
document.addEventListener("focusout", this.closeOnFocusOut);
|
||||
}
|
||||
|
||||
#removeEventListeners() {
|
||||
document.removeEventListener("click", this.closeDetails);
|
||||
document.removeEventListener("keydown", this.closeOnEscape);
|
||||
document.removeEventListener("focusout", this.closeOnFocusOut);
|
||||
}
|
||||
|
||||
@action
|
||||
registerEventListeners() {
|
||||
this.#addEventListeners();
|
||||
}
|
||||
|
||||
@action
|
||||
unregisterEventListeners() {
|
||||
this.#removeEventListeners();
|
||||
}
|
||||
|
||||
#isOutsideDetailsClick(event) {
|
||||
|
@ -140,8 +189,8 @@ export default class SidebarMoreSectionLinks extends Component {
|
|||
{{#if this.open}}
|
||||
<div class="sidebar-more-section-links-details">
|
||||
<div
|
||||
{{didInsert this.registerClickListener}}
|
||||
{{willDestroy this.unregisterClickListener}}
|
||||
{{didInsert this.registerEventListeners}}
|
||||
{{willDestroy this.unregisterEventListeners}}
|
||||
class="sidebar-more-section-links-details-content-wrapper"
|
||||
>
|
||||
|
||||
|
|
|
@ -90,13 +90,6 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
|
|||
),
|
||||
"additional section links are hidden when clicking outside"
|
||||
);
|
||||
|
||||
assert.ok(
|
||||
exists(
|
||||
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary[aria-expanded='false']"
|
||||
),
|
||||
"aria-expanded toggles to false when additional links are hidden"
|
||||
);
|
||||
});
|
||||
|
||||
test("clicking on everything link", async function (assert) {
|
||||
|
@ -1134,6 +1127,47 @@ acceptance("Sidebar - Logged on user - Community Section", function (needs) {
|
|||
|
||||
assert.ok(teardownCalled, "section link teardown callback was called");
|
||||
});
|
||||
|
||||
test("pressing esc closes the more menu", async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
await click(
|
||||
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
|
||||
);
|
||||
|
||||
assert.ok(exists(".sidebar-more-section-links-details-content"));
|
||||
|
||||
const event = new KeyboardEvent("keydown", { key: "Escape" });
|
||||
document.dispatchEvent(event);
|
||||
|
||||
assert.notOk(exists(".sidebar-more-section-links-details-content"));
|
||||
|
||||
assert.strictEqual(
|
||||
query(
|
||||
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
|
||||
).getAttribute("aria-expanded"),
|
||||
"false",
|
||||
"aria-expanded is set to false after closing the menu"
|
||||
);
|
||||
});
|
||||
|
||||
test("first link is focused when the more menu is opened", async function (assert) {
|
||||
await visit("/");
|
||||
|
||||
await click(
|
||||
".sidebar-section[data-section-name='community'] .sidebar-more-section-links-details-summary"
|
||||
);
|
||||
|
||||
const firstLink = query(
|
||||
".sidebar-more-section-links-details-content-wrapper a"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
document.activeElement,
|
||||
firstLink,
|
||||
"first link is focused"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
acceptance(
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3849f0fd3d6f7916de26d43e3a3c9f8e6615bc8f
|
Loading…
Reference in New Issue