FIX: Emoji autocomplete “more” button not working in chat (#20113)

* FIX: Emoji autocomplete “more” button not working

* Rely on setting an intial value on the filter input

This commit removes custom logic applied on initial filter and instead gives a param to use as value for the input, automatically triggering the existing filtering handler.

Other notes:
- Slightly changes the API to be able to set a filter and open the composer in one go
- Adds a very simple service spec
- Adds a system spec to fully validate the behavior

---------

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>
This commit is contained in:
Jan Cernik 2023-02-02 15:04:52 -03:00 committed by GitHub
parent e4fd3d9850
commit 6325e641d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 51 additions and 15 deletions

View File

@ -464,7 +464,9 @@ export default Component.extend(TextareaTextManipulation, {
return `${v.code}:`;
} else {
$textarea.autocomplete({ cancel: true });
this.set("emojiPickerIsActive", true);
this.chatEmojiPickerManager.startFromComposer(this.emojiSelected, {
filter: v.term,
});
return "";
}
},

View File

@ -13,6 +13,7 @@
<div class="chat-emoji-picker__filter-container">
<DcFilterInput
@class="chat-emoji-picker__filter"
@value={{this.chatEmojiPickerManager.initialFilter}}
@filterAction={{this.didInputFilter}}
@icons={{hash left="search"}}
placeholder={{i18n "chat.emoji_picker.search_placeholder"}}

View File

@ -518,8 +518,8 @@ export default Component.extend({
startReactionForMessageActions() {
this.chatEmojiPickerManager.startFromMessageActions(
this.message,
this.site.desktopView,
this.selectReaction
this.selectReaction,
{ desktop: this.site.desktopView }
);
},
@ -527,8 +527,8 @@ export default Component.extend({
startReactionForReactionList() {
this.chatEmojiPickerManager.startFromMessageReactionList(
this.message,
this.site.desktopView,
this.selectReaction
this.selectReaction,
{ desktop: this.site.desktopView }
);
},

View File

@ -11,6 +11,7 @@
<Input
class="dc-filter-input"
@value={{@value}}
{{on "input" (if @filterAction @filterAction (noop))}}
{{on "focusin" (action (mut this.isFocused) true)}}
{{on "focusout" (action (mut this.isFocused) false)}}

View File

@ -23,6 +23,7 @@ export default class ChatEmojiPickerManager extends Service {
@tracked emojis = null;
@tracked visibleSections = DEFAULT_VISIBLE_SECTIONS;
@tracked lastVisibleSection = DEFAULT_LAST_SECTION;
@tracked initialFilter = null;
@tracked element = null;
@tracked callback;
@ -35,6 +36,7 @@ export default class ChatEmojiPickerManager extends Service {
closeExisting() {
this.callback = null;
this.opened = false;
this.initialFilter = null;
this.visibleSections = DEFAULT_VISIBLE_SECTIONS;
this.lastVisibleSection = DEFAULT_LAST_SECTION;
}
@ -51,6 +53,7 @@ export default class ChatEmojiPickerManager extends Service {
this.visibleSections = DEFAULT_VISIBLE_SECTIONS;
this.lastVisibleSection = DEFAULT_LAST_SECTION;
this.initialFilter = null;
this.closing = false;
this.opened = false;
}, TRANSITION_TIME);
@ -68,27 +71,32 @@ export default class ChatEmojiPickerManager extends Service {
this.close();
}
startFromMessageReactionList(message, isDesktop, callback) {
startFromMessageReactionList(message, callback, options = {}) {
const trigger = document.querySelector(
`.chat-message-container[data-id="${message.id}"] .chat-message-react-btn`
);
this.startFromMessage(callback, isDesktop, trigger);
this.startFromMessage(callback, trigger, options);
}
startFromMessageActions(message, isDesktop, callback) {
startFromMessageActions(message, callback, options = {}) {
const trigger = document.querySelector(
`.chat-message-actions-container[data-id="${message.id}"] .chat-message-actions`
);
this.startFromMessage(callback, isDesktop, trigger);
this.startFromMessage(callback, trigger, options);
}
startFromMessage(callback, isDesktop, trigger) {
startFromMessage(
callback,
trigger,
options = { filter: null, desktop: true }
) {
this.initialFilter = options.filter;
this.context = "chat-message";
this.element = document.querySelector(".chat-message-emoji-picker-anchor");
this.open(callback);
this._popper?.destroy();
if (isDesktop) {
if (options.desktop) {
schedule("afterRender", () => {
this._popper = createPopper(trigger, this.element, {
placement: "top",
@ -112,7 +120,8 @@ export default class ChatEmojiPickerManager extends Service {
}
}
startFromComposer(callback) {
startFromComposer(callback, options = { filter: null }) {
this.initialFilter = options.filter;
this.context = "chat-composer";
this.element = document.querySelector(".chat-composer-emoji-picker-anchor");
this.open(callback);
@ -140,7 +149,6 @@ export default class ChatEmojiPickerManager extends Service {
.then((emojis) => {
this.emojis = emojis;
})
.catch(popupAjaxError)
.finally(() => {
this.loading = false;

View File

@ -94,6 +94,22 @@ RSpec.describe "Chat composer", type: :system, js: true do
end
end
context "when opening emoji picker through more button of the autocomplete" do
before do
channel_1.add(current_user)
sign_in(current_user)
end
it "prefills the emoji picker filter input" do
chat.visit_channel(channel_1)
find(".chat-composer-input").fill_in(with: ":gri")
click_link(I18n.t("js.composer.more_emoji"))
expect(find(".chat-emoji-picker .dc-filter-input").value).to eq("gri")
end
end
context "when typing on keyboard" do
before do
channel_1.add(current_user)

View File

@ -24,7 +24,7 @@ module(
test("startFromMessageReactionList", async function (assert) {
const callback = () => {};
this.manager.startFromMessageReactionList({ id: 1 }, false, callback);
this.manager.startFromMessageReactionList({ id: 1 }, callback);
assert.ok(this.manager.loading);
assert.ok(this.manager.opened);
@ -44,7 +44,7 @@ module(
test("startFromMessageActions", async function (assert) {
const callback = () => {};
this.manager.startFromMessageReactionList({ id: 1 }, false, callback);
this.manager.startFromMessageReactionList({ id: 1 }, callback);
assert.ok(this.manager.loading);
assert.ok(this.manager.opened);
@ -104,6 +104,14 @@ module(
assert.strictEqual(this.manager.loading, false);
});
test("startFromComposer with filter option", async function (assert) {
const callback = () => {};
this.manager.startFromComposer(callback, { filter: "foofilter" });
await settled();
assert.strictEqual(this.manager.initialFilter, "foofilter");
});
test("closeExisting", async function (assert) {
const callback = () => {
return;