mirror of
https://github.com/discourse/discourse.git
synced 2025-02-16 17:52:45 +08:00
![Keegan George](/assets/img/avatar_default.png)
This PR makes some updates to the prior keyboard accessibility commit (eb98746
):
- Makes `tabindex` attribute only appear on emoji markup in the emoji picker.
- After pressing the Esc key, focus returns to the <textarea/> input (composer editor or chat input)
278 lines
9.4 KiB
JavaScript
278 lines
9.4 KiB
JavaScript
import {
|
|
acceptance,
|
|
count,
|
|
exists,
|
|
query,
|
|
queryAll,
|
|
} from "discourse/tests/helpers/qunit-helpers";
|
|
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
|
import { test } from "qunit";
|
|
|
|
acceptance("EmojiPicker", function (needs) {
|
|
needs.user();
|
|
|
|
needs.hooks.beforeEach(function () {
|
|
this.emojiStore = this.container.lookup("service:emoji-store");
|
|
this.emojiStore.reset();
|
|
});
|
|
needs.hooks.afterEach(function () {
|
|
this.emojiStore.reset();
|
|
});
|
|
|
|
test("emoji picker can be opened/closed", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
|
|
await click("button.emoji.btn");
|
|
assert.ok(exists(".emoji-picker.opened"), "it opens the picker");
|
|
|
|
await click("button.emoji.btn");
|
|
assert.notOk(exists(".emoji-picker.opened"), "it closes the picker");
|
|
});
|
|
|
|
test("filters emoji", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await fillIn(".emoji-picker input.filter", "guitar");
|
|
|
|
assert.strictEqual(query(`.emoji-picker .results img`).title, "guitar");
|
|
});
|
|
|
|
test("emoji picker triggers event when picking emoji", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await click(".emoji-picker-emoji-area img.emoji[title='grinning']");
|
|
|
|
assert.strictEqual(
|
|
query(".d-editor-input").value,
|
|
":grinning:",
|
|
"it adds the emoji code in the editor when selected"
|
|
);
|
|
});
|
|
|
|
test("emoji picker adds leading whitespace before emoji", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
|
|
// Whitespace should be added on text
|
|
await fillIn(".d-editor-input", "This is a test input");
|
|
await click("button.emoji.btn");
|
|
await click(".emoji-picker-emoji-area img.emoji[title='grinning']");
|
|
assert.strictEqual(
|
|
query(".d-editor-input").value,
|
|
"This is a test input :grinning:",
|
|
"it adds the emoji code and a leading whitespace when there is text"
|
|
);
|
|
|
|
// Whitespace should not be added on whitespace
|
|
await fillIn(".d-editor-input", "This is a test input ");
|
|
await click(".emoji-picker-emoji-area img.emoji[title='grinning']");
|
|
|
|
assert.strictEqual(
|
|
query(".d-editor-input").value,
|
|
"This is a test input :grinning:",
|
|
"it adds the emoji code and no leading whitespace when user already entered whitespace"
|
|
);
|
|
});
|
|
|
|
test("emoji picker has a list of recently used emojis", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await click(".emoji-picker-emoji-area img.emoji[title='grinning']");
|
|
|
|
assert.ok(
|
|
exists(
|
|
".emoji-picker .section.recent .section-group img.emoji[title='grinning']"
|
|
),
|
|
"it shows recent selected emoji"
|
|
);
|
|
|
|
assert.ok(
|
|
exists('.emoji-picker .category-button[data-section="recent"]'),
|
|
"it shows recent category icon"
|
|
);
|
|
|
|
await click(".emoji-picker .trash-recent");
|
|
|
|
assert.notOk(
|
|
exists(
|
|
".emoji-picker .section.recent .section-group img.emoji[title='grinning']"
|
|
),
|
|
"it has cleared recent emojis"
|
|
);
|
|
|
|
assert.notOk(
|
|
exists('.emoji-picker .section[data-section="recent"]'),
|
|
"it hides recent section"
|
|
);
|
|
|
|
assert.notOk(
|
|
exists('.emoji-picker .category-button[data-section="recent"]'),
|
|
"it hides recent category icon"
|
|
);
|
|
});
|
|
|
|
test("emoji picker correctly orders recently used emojis", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await click(".emoji-picker-emoji-area img.emoji[title='sunglasses']");
|
|
await click(".emoji-picker-emoji-area img.emoji[title='grinning']");
|
|
|
|
assert.strictEqual(
|
|
count('.section[data-section="recent"] .section-group img.emoji'),
|
|
2,
|
|
"it has multiple recent emojis"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
/grinning/.test(
|
|
query(".section.recent .section-group img.emoji").getAttribute("src")
|
|
),
|
|
true,
|
|
"it puts the last used emoji in first"
|
|
);
|
|
});
|
|
|
|
test("updates the recent list when selecting from it (after you close re-open it or select other emoji)", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await click(`.emoji-picker-emoji-area img.emoji[title="sunglasses"]`);
|
|
await click(`.emoji-picker-emoji-area img.emoji[title="grinning"]`);
|
|
|
|
let recent = queryAll(".section.recent .section-group img.emoji");
|
|
assert.strictEqual(recent[0].title, "grinning");
|
|
assert.strictEqual(recent[1].title, "sunglasses");
|
|
|
|
await click(
|
|
`.section[data-section="recent"] .section-group img.emoji[title="sunglasses"]`
|
|
);
|
|
|
|
// The order is still the same
|
|
recent = queryAll(".section.recent .section-group img.emoji");
|
|
assert.strictEqual(recent[0].title, "grinning");
|
|
assert.strictEqual(recent[1].title, "sunglasses");
|
|
|
|
await click("button.emoji.btn");
|
|
await click("button.emoji.btn");
|
|
|
|
// but updates when you re-open
|
|
recent = queryAll(".section.recent .section-group img.emoji");
|
|
assert.strictEqual(recent[0].title, "sunglasses");
|
|
assert.strictEqual(recent[1].title, "grinning");
|
|
});
|
|
|
|
test("emoji picker persists state", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await click(".emoji-picker button.diversity-scale.medium-dark");
|
|
await click("button.emoji.btn");
|
|
await click("button.emoji.btn");
|
|
|
|
assert.ok(
|
|
exists(".emoji-picker button.diversity-scale.medium-dark .d-icon"),
|
|
"it stores diversity scale"
|
|
);
|
|
});
|
|
|
|
test("emoji can be selected with keyboard", async function (assert) {
|
|
const searchInput = ".emoji-picker-search-container input";
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click(".emoji.btn");
|
|
|
|
let emojis = document.querySelectorAll(
|
|
".emoji-picker-emoji-area img.emoji"
|
|
);
|
|
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
document.querySelector(searchInput),
|
|
"search input is focused by default"
|
|
);
|
|
|
|
await triggerKeyEvent(searchInput, "keydown", "ArrowDown");
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
emojis[0],
|
|
"ArrowDown from search focuses on the first emoji result"
|
|
);
|
|
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowRight");
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
emojis[1],
|
|
"ArrowRight from first emoji focuses on the second emoji"
|
|
);
|
|
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowLeft");
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
emojis[0],
|
|
"ArrowLeft from second emoji focuses on the first emoji"
|
|
);
|
|
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowRight");
|
|
await triggerKeyEvent(document.activeElement, "keydown", "Enter");
|
|
assert.strictEqual(
|
|
document.querySelector(".d-editor-input").value,
|
|
":smiley:",
|
|
"Pressing enter inserts the emoji markup in the composer"
|
|
);
|
|
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click(".emoji.btn");
|
|
await triggerKeyEvent(searchInput, "keydown", "ArrowDown");
|
|
emojis = document.querySelectorAll(".emoji-picker-emoji-area img.emoji");
|
|
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
document.querySelector(".emoji-picker-emoji-area .emoji.recent-emoji"),
|
|
"ArrowDown focuses on the first emoji result (recent emoji)"
|
|
);
|
|
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowDown");
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
document.querySelector(".emojis-container .emoji[title='grinning']"),
|
|
"ArrowDown again focuses on the first emoji result in a section"
|
|
);
|
|
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowRight");
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowRight");
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowRight");
|
|
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
emojis[4],
|
|
"ArrowRight moves focus to next right element"
|
|
);
|
|
|
|
await triggerKeyEvent(document.activeElement, "keydown", "ArrowUp");
|
|
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
document.querySelector(searchInput),
|
|
"ArrowUp from first row items moves focus to input"
|
|
);
|
|
});
|
|
|
|
test("emoji picker can be dismissed with escape key", async function (assert) {
|
|
await visit("/t/internationalization-localization/280");
|
|
await click("#topic-footer-buttons .btn.create");
|
|
await click("button.emoji.btn");
|
|
await triggerKeyEvent(document.activeElement, "keydown", "Escape");
|
|
assert.notOk(exists(".emoji-picker"));
|
|
assert.strictEqual(
|
|
document.activeElement,
|
|
document.querySelector("textarea"),
|
|
"escaping from emoji picker focuses back on input"
|
|
);
|
|
});
|
|
});
|