mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 16:46:12 +08:00
A11Y: select kit close on focus out (#21274)
When navigating with the keyboard, the select-kit would not close when focus was moved to an element outside of the body. For example, when navigating via Tab or Shift+Tab, once the end (or beginning) of the list was reached, focus would move out of the SK element, but the SK itself would stay visible. Switching from a click event to a focusout event solves the issue and covers both mouse and keyboard navigation.
This commit is contained in:
parent
bfd3bd5516
commit
1b2a1c94d4
|
@ -113,10 +113,12 @@ acceptance("Category Edit", function (needs) {
|
|||
const allowedTagChooser = selectKit("#category-allowed-tags");
|
||||
await allowedTagChooser.expand();
|
||||
await allowedTagChooser.selectRowByValue("monkey");
|
||||
await allowedTagChooser.collapse();
|
||||
|
||||
const allowedTagGroupChooser = selectKit("#category-allowed-tag-groups");
|
||||
await allowedTagGroupChooser.expand();
|
||||
await allowedTagGroupChooser.selectRowByValue("TagGroup1");
|
||||
await allowedTagGroupChooser.collapse();
|
||||
|
||||
await click("#save-category");
|
||||
|
||||
|
@ -129,6 +131,7 @@ acceptance("Category Edit", function (needs) {
|
|||
|
||||
await allowedTagChooser.expand();
|
||||
await allowedTagChooser.deselectItemByValue("monkey");
|
||||
await allowedTagChooser.collapse();
|
||||
|
||||
await allowedTagGroupChooser.expand();
|
||||
await allowedTagGroupChooser.deselectItemByValue("TagGroup1");
|
||||
|
|
|
@ -39,10 +39,16 @@ acceptance("User Preferences - Categories", function (needs) {
|
|||
".tracking-controls__regular-categories .category-selector"
|
||||
);
|
||||
|
||||
await trackedCategoriesSelector.collapse();
|
||||
|
||||
await regularCategoriesSelector.expand();
|
||||
await regularCategoriesSelector.deselectItemByValue("4");
|
||||
|
||||
await regularCategoriesSelector.collapse();
|
||||
await trackedCategoriesSelector.expand();
|
||||
await trackedCategoriesSelector.selectRowByValue("4");
|
||||
await trackedCategoriesSelector.collapse();
|
||||
|
||||
await click(".save-changes");
|
||||
|
||||
assert.deepEqual(putRequestData, {
|
||||
|
@ -63,6 +69,7 @@ acceptance("User Preferences - Categories", function (needs) {
|
|||
await categorySelector.expand();
|
||||
// User has `regular_category_ids` set to [4] in fixtures
|
||||
await categorySelector.selectRowByValue(4);
|
||||
await categorySelector.collapse();
|
||||
await click(".save-changes");
|
||||
|
||||
assert.deepEqual(putRequestData, {
|
||||
|
|
|
@ -173,17 +173,25 @@ acceptance("User Preferences - Tracking", function (needs) {
|
|||
|
||||
assert.notOk(
|
||||
trackedCategoriesSelector.rowByValue("4").exists(),
|
||||
"category that is set to regular is not available for selection"
|
||||
"category that is set to regular is not available for selection under tracked"
|
||||
);
|
||||
|
||||
const regularCategoriesSelector = selectKit(
|
||||
".tracking-controls__regular-categories .category-selector"
|
||||
);
|
||||
|
||||
await trackedCategoriesSelector.collapse();
|
||||
await regularCategoriesSelector.expand();
|
||||
await regularCategoriesSelector.deselectItemByValue("4");
|
||||
|
||||
assert.ok(
|
||||
regularCategoriesSelector.rowByValue("4").exists(),
|
||||
"category is no longer selected under regular"
|
||||
);
|
||||
|
||||
await regularCategoriesSelector.collapse();
|
||||
await trackedCategoriesSelector.expand();
|
||||
await trackedCategoriesSelector.selectRowByValue("4");
|
||||
await trackedCategoriesSelector.collapse();
|
||||
await click(".save-changes");
|
||||
|
||||
assert.deepEqual(putRequestData, {
|
||||
|
|
|
@ -58,9 +58,8 @@ module(
|
|||
await this.subject.fillInFilter("baz");
|
||||
await this.subject.selectRowByValue("monkey");
|
||||
|
||||
const error = query(".select-kit-error").innerText;
|
||||
assert.strictEqual(
|
||||
error,
|
||||
query(".select-kit-error").innerText,
|
||||
I18n.t("select_kit.max_content_reached", {
|
||||
count: this.siteSettings.max_tags_per_topic,
|
||||
})
|
||||
|
|
|
@ -75,6 +75,7 @@ export default SelectKitComponent.extend({
|
|||
},
|
||||
|
||||
select(value, item) {
|
||||
this.selectKit.set("multiSelectInFocus", true);
|
||||
if (this.selectKit.hasSelection && this.selectKit.options.maximum === 1) {
|
||||
this.selectKit.deselectByValue(
|
||||
this.getValue(this.selectedContent.firstObject)
|
||||
|
|
|
@ -94,6 +94,7 @@ export default Component.extend(
|
|||
noneItem: null,
|
||||
newItem: null,
|
||||
filter: null,
|
||||
multiSelectInFocus: null,
|
||||
|
||||
modifyContent: bind(this, this._modifyContentWrapper),
|
||||
modifySelection: bind(this, this._modifySelectionWrapper),
|
||||
|
@ -441,6 +442,7 @@ export default Component.extend(
|
|||
items = makeArray(items);
|
||||
|
||||
if (this.multiSelect) {
|
||||
this.selectKit.set("multiSelectInFocus", true);
|
||||
items = items.filter(
|
||||
(i) =>
|
||||
i !== this.newItem &&
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Component from "@ember/component";
|
||||
import { bind } from "@ember/runloop";
|
||||
import { bind } from "discourse-common/utils/decorators";
|
||||
import { computed } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
|
@ -10,55 +10,41 @@ 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
|
||||
);
|
||||
this.element.addEventListener("focusout", this._handleFocusOut, true);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
document.removeEventListener(
|
||||
this.rootEventType,
|
||||
this.handleRootMouseDownHandler,
|
||||
true
|
||||
);
|
||||
this.element.removeEventListener("focusout", this._handleFocusOut, true);
|
||||
},
|
||||
|
||||
handleRootMouseDown(event) {
|
||||
@bind
|
||||
_handleFocusOut(event) {
|
||||
if (!this.selectKit.isExpanded) {
|
||||
return;
|
||||
}
|
||||
|
||||
const headerElement = document.querySelector(
|
||||
`#${this.selectKit.uniqueID}-header`
|
||||
);
|
||||
|
||||
if (headerElement && headerElement.contains(event.target)) {
|
||||
if (!this.selectKit.mainElement()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.element.contains(event.target)) {
|
||||
if (this.selectKit.mainElement().contains(event.relatedTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.selectKit.mainElement()) {
|
||||
this.selectKit.close(event);
|
||||
// We have to use a custom flag for multi-selects to keep UI visible.
|
||||
// We can't rely on event.relatedTarget for these cases because,
|
||||
// when adding/removing items in a multi-select, the DOM element
|
||||
// has already been removed by this point, and therefore
|
||||
// event.relatedTarget is going to be null.
|
||||
if (this.selectKit.multiSelectInFocus) {
|
||||
this.selectKit.set("multiSelectInFocus", false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectKit.close(event);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -122,7 +122,10 @@ export default Component.extend(UtilsMixin, {
|
|||
event.stopPropagation();
|
||||
event.preventDefault(); // prevents the space to trigger a scroll page-next
|
||||
this.selectKit.open(event);
|
||||
} else if (event.key === "Escape") {
|
||||
} else if (
|
||||
event.key === "Escape" ||
|
||||
(event.shiftKey && event.key === "Tab")
|
||||
) {
|
||||
event.stopPropagation();
|
||||
if (this.selectKit.isExpanded) {
|
||||
this.selectKit.close(event);
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue
Block a user