mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
A11Y: Improve select-kit accessibility (#21400)
This improves keyboard navigation in and out of select-kit components. The improvements include: - `Tab` will now dismiss the dropdown once the active element is outside the select-kit element - pressing `Escape` will not bubble, this is most noticeable in the composer, pressing `Esc` there now when a dropdown is expanded will not dismiss the composer - `Shift+Tab` will also dismiss the dropdown once focus is outside it
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import Component from "@ember/component";
|
||||
import { bind } from "@ember/runloop";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { later } from "@ember/runloop";
|
||||
import { computed } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
@@ -10,55 +11,53 @@ export default Component.extend({
|
||||
return false;
|
||||
}),
|
||||
|
||||
rootEventType: "click",
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.handleRootMouseDownHandler = bind(this, this.handleRootMouseDown);
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.element.style.position = "relative";
|
||||
|
||||
document.addEventListener(
|
||||
this.rootEventType,
|
||||
this.handleRootMouseDownHandler,
|
||||
true
|
||||
);
|
||||
document.addEventListener("click", this.handleClick, true);
|
||||
this.selectKit
|
||||
.mainElement()
|
||||
.addEventListener("keydown", this._handleKeydown, true);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
document.removeEventListener(
|
||||
this.rootEventType,
|
||||
this.handleRootMouseDownHandler,
|
||||
true
|
||||
);
|
||||
document.removeEventListener("click", this.handleClick, true);
|
||||
this.selectKit
|
||||
.mainElement()
|
||||
.removeEventListener("keydown", this._handleKeydown, true);
|
||||
},
|
||||
|
||||
handleRootMouseDown(event) {
|
||||
if (!this.selectKit.isExpanded) {
|
||||
@bind
|
||||
handleClick(event) {
|
||||
if (!this.selectKit.isExpanded || !this.selectKit.mainElement()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const headerElement = document.querySelector(
|
||||
`#${this.selectKit.uniqueID}-header`
|
||||
);
|
||||
|
||||
if (headerElement && headerElement.contains(event.target)) {
|
||||
if (this.selectKit.mainElement().contains(event.target)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.element.contains(event.target)) {
|
||||
this.selectKit.close(event);
|
||||
},
|
||||
|
||||
@bind
|
||||
_handleKeydown(event) {
|
||||
if (!this.selectKit.isExpanded || event.key !== "Tab") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectKit.mainElement()) {
|
||||
later(() => {
|
||||
if (
|
||||
this.isDestroying ||
|
||||
this.isDestroyed ||
|
||||
this.selectKit.mainElement().contains(document.activeElement)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectKit.close(event);
|
||||
}
|
||||
}, 50);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -94,6 +94,8 @@ export default Component.extend(UtilsMixin, {
|
||||
if (event.key === "Escape") {
|
||||
this.selectKit.close(event);
|
||||
this.selectKit.headerElement().focus();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,6 @@ export default Component.extend(UtilsMixin, {
|
||||
if (!this.site.mobileView) {
|
||||
this.element.addEventListener("mouseenter", this.handleMouseEnter);
|
||||
this.element.addEventListener("focus", this.handleMouseEnter);
|
||||
this.element.addEventListener("blur", this.handleBlur);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -49,9 +48,8 @@ export default Component.extend(UtilsMixin, {
|
||||
this._super(...arguments);
|
||||
|
||||
if (!this.site.mobileView) {
|
||||
this.element.removeEventListener("mouseenter", this.handleBlur);
|
||||
this.element.removeEventListener("mouseenter", this.handleMouseEnter);
|
||||
this.element.removeEventListener("focus", this.handleMouseEnter);
|
||||
this.element.removeEventListener("blur", this.handleMouseEnter);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -134,20 +132,6 @@ export default Component.extend(UtilsMixin, {
|
||||
return false;
|
||||
},
|
||||
|
||||
@action
|
||||
handleBlur(event) {
|
||||
if (
|
||||
(!this.isDestroying || !this.isDestroyed) &&
|
||||
event.target &&
|
||||
this.selectKit.mainElement()
|
||||
) {
|
||||
if (!this.selectKit.mainElement().contains(event.target)) {
|
||||
this.selectKit.close(event);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
click(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@@ -193,6 +177,8 @@ export default Component.extend(UtilsMixin, {
|
||||
} else if (event.key === "Escape") {
|
||||
this.selectKit.close(event);
|
||||
this.selectKit.headerElement().focus();
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
} else {
|
||||
if (this.isValidInput(event.key)) {
|
||||
this.selectKit.set("filter", event.key);
|
||||
|
||||
Reference in New Issue
Block a user