From fa006f2bbe7cf9a3a9ee458aa89eca116cf272c0 Mon Sep 17 00:00:00 2001 From: zel-odoo Date: Wed, 29 Mar 2023 10:15:07 +0000 Subject: [PATCH] [IMP] mail: ChannelSelector improvements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Display no result when having no matched results. 2. Hidden when clicking outside of the field. 3. Be visible when clicking inside the field. 4. Add related tests task-3208033 closes odoo/odoo#116214 Signed-off-by: Alexandre Kühn (aku) --- addons/mail/static/src/composer/composer.js | 1 + addons/mail/static/src/composer/composer.xml | 1 + .../static/src/composer/navigable_list.js | 17 ++++++++++++- .../static/src/composer/navigable_list.xml | 2 +- .../static/src/discuss/channel_selector.js | 16 +++++++++--- .../static/src/discuss/channel_selector.xml | 1 + .../src/web/messaging_menu/messaging_menu.xml | 2 +- .../chat_window_new_message_tests.js | 14 +++++------ .../static/tests/discuss/discuss_tests.js | 25 +++++++++++++++++++ .../messaging_menu/messaging_menu_tests.js | 4 +-- 10 files changed, 67 insertions(+), 16 deletions(-) diff --git a/addons/mail/static/src/composer/composer.js b/addons/mail/static/src/composer/composer.js index 0cca26d5663..4e107c2d594 100644 --- a/addons/mail/static/src/composer/composer.js +++ b/addons/mail/static/src/composer/composer.js @@ -100,6 +100,7 @@ export class Composer extends Component { }, }); this.suggestion = this.store.user ? useSuggestion() : undefined; + this.markEventHandled = markEventHandled; if (this.props.dropzoneRef && this.allowUpload) { useDropzone( this.props.dropzoneRef, diff --git a/addons/mail/static/src/composer/composer.xml b/addons/mail/static/src/composer/composer.xml index c3707c915f1..b48e4290a4c 100644 --- a/addons/mail/static/src/composer/composer.xml +++ b/addons/mail/static/src/composer/composer.xml @@ -41,6 +41,7 @@ t-on-input="onInput" t-on-focusin="onFocusin" t-on-focusout="() => this.props.composer.isFocused = false" + t-on-click="(ev) => markEventHandled(ev, 'composer.onClickTextarea')" t-on-paste="onPaste" t-model="props.composer.textInputContent" t-att-placeholder="placeholder" diff --git a/addons/mail/static/src/composer/navigable_list.js b/addons/mail/static/src/composer/navigable_list.js index d91a523a69b..178cfe320a2 100644 --- a/addons/mail/static/src/composer/navigable_list.js +++ b/addons/mail/static/src/composer/navigable_list.js @@ -13,7 +13,8 @@ import { useRef, useState, } from "@odoo/owl"; -import { markEventHandled } from "../utils/misc"; +import { markEventHandled, isEventHandled } from "../utils/misc"; +import { onExternalClick } from "@mail/utils/hooks"; export class NavigableList extends Component { static components = { ImStatus }; @@ -41,6 +42,17 @@ export class NavigableList extends Component { this.hotkeysToRemove = []; useExternalListener(window, "keydown", this.onKeydown, true); + onExternalClick("root", async (ev) => { + // Let event be handled by bubbling handlers first. + await new Promise(setTimeout); + if ( + isEventHandled(ev, "composer.onClickTextarea") || + isEventHandled(ev, "channelSelector.onClickInput") + ) { + return; + } + this.close(); + }); // position and size usePosition(() => this.props.anchorRef, { popper: "root", @@ -72,6 +84,9 @@ export class NavigableList extends Component { } open() { + if (this.state.isLoading) { + return; + } this.load().then(() => { this.state.open = true; this.navigate("first"); diff --git a/addons/mail/static/src/composer/navigable_list.xml b/addons/mail/static/src/composer/navigable_list.xml index 8a2488cc62f..2b359c18544 100644 --- a/addons/mail/static/src/composer/navigable_list.xml +++ b/addons/mail/static/src/composer/navigable_list.xml @@ -5,7 +5,7 @@
- + diff --git a/addons/mail/static/src/discuss/channel_selector.js b/addons/mail/static/src/discuss/channel_selector.js index 918c68481f9..dffe1083b1d 100644 --- a/addons/mail/static/src/discuss/channel_selector.js +++ b/addons/mail/static/src/discuss/channel_selector.js @@ -7,7 +7,7 @@ import { useService } from "@web/core/utils/hooks"; import { getActiveHotkey } from "@web/core/hotkeys/hotkey_service"; import { Component, onMounted, useRef, useState } from "@odoo/owl"; import { cleanTerm } from "@mail/utils/format"; -import { createLocalId, isEventHandled } from "@mail/utils/misc"; +import { createLocalId, isEventHandled, markEventHandled } from "@mail/utils/misc"; import { _t } from "@web/core/l10n/translation"; export class ChannelSelector extends Component { @@ -30,10 +30,11 @@ export class ChannelSelector extends Component { if (this.props.autofocus) { onMounted(() => this.inputRef.el.focus()); } + this.markEventHandled = markEventHandled; } - async fetchSuggestions(term) { - const cleanedTerm = cleanTerm(term); + async fetchSuggestions() { + const cleanedTerm = cleanTerm(this.state.value); if (cleanedTerm) { if (this.props.category.id === "channels") { const domain = [ @@ -79,6 +80,13 @@ export class ChannelSelector extends Component { partner: this.store.self, }); } + if (suggestions.length === 0) { + suggestions.push({ + classList: "o-mail-ChannelSelector-suggestion", + label: _t("No results found"), + unselectable: true, + }); + } return suggestions; } } @@ -184,7 +192,7 @@ export class ChannelSelector extends Component { this.props.category.id === "channels" ? "mail.ChannelSelector.channel" : "mail.ChannelSelector.chat", - options: this.fetchSuggestions(this.state.value), + options: this.fetchSuggestions(), }; } } diff --git a/addons/mail/static/src/discuss/channel_selector.xml b/addons/mail/static/src/discuss/channel_selector.xml index b5402c14dee..5fe427a168a 100644 --- a/addons/mail/static/src/discuss/channel_selector.xml +++ b/addons/mail/static/src/discuss/channel_selector.xml @@ -9,6 +9,7 @@ t-att-placeholder="inputPlaceholder" t-model="state.value" t-ref="input" + t-on-click="(ev) => markEventHandled(ev, 'channelSelector.onClickInput')" t-on-keydown="onKeydownInput" type="text" /> diff --git a/addons/mail/static/src/web/messaging_menu/messaging_menu.xml b/addons/mail/static/src/web/messaging_menu/messaging_menu.xml index cbacc052b6b..7fd189f9348 100644 --- a/addons/mail/static/src/web/messaging_menu/messaging_menu.xml +++ b/addons/mail/static/src/web/messaging_menu/messaging_menu.xml @@ -34,7 +34,7 @@
- diff --git a/addons/mail/static/tests/chat_window/chat_window_new_message_tests.js b/addons/mail/static/tests/chat_window/chat_window_new_message_tests.js index 9472fb63d5d..678901c2ba6 100644 --- a/addons/mail/static/tests/chat_window/chat_window_new_message_tests.js +++ b/addons/mail/static/tests/chat_window/chat_window_new_message_tests.js @@ -20,7 +20,7 @@ QUnit.module("chat window: new message"); QUnit.test("basic rendering", async (assert) => { await start(); await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); assert.containsOnce($, ".o-mail-ChatWindow"); assert.containsOnce($, ".o-mail-ChatWindow-header"); assert.containsOnce($, ".o-mail-ChatWindow-header .o-mail-ChatWindow-name"); @@ -40,7 +40,7 @@ QUnit.test("basic rendering", async (assert) => { QUnit.test("focused on open [REQUIRE FOCUS]", async (assert) => { await start(); await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); assert.strictEqual( document.activeElement, $(".o-mail-ChatWindow .o-mail-ChannelSelector input")[0] @@ -50,7 +50,7 @@ QUnit.test("focused on open [REQUIRE FOCUS]", async (assert) => { QUnit.test("close", async (assert) => { await start(); await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); await click(".o-mail-ChatWindow-header .o-mail-ChatWindow-command[title='Close chat window']"); assert.containsNone($, ".o-mail-ChatWindow"); }); @@ -58,7 +58,7 @@ QUnit.test("close", async (assert) => { QUnit.test("fold", async (assert) => { await start(); await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); assert.containsOnce($, ".o-mail-ChatWindow-content"); assert.containsOnce($, ".o-mail-ChannelSelector"); @@ -111,7 +111,7 @@ QUnit.test( // open "new message" chat window await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); assert.containsOnce($, ".o-mail-ChatWindow-header:contains(New message)"); assert.containsN($, ".o-mail-ChatWindow", 2); assert.containsOnce($, ".o-mail-ChatWindow .o-mail-ChannelSelector"); @@ -170,7 +170,7 @@ QUnit.test( }); await start(); await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); await insertText(".o-mail-ChannelSelector", "131"); await click(".o-mail-ChannelSelector-suggestion a"); assert.containsNone($, ".o-mail-ChatWindow-header:contains(New message)"); @@ -192,7 +192,7 @@ QUnit.test("new message autocomplete should automatically select first result", }); // open "new message" chat window await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); // search for a user in "new message" autocomplete await afterNextRender(async () => { await insertText(".o-mail-ChannelSelector", "131"); diff --git a/addons/mail/static/tests/discuss/discuss_tests.js b/addons/mail/static/tests/discuss/discuss_tests.js index ba671edbf50..8d0f615b2e9 100644 --- a/addons/mail/static/tests/discuss/discuss_tests.js +++ b/addons/mail/static/tests/discuss/discuss_tests.js @@ -223,6 +223,31 @@ QUnit.test("can create a group chat conversation", async (assert) => { assert.containsNone($, ".o-mail-Message"); }); +QUnit.test("chat search should display no result when no matches found", async (assert) => { + const { openDiscuss } = await start(); + await openDiscuss(); + await click(".o-mail-DiscussSidebar i[title='Start a conversation']"); + await insertText(".o-mail-ChannelSelector", "Rainbow Panda"); + assert.containsOnce($, ".o-mail-ChannelSelector-suggestion:contains(No results found)"); +}); + +QUnit.test( + "chat search should not be visible when clicking outside of the field", + async (assert) => { + const pyEnv = await startServer(); + const partnerId = pyEnv["res.partner"].create({ name: "Panda" }); + pyEnv["res.users"].create({ partner_id: partnerId }); + const { openDiscuss } = await start(); + await openDiscuss(); + assert.containsNone($, ".o-mail-DiscussCategoryItem"); + await click(".o-mail-DiscussSidebar i[title='Start a conversation']"); + await insertText(".o-mail-ChannelSelector", "Panda"); + assert.containsOnce($, ".o-mail-ChannelSelector-suggestion"); + await click(".o-mail-DiscussSidebar"); + assert.containsNone($, ".o-mail-ChannelSelector-suggestion"); + } +); + QUnit.test("Message following a notification should not be squashed", async (assert) => { const pyEnv = await startServer(); const channelId = pyEnv["mail.channel"].create({ diff --git a/addons/mail/static/tests/messaging_menu/messaging_menu_tests.js b/addons/mail/static/tests/messaging_menu/messaging_menu_tests.js index 75a7152c1c5..9702afcdaea 100644 --- a/addons/mail/static/tests/messaging_menu/messaging_menu_tests.js +++ b/addons/mail/static/tests/messaging_menu/messaging_menu_tests.js @@ -127,7 +127,7 @@ QUnit.test("respond to notification prompt (granted)", async (assert) => { QUnit.test("Is closed after clicking on new message", async (assert) => { await start(); await click(".o_menu_systray i[aria-label='Messages']"); - await click(".o-mail-MessagingMenu-new"); + await click("button:contains(New Message)"); assert.containsNone($, ".o-mail-MessagingMenu"); }); @@ -582,7 +582,7 @@ QUnit.test("basic rendering", async (assert) => { assert.hasClass($('.o-mail-MessagingMenu button:contains("All")'), "fw-bolder"); assert.doesNotHaveClass($('.o-mail-MessagingMenu button:contains("Chats")'), "fw-bolder"); assert.doesNotHaveClass($('.o-mail-MessagingMenu button:contains("Channels")'), "fw-bolder"); - assert.containsOnce($, ".o-mail-MessagingMenu-new"); + assert.containsOnce($, "button:contains(New Message)"); assert.containsOnce($, '.o-mail-MessagingMenu:contains("No conversation yet...")'); await click(".o_menu_systray .dropdown-toggle:has(i[aria-label='Messages'])"); assert.doesNotHaveClass(