FEATURE: Select emojis in picker via keyboard (#18163)
* DEV: Make emoji elements focusable Since emoji elements are of type `<img>` it requires a `tablindex="0"` in order to be focusable. * WIP: Handle emoji focus/selection via arrow keys Near completion, however, need a few fixes/improvements and overall code cleanup * WIP: Testing * DEV: Fixes and cleanup * DEV: Follow conventions * DEV: Improve up/down traversal when recents present * DEV: Emoji markup in tests should include `tabindex` * DEV: Add `tabindex` to topic tests * DEV: Variable name as `searchInput` instead of `searchBar` * DEV: Use appropriate method name (`_setNumEmojiPerRow`) * DEV: Add comments and avoid nested if * WIP: Adding test * Fix first test * DEV: Add assertions for arrow keys and escape key * Some fixes for up/down navigation This does not fix everything, when going from one section to another, there are issues * Fix a small regression * FIX: Ability to focus on search results Fixes regression * Refactor calculating next up/down emoji * Debugging test failure * Skip stubborn CI test, add others Co-authored-by: Penar Musaraj <pmusaraj@gmail.com>
This commit is contained in:
parent
e3d7253199
commit
eb987460f2
|
@ -41,6 +41,10 @@ export default Component.extend({
|
||||||
usePopper: true,
|
usePopper: true,
|
||||||
placement: "auto", // one of popper.js' placements, see https://popper.js.org/docs/v2/constructors/#options
|
placement: "auto", // one of popper.js' placements, see https://popper.js.org/docs/v2/constructors/#options
|
||||||
initialFilter: "",
|
initialFilter: "",
|
||||||
|
elements: {
|
||||||
|
searchInput: ".emoji-picker-search-container input",
|
||||||
|
picker: ".emoji-picker-emoji-area",
|
||||||
|
},
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
@ -97,7 +101,6 @@ export default Component.extend({
|
||||||
if (!emojiPicker) {
|
if (!emojiPicker) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const popperAnchor = this._getPopperAnchor();
|
const popperAnchor = this._getPopperAnchor();
|
||||||
|
|
||||||
if (!this.site.isMobileDevice && this.usePopper && popperAnchor) {
|
if (!this.site.isMobileDevice && this.usePopper && popperAnchor) {
|
||||||
|
@ -245,10 +248,100 @@ export default Component.extend({
|
||||||
|
|
||||||
@action
|
@action
|
||||||
keydown(event) {
|
keydown(event) {
|
||||||
if (event.code === "Escape") {
|
const arrowKeys = ["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight"];
|
||||||
|
const emojis = document.querySelectorAll(".emoji-picker-emoji-area .emoji");
|
||||||
|
|
||||||
|
let currentEmoji;
|
||||||
|
|
||||||
|
this.set(
|
||||||
|
"hoveredEmoji",
|
||||||
|
this._codeWithDiversity(event.target.title, this.selectedDiversity)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
event.key === "ArrowDown" &&
|
||||||
|
this._focusedOn(this.elements.searchInput)
|
||||||
|
) {
|
||||||
|
emojis[0].focus();
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Escape") {
|
||||||
this.onClose(event);
|
this.onClose(event);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arrowKeys.includes(event.key)) {
|
||||||
|
if (!this._focusedOn(this.elements.picker)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.from(emojis).find((e, index) => {
|
||||||
|
currentEmoji = index;
|
||||||
|
return e.isEqualNode(event.target);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (event.key === "ArrowRight") {
|
||||||
|
let nextEmoji = currentEmoji + 1;
|
||||||
|
|
||||||
|
if (nextEmoji < emojis.length) {
|
||||||
|
emojis[nextEmoji].focus();
|
||||||
|
} else if (nextEmoji >= emojis.length) {
|
||||||
|
emojis[0].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowLeft") {
|
||||||
|
const previousEmoji = currentEmoji - 1;
|
||||||
|
if (currentEmoji > 0) {
|
||||||
|
emojis[previousEmoji].focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const active = emojis[currentEmoji];
|
||||||
|
|
||||||
|
if (event.key === "ArrowDown") {
|
||||||
|
// source: https://stackoverflow.com/a/49090383/349424
|
||||||
|
// look for same element type with
|
||||||
|
// - higher offsetTop
|
||||||
|
// - same offsetLeft
|
||||||
|
const emojiBelow = [...emojis]
|
||||||
|
.filter((c) => c.offsetTop > active.offsetTop)
|
||||||
|
.find((c) => c.offsetLeft === active.offsetLeft);
|
||||||
|
|
||||||
|
emojiBelow?.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "ArrowUp") {
|
||||||
|
// look for same element type with
|
||||||
|
// - lower offsetTop
|
||||||
|
// - same offsetLeft
|
||||||
|
const emojiAbove = [...emojis]
|
||||||
|
.reverse()
|
||||||
|
.filter((c) => c.offsetTop < active.offsetTop)
|
||||||
|
.find((c) => c.offsetLeft === active.offsetLeft);
|
||||||
|
|
||||||
|
if (emojiAbove) {
|
||||||
|
emojiAbove.focus();
|
||||||
|
} else {
|
||||||
|
document.querySelector(this.elements.searchInput).focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
if (!this._focusedOn(".emoji")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onEmojiSelection(event);
|
||||||
|
this.onClose(event);
|
||||||
|
event.preventDefault();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@action
|
@action
|
||||||
|
@ -256,6 +349,11 @@ export default Component.extend({
|
||||||
this._applyFilter(event.target.value);
|
this._applyFilter(event.target.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_focusedOn(item) {
|
||||||
|
// returns the item currently being focused on
|
||||||
|
return document.activeElement.closest(item) ? document.activeElement : null;
|
||||||
|
},
|
||||||
|
|
||||||
_applyFilter(filter) {
|
_applyFilter(filter) {
|
||||||
const emojiPicker = document.querySelector(".emoji-picker");
|
const emojiPicker = document.querySelector(".emoji-picker");
|
||||||
const results = document.querySelector(".emoji-picker-emoji-area .results");
|
const results = document.querySelector(".emoji-picker-emoji-area .results");
|
||||||
|
@ -287,7 +385,7 @@ export default Component.extend({
|
||||||
const escaped = emojiUnescape(`:${escapeExpression(code)}:`, {
|
const escaped = emojiUnescape(`:${escapeExpression(code)}:`, {
|
||||||
lazy: true,
|
lazy: true,
|
||||||
});
|
});
|
||||||
return htmlSafe(`<span>${escaped}</span>`);
|
return htmlSafe(escaped);
|
||||||
},
|
},
|
||||||
|
|
||||||
_codeWithDiversity(code, selectedDiversity) {
|
_codeWithDiversity(code, selectedDiversity) {
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="section-group">
|
<div class="section-group">
|
||||||
{{#each this.recentEmojis as |emoji|}}
|
{{#each this.recentEmojis as |emoji|}}
|
||||||
{{replace-emoji (concat ":" emoji ":") (hash lazy=true)}}
|
{{replace-emoji (concat ":" emoji ":") (hash lazy=true class="recent-emoji")}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -55,7 +55,9 @@
|
||||||
{{#if emojis.length}}
|
{{#if emojis.length}}
|
||||||
<div class="section-group">
|
<div class="section-group">
|
||||||
{{#each emojis as |emoji|}}
|
{{#each emojis as |emoji|}}
|
||||||
|
<span>
|
||||||
<img title={{emoji.code}} width="20" height="20" loading="lazy" class="emoji" src={{emoji.src}}>
|
<img title={{emoji.code}} width="20" height="20" loading="lazy" class="emoji" src={{emoji.src}}>
|
||||||
|
</span>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import {
|
||||||
query,
|
query,
|
||||||
queryAll,
|
queryAll,
|
||||||
} from "discourse/tests/helpers/qunit-helpers";
|
} from "discourse/tests/helpers/qunit-helpers";
|
||||||
import { click, fillIn, visit } from "@ember/test-helpers";
|
import { click, fillIn, triggerKeyEvent, visit } from "@ember/test-helpers";
|
||||||
import { test } from "qunit";
|
import { test } from "qunit";
|
||||||
|
|
||||||
acceptance("EmojiPicker", function (needs) {
|
acceptance("EmojiPicker", function (needs) {
|
||||||
|
@ -179,4 +179,94 @@ acceptance("EmojiPicker", function (needs) {
|
||||||
"it stores diversity scale"
|
"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"));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,12 +32,12 @@ discourseModule("Unit | Utility | emoji", function () {
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"emoticons :)",
|
"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'>`,
|
`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 are still supported"
|
"emoticons are still supported"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"With emoji :O: :frog: :smile:",
|
"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'> <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'>`,
|
`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'>`,
|
||||||
"title with emoji"
|
"title with emoji"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
|
@ -47,27 +47,27 @@ discourseModule("Unit | Utility | emoji", function () {
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"(:frog:) :)",
|
"(:frog:) :)",
|
||||||
`(<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'>`,
|
`(<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'>`,
|
||||||
"non-word characters allowed next to emoji"
|
"non-word characters allowed next to emoji"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
":smile: hi",
|
":smile: hi",
|
||||||
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'> hi`,
|
`<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'> hi`,
|
||||||
"start of line"
|
"start of line"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"hi :smile:",
|
"hi :smile:",
|
||||||
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
`hi <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||||
"end of line"
|
"end of line"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"hi :blonde_woman:t4:",
|
"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'>`,
|
`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'>`,
|
||||||
"support for skin tones"
|
"support for skin tones"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"hi :blonde_woman:t4: :blonde_man:t6:",
|
"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'> <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'>`,
|
`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'>`,
|
||||||
"support for multiple skin tones"
|
"support for multiple skin tones"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
|
@ -95,7 +95,7 @@ discourseModule("Unit | Utility | emoji", function () {
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"Hello 😊 World",
|
"Hello 😊 World",
|
||||||
`Hello <img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blush.png?v=${v}' title='blush' alt='blush' class='emoji'> 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`,
|
||||||
"emoji from Unicode emoji"
|
"emoji from Unicode emoji"
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
|
@ -108,7 +108,7 @@ discourseModule("Unit | Utility | emoji", function () {
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"Hello😊World",
|
"Hello😊World",
|
||||||
`Hello<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/blush.png?v=${v}' title='blush' alt='blush' class='emoji'>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`,
|
||||||
"emoji from Unicode emoji when inline translation enabled",
|
"emoji from Unicode emoji when inline translation enabled",
|
||||||
{
|
{
|
||||||
enable_inline_emoji_translation: true,
|
enable_inline_emoji_translation: true,
|
||||||
|
@ -124,7 +124,7 @@ discourseModule("Unit | Utility | emoji", function () {
|
||||||
);
|
);
|
||||||
testUnescape(
|
testUnescape(
|
||||||
"hi:smile:",
|
"hi:smile:",
|
||||||
`hi<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji'>`,
|
`hi<img width=\"20\" height=\"20\" src='/images/emoji/google_classic/smile.png?v=${v}' title='smile' alt='smile' class='emoji' tabindex='0'>`,
|
||||||
"emoji when inline translation enabled",
|
"emoji when inline translation enabled",
|
||||||
{ enable_inline_emoji_translation: true }
|
{ enable_inline_emoji_translation: true }
|
||||||
);
|
);
|
||||||
|
|
|
@ -143,7 +143,7 @@ discourseModule("Unit | Model | topic", function () {
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
topic.get("fancyTitle"),
|
topic.get("fancyTitle"),
|
||||||
`<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'>`,
|
`<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'>`,
|
||||||
"supports emojis"
|
"supports emojis"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -173,7 +173,7 @@ discourseModule("Unit | Model | topic", function () {
|
||||||
|
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
topic.get("escapedExcerpt"),
|
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'>`,
|
`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'>`,
|
||||||
"supports emojis"
|
"supports emojis"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
@ -98,7 +98,7 @@ export function performEmojiUnescape(string, opts) {
|
||||||
opts.skipTitle ? "" : `title='${title}'`
|
opts.skipTitle ? "" : `title='${title}'`
|
||||||
} ${
|
} ${
|
||||||
opts.lazy ? "loading='lazy' " : ""
|
opts.lazy ? "loading='lazy' " : ""
|
||||||
}alt='${title}' class='${classes}'>`
|
}alt='${title}' class='${classes}' tabindex='0'>`
|
||||||
: m;
|
: m;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -87,12 +87,18 @@ sup img.emoji {
|
||||||
|
|
||||||
.section-group,
|
.section-group,
|
||||||
.results {
|
.results {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
img.emoji {
|
img.emoji {
|
||||||
padding: 0.25em;
|
padding: 0.25em 0.28em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
margin: 0;
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
&:hover {
|
&:hover,
|
||||||
background: $tertiary-low;
|
&:focus {
|
||||||
|
background: var(--tertiary-low);
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,10 +109,6 @@ sup img.emoji {
|
||||||
&:empty {
|
&:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
img.emoji {
|
|
||||||
padding: 0.5em;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue