mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 18:13:38 +08:00
DEV: Emoji picker keyboard accessibility updates (#18331)
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)
This commit is contained in:
parent
998bd191a5
commit
a23d19fab0
|
@ -101,6 +101,7 @@ export default Component.extend({
|
|||
if (!emojiPicker) {
|
||||
return;
|
||||
}
|
||||
|
||||
const popperAnchor = this._getPopperAnchor();
|
||||
|
||||
if (!this.site.isMobileDevice && this.usePopper && popperAnchor) {
|
||||
|
@ -269,6 +270,24 @@ export default Component.extend({
|
|||
|
||||
if (event.key === "Escape") {
|
||||
this.onClose(event);
|
||||
const path = event.path || (event.composedPath && event.composedPath());
|
||||
|
||||
const fromChatComposer = path.find((e) =>
|
||||
e?.classList?.contains("chat-composer-container")
|
||||
);
|
||||
|
||||
const fromTopicComposer = path.find((e) =>
|
||||
e?.classList?.contains("d-editor")
|
||||
);
|
||||
|
||||
if (fromTopicComposer) {
|
||||
document.querySelector(".d-editor-input")?.focus();
|
||||
} else if (fromChatComposer) {
|
||||
document.querySelector(".chat-composer-input")?.focus();
|
||||
} else {
|
||||
document.querySelector("textarea")?.focus();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -384,6 +403,7 @@ export default Component.extend({
|
|||
_replaceEmoji(code) {
|
||||
const escaped = emojiUnescape(`:${escapeExpression(code)}:`, {
|
||||
lazy: true,
|
||||
tabIndex: "0",
|
||||
});
|
||||
return htmlSafe(escaped);
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -37,7 +37,7 @@
|
|||
</div>
|
||||
<div class="section-group">
|
||||
{{#each this.recentEmojis as |emoji|}}
|
||||
{{replace-emoji (concat ":" emoji ":") (hash lazy=true class="recent-emoji")}}
|
||||
{{replace-emoji (concat ":" emoji ":") (hash lazy=true tabIndex="0" class="recent-emoji")}}
|
||||
{{/each}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -268,5 +268,10 @@ acceptance("EmojiPicker", function (needs) {
|
|||
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"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -32,12 +32,12 @@ discourseModule("Unit | Utility | emoji", function () {
|
|||
);
|
||||
testUnescape(
|
||||
"emoticons :)",
|
||||
`emoticons <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji' tabindex='0'>`,
|
||||
`emoticons <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'>`,
|
||||
"emoticons are still supported"
|
||||
);
|
||||
testUnescape(
|
||||
"With emoji :O: :frog: :smile:",
|
||||
`With emoji <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/o.png?v=${v}' title='O' alt='O' class='emoji' tabindex='0'> <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/frog.png?v=${v}' title='frog' alt='frog' class='emoji' tabindex='0'> <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||
`With emoji <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/o.png?v=${v}' title='O' alt='O' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/frog.png?v=${v}' title='frog' alt='frog' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"title with emoji"
|
||||
);
|
||||
testUnescape(
|
||||
|
@ -47,27 +47,27 @@ discourseModule("Unit | Utility | emoji", function () {
|
|||
);
|
||||
testUnescape(
|
||||
"(:frog:) :)",
|
||||
`(<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/frog.png?v=${v}' title='frog' alt='frog' class='emoji' tabindex='0'>) <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji' tabindex='0'>`,
|
||||
`(<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/frog.png?v=${v}' title='frog' alt='frog' class='emoji'>) <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'>`,
|
||||
"non-word characters allowed next to emoji"
|
||||
);
|
||||
testUnescape(
|
||||
":smile: hi",
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'> hi`,
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> hi`,
|
||||
"start of line"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :smile:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"end of line"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_woman:t4:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji' tabindex='0'>`,
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji'>`,
|
||||
"support for skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
"hi :blonde_woman:t4: :blonde_man:t6:",
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji' tabindex='0'> <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blonde_man/6.png?v=${v}' title='blonde_man:t6' alt='blonde_man:t6' class='emoji' tabindex='0'>`,
|
||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blonde_woman/4.png?v=${v}' title='blonde_woman:t4' alt='blonde_woman:t4' class='emoji'> <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blonde_man/6.png?v=${v}' title='blonde_man:t6' alt='blonde_man:t6' class='emoji'>`,
|
||||
"support for multiple skin tones"
|
||||
);
|
||||
testUnescape(
|
||||
|
@ -95,7 +95,7 @@ discourseModule("Unit | Utility | emoji", function () {
|
|||
);
|
||||
testUnescape(
|
||||
"Hello 😊 World",
|
||||
`Hello <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blush.png?v=${v}' title='blush' alt='blush' class='emoji' tabindex='0'> World`,
|
||||
`Hello <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blush.png?v=${v}' title='blush' alt='blush' class='emoji'> World`,
|
||||
"emoji from Unicode emoji"
|
||||
);
|
||||
testUnescape(
|
||||
|
@ -108,7 +108,7 @@ discourseModule("Unit | Utility | emoji", function () {
|
|||
);
|
||||
testUnescape(
|
||||
"Hello😊World",
|
||||
`Hello<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blush.png?v=${v}' title='blush' alt='blush' class='emoji' tabindex='0'>World`,
|
||||
`Hello<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blush.png?v=${v}' title='blush' alt='blush' class='emoji'>World`,
|
||||
"emoji from Unicode emoji when inline translation enabled",
|
||||
{
|
||||
enable_inline_emoji_translation: true,
|
||||
|
@ -124,10 +124,15 @@ discourseModule("Unit | Utility | emoji", function () {
|
|||
);
|
||||
testUnescape(
|
||||
"hi:smile:",
|
||||
`hi<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||
`hi<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"emoji when inline translation enabled",
|
||||
{ enable_inline_emoji_translation: true }
|
||||
);
|
||||
assert.strictEqual(
|
||||
emojiUnescape(":smile:", { tabIndex: "0" }),
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||
"emoji when tabindex is enabled"
|
||||
);
|
||||
});
|
||||
|
||||
test("Emoji search", function (assert) {
|
||||
|
|
|
@ -143,7 +143,7 @@ discourseModule("Unit | Model | topic", function () {
|
|||
|
||||
assert.strictEqual(
|
||||
topic.get("fancyTitle"),
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'> with all <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji' tabindex='0'> the emojis <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/pear.png?v=${v}' title='pear' alt='pear' class='emoji' tabindex='0'><img width=\"20\" height=\"20\" src='/images/emoji/google_classic/peach.png?v=${v}' title='peach' alt='peach' class='emoji' tabindex='0'>`,
|
||||
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> with all <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/slight_smile.png?v=${v}' title='slight_smile' alt='slight_smile' class='emoji'> the emojis <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/pear.png?v=${v}' title='pear' alt='pear' class='emoji'><img width=\"20\" height=\"20\" src='/images/emoji/google_classic/peach.png?v=${v}' title='peach' alt='peach' class='emoji'>`,
|
||||
"supports emojis"
|
||||
);
|
||||
});
|
||||
|
@ -173,7 +173,7 @@ discourseModule("Unit | Model | topic", function () {
|
|||
|
||||
assert.strictEqual(
|
||||
topic.get("escapedExcerpt"),
|
||||
`This is a test topic <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||
`This is a test topic <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
||||
"supports emojis"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -93,12 +93,13 @@ export function performEmojiUnescape(string, opts) {
|
|||
isReplacableInlineEmoji(string, index, opts.inlineEmoji);
|
||||
|
||||
const title = opts.title ?? emojiVal;
|
||||
const tabIndex = opts.tabIndex ? ` tabindex='${opts.tabIndex}'` : "";
|
||||
return url && isReplacable
|
||||
? `<img width="20" height="20" src='${url}' ${
|
||||
opts.skipTitle ? "" : `title='${title}'`
|
||||
} ${
|
||||
opts.lazy ? "loading='lazy' " : ""
|
||||
}alt='${title}' class='${classes}' tabindex='0'>`
|
||||
}alt='${title}' class='${classes}'${tabIndex}>`
|
||||
: m;
|
||||
};
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ end
|
|||
def html_for_section(group)
|
||||
icons = group["icons"].map do |icon|
|
||||
class_attr = icon["diversity"] ? " class=\"diversity\"" : ""
|
||||
" {{replace-emoji \":#{icon['name']}:\" (hash lazy=true#{class_attr})}}"
|
||||
" {{replace-emoji \":#{icon['name']}:\" (hash lazy=true#{class_attr} tabIndex=\"0\")}}"
|
||||
end
|
||||
|
||||
<<~HTML
|
||||
|
@ -210,7 +210,7 @@ task 'javascript:update_constants' => :environment do
|
|||
|
||||
emoji_buttons = groups_json.map do |group|
|
||||
<<~HTML
|
||||
<button type="button" data-section="#{group["name"]}" {{action onCategorySelection "#{group["name"]}"}} class="btn btn-default category-button emoji">
|
||||
<button type="button" data-section="#{group["name"]}" {{action this.onCategorySelection "#{group["name"]}"}} class="btn btn-default category-button emoji">
|
||||
{{replace-emoji ":#{group["tabicon"]}:"}}
|
||||
</button>
|
||||
HTML
|
||||
|
|
Loading…
Reference in New Issue
Block a user