diff --git a/addons/mail/static/src/composer/composer.js b/addons/mail/static/src/composer/composer.js index ed9c6c11f10..ccb5df65041 100644 --- a/addons/mail/static/src/composer/composer.js +++ b/addons/mail/static/src/composer/composer.js @@ -217,77 +217,79 @@ export class Composer extends Component { } get hasSuggestions() { - return this.suggestion?.state.items.length > 0; + return Boolean(this.suggestion?.state.items); } get navigableListProps() { - return { + const props = { anchorRef: this.ref.el, position: "top", + placeholder: _t("Loading"), onSelect: (ev, option) => { this.suggestion.insert(option); markEventHandled(ev, "composer.selectSuggestion"); }, - sources: this.suggestion.state.items.map((mainOrExtraSuggestions) => { - switch (mainOrExtraSuggestions.type) { - case "Partner": - return { - placeholder: "Loading", - optionTemplate: "mail.Composer.suggestionPartner", - options: mainOrExtraSuggestions.suggestions.map((suggestion) => { - return { - label: suggestion.name, - partner: suggestion, - classList: - "o-composer-suggestion o-composer-suggestion-partner", - }; - }), - }; - case "Thread": - return { - placeholder: "Loading", - optionTemplate: "mail.Composer.suggestionThread", - options: mainOrExtraSuggestions.suggestions.map((suggestion) => { - return { - label: suggestion.displayName, - thread: suggestion, - classList: "o-composer-suggestion o-composer-suggestion-thread", - }; - }), - }; - case "ChannelCommand": - return { - placeholder: "Loading", - optionTemplate: "mail.Composer.suggestionChannelCommand", - options: mainOrExtraSuggestions.suggestions.map((suggestion) => { - return { - label: suggestion.name, - help: suggestion.help, - classList: - "o-composer-suggestion o-composer-suggestion-channel-command", - }; - }), - }; - case "CannedResponse": - return { - placeholder: "Loading", - optionTemplate: "mail.Composer.suggestionCannedResponse", - options: mainOrExtraSuggestions.suggestions.map((suggestion) => { - return { - name: suggestion.name, - label: suggestion.substitution, - classList: - "o-composer-suggestion o-composer-suggestion-canned-response", - }; - }), - }; - default: - return { - options: [], - }; - } - }), + options: [], }; + if (!this.hasSuggestions) { + return props; + } + const suggestions = Array( + ...this.suggestion.state.items.mainSuggestions, + ...this.suggestion.state.items.extraSuggestions + ); + switch (this.suggestion.state.items.type) { + case "Partner": + return { + ...props, + optionTemplate: "mail.Composer.suggestionPartner", + options: suggestions.map((suggestion) => { + return { + label: suggestion.name, + partner: suggestion, + classList: "o-mail-Composer-suggestion", + }; + }), + }; + case "Thread": + return { + ...props, + optionTemplate: "mail.Composer.suggestionThread", + options: suggestions.map((suggestion) => { + return { + label: suggestion.displayName, + thread: suggestion, + classList: "o-mail-Composer-suggestion", + }; + }), + }; + case "ChannelCommand": + return { + ...props, + optionTemplate: "mail.Composer.suggestionChannelCommand", + options: suggestions.map((suggestion) => { + return { + label: suggestion.name, + help: suggestion.help, + classList: "o-mail-Composer-suggestion", + }; + }), + }; + case "CannedResponse": + return { + ...props, + optionTemplate: "mail.Composer.suggestionCannedResponse", + options: suggestions.map((suggestion) => { + return { + name: suggestion.name, + label: suggestion.substitution, + classList: "o-mail-Composer-suggestion", + }; + }), + }; + default: + return props; + } } /** diff --git a/addons/mail/static/src/composer/composer.xml b/addons/mail/static/src/composer/composer.xml index efb72af3ae5..da632acf4e4 100644 --- a/addons/mail/static/src/composer/composer.xml +++ b/addons/mail/static/src/composer/composer.xml @@ -10,7 +10,7 @@ t-att-class="{ 'px-3 py-4 o-extended': extended, 'p-3': normal, - 'o-hasSelfAvatar px-3': !this.env.inChatWindow and thread, + 'o-hasSelfAvatar px-3': !env.inChatWindow and thread, }">
Avatar of user @@ -82,7 +82,7 @@ t-att-disabled="isSendButtonDisabled" aria-label="Send" > - + Send
@@ -99,7 +99,7 @@ escape to cancel, enter to save - + diff --git a/addons/mail/static/src/composer/navigable_list.js b/addons/mail/static/src/composer/navigable_list.js index 8b13eca79de..d91a523a69b 100644 --- a/addons/mail/static/src/composer/navigable_list.js +++ b/addons/mail/static/src/composer/navigable_list.js @@ -20,33 +20,22 @@ export class NavigableList extends Component { static template = "mail.NavigableList"; static props = { anchorRef: {}, - position: { type: String, optional: true }, - onSelect: { type: Function }, - sources: { - type: Array, - element: { - type: Object, - shape: { - placeholder: { type: String, optional: true }, - optionTemplate: { type: String, optional: true }, - options: [Array, Promise], - }, - }, - }, class: { type: String, optional: true }, + onSelect: { type: Function }, + options: { type: [Array, Promise] }, + optionTemplate: { type: String, optional: true }, + placeholder: { type: String, optional: true }, + position: { type: String, optional: true }, }; static defaultProps = { position: "bottom" }; setup() { - this.nextOptionId = 0; - this.nextSourceId = 0; this.rootRef = useRef("root"); - this.sources = []; this.state = useState({ - activeSourceOption: null, - navigationRev: 0, + activeOption: null, + isLoading: false, open: false, - optionsRev: 0, + options: [], }); this.hotkey = useService("hotkey"); this.hotkeysToRemove = []; @@ -78,88 +67,49 @@ export class NavigableList extends Component { }); } - get isOpened() { - return this.state.open; - } - - get hasOptions() { - for (const source of this.sources) { - if (source.isLoading || source.options.length) { - return true; - } - } - return false; + get show() { + return Boolean(this.state.open && (this.state.isLoading || this.state.options.length)); } open() { - this.loadSources(); + this.load().then(() => { + this.state.open = true; + this.navigate("first"); + }); } close() { this.state.open = false; - this.state.activeSourceOption = null; + this.state.activeOption = null; } - loadSources() { - this.sources = []; - const proms = []; - for (const pSource of this.props.sources) { - if (pSource.options.length === 0) { - continue; - } - const source = this.makeSource(pSource); - this.sources.push(source); - const options = pSource.options; - if (options instanceof Promise) { - source.isLoading = true; - const prom = options.then((options) => { - source.options = options.map((option) => this.makeOption(option)); - source.isLoading = false; - if (source.options.length === 0) { - this.sources = this.sources.filter((s) => s !== source); - } - this.state.optionsRev++; - }); - proms.push(prom); - } else { - source.options = options.map((option) => this.makeOption(option)); - } - } - Promise.all(proms).then(() => { - this.navigate(0); - }); - if (this.sources.length === 0) { - return; - } - this.state.open = true; - } - - makeOption(option) { - return Object.assign(Object.create(option), { - id: ++this.nextOptionId, - }); - } - - makeSource(source) { - return { - id: ++this.nextSourceId, - options: [], - isLoading: false, - placeholder: source.placeholder, - optionTemplate: source.optionTemplate, + async load() { + this.state.options = []; + const makeOption = (opt) => { + return Object.assign(Object.create(opt), { + id: this.state.options.length, + }); }; + if (this.props.options instanceof Promise) { + this.state.isLoading = true; + return this.props.options.then((opts) => { + opts.forEach((opt) => this.state.options.push(makeOption(opt))); + this.state.isLoading = false; + }); + } + if (this.props.options instanceof Array) { + if (this.props.options.length === 0) { + return; + } + this.props.options.forEach((opt) => this.state.options.push(makeOption(opt))); + } } - isActiveSourceOption([sourceIndex, optionIndex]) { - return ( - this.state.activeSourceOption && - this.state.activeSourceOption[0] === sourceIndex && - this.state.activeSourceOption[1] === optionIndex - ); + isActiveOption(option) { + return this.state.activeOption?.id === option.id; } - selectOption(ev, indices, params = {}) { - const option = this.sources[indices[0]].options[indices[1]]; + selectOption(ev, option, params = {}) { if (option.unselectable) { this.close(); return; @@ -171,85 +121,67 @@ export class NavigableList extends Component { } navigate(direction) { - let step = Math.sign(direction); - if (!step) { - this.state.activeSourceOption = null; - step = 1; - } else { - this.state.navigationRev++; - } - if (this.state.activeSourceOption) { - let [sourceIndex, optionIndex] = this.state.activeSourceOption; - let source = this.sources[sourceIndex]; - optionIndex += step; - if (0 > optionIndex || optionIndex >= source.options.length) { - sourceIndex += step; - source = this.sources[sourceIndex]; - while (source && source.isLoading) { - sourceIndex += step; - source = this.sources[sourceIndex]; + const activeOptionId = this.state.activeOption ? this.state.activeOption.id : -1; + let targetId = undefined; + switch (direction) { + case "first": + targetId = 0; + break; + case "last": + targetId = this.state.options.length - 1; + break; + case "previous": + targetId = activeOptionId - 1; + if (targetId < 0) { + this.navigate("last"); + return; } - if (source) { - optionIndex = step < 0 ? source.options.length - 1 : 0; + break; + case "next": + targetId = activeOptionId + 1; + if (targetId > this.state.options.length - 1) { + this.navigate("first"); + return; } - } - if (source) { - this.state.activeSourceOption = [sourceIndex, optionIndex]; - } else { - this.state.activeSourceOption = - direction > 0 - ? [0, 0] - : [ - this.sources.length - 1, - this.sources[this.sources.length - 1].options.length - 1, - ]; - } - } else { - let sourceIndex = step < 0 ? this.sources.length - 1 : 0; - let source = this.sources[sourceIndex]; - while (source && source.isLoading) { - sourceIndex += step; - source = this.sources[sourceIndex]; - } - if (source) { - const optionIndex = step < 0 ? source.options.length - 1 : 0; - this.state.activeSourceOption = [sourceIndex, optionIndex]; - } + break; + default: + return; } + this.state.activeOption = this.state.options.find((o) => o.id === targetId); } onKeydown(ev) { const hotkey = getActiveHotkey(ev); switch (hotkey) { case "enter": - if (!this.isOpened || !this.state.activeSourceOption) { + if (!this.show || !this.state.activeOption) { return; } markEventHandled(ev, "NavigableList.select"); - this.selectOption(ev, this.state.activeSourceOption); + this.selectOption(ev, this.state.activeOption); break; case "escape": - if (!this.isOpened) { + if (!this.show) { return; } markEventHandled(ev, "NavigableList.close"); this.close(); break; case "tab": - this.navigate(+1); - if (!this.isOpened) { + this.navigate("next"); + if (!this.show) { this.open(); } break; case "arrowup": - this.navigate(-1); - if (!this.isOpened) { + this.navigate("previous"); + if (!this.show) { this.open(); } break; case "arrowdown": - this.navigate(+1); - if (!this.isOpened) { + this.navigate("next"); + if (!this.show) { this.open(); } break; @@ -259,15 +191,7 @@ export class NavigableList extends Component { ev.preventDefault(); } - onOptionMouseEnter(indices) { - this.state.activeSourceOption = indices; - } - - onOptionMouseLeave() { - this.state.activeSourceOption = null; - } - - onOptionClick(ev, indices) { - this.selectOption(ev, indices); + onOptionMouseEnter(option) { + this.state.activeOption = option; } } diff --git a/addons/mail/static/src/composer/navigable_list.xml b/addons/mail/static/src/composer/navigable_list.xml index 7e846de7a25..8a2488cc62f 100644 --- a/addons/mail/static/src/composer/navigable_list.xml +++ b/addons/mail/static/src/composer/navigable_list.xml @@ -3,29 +3,26 @@
-
- - diff --git a/addons/mail/static/src/composer/suggestion_hook.js b/addons/mail/static/src/composer/suggestion_hook.js index b7418f5de1e..260aad4f25b 100644 --- a/addons/mail/static/src/composer/suggestion_hook.js +++ b/addons/mail/static/src/composer/suggestion_hook.js @@ -17,7 +17,7 @@ export function useSuggestion() { position: undefined, term: undefined, }); - self.state.items.length = 0; + self.state.items = undefined; }, detect() { const selectionEnd = comp.props.composer.selection.end; @@ -113,7 +113,7 @@ export function useSuggestion() { }, state: useState({ count: 0, - items: [], + items: undefined, }), update() { if (!self.search.delimiter || !comp.props.composer.thread) { @@ -127,16 +127,16 @@ export function useSuggestion() { if (!suggestions) { return; } - const [main, extra = { suggestions: [] }] = suggestions; + const { type, mainSuggestions, extraSuggestions = [] } = suggestions; // arbitrary limit to avoid displaying too many elements at once // ideally a load more mechanism should be introduced const limit = 8; - main.suggestions.length = Math.min(main.suggestions.length, limit); - extra.suggestions.length = Math.min( - extra.suggestions.length, - limit - main.suggestions.length + mainSuggestions.length = Math.min(mainSuggestions.length, limit); + extraSuggestions.length = Math.min( + extraSuggestions.length, + limit - mainSuggestions.length ); - self.state.items = [main, extra]; + self.state.items = { type, mainSuggestions, extraSuggestions }; }, }; useEffect( diff --git a/addons/mail/static/src/composer/suggestion_service.js b/addons/mail/static/src/composer/suggestion_service.js index f926472680c..e2267268205 100644 --- a/addons/mail/static/src/composer/suggestion_service.js +++ b/addons/mail/static/src/composer/suggestion_service.js @@ -99,16 +99,11 @@ class SuggestionService { case "/": return this.searchChannelCommand(cleanedSearchTerm, thread, sort); } - return [ - { - type: undefined, - suggestions: [], - }, - { - type: undefined, - suggestions: [], - }, - ]; + return { + type: undefined, + mainSuggestions: [], + extraSuggestions: [], + }; } searchChannelCommand(cleanedSearchTerm, thread, sort) { @@ -164,12 +159,10 @@ class SuggestionService { } return c1.id - c2.id; }; - return [ - { - type: "ChannelCommand", - suggestions: sort ? commands.sort(sortFunc) : commands, - }, - ]; + return { + type: "ChannelCommand", + mainSuggestions: sort ? commands.sort(sortFunc) : commands, + }; } searchPartnerSuggestions(cleanedSearchTerm, thread, sort) { @@ -285,16 +278,11 @@ class SuggestionService { } return p1.id - p2.id; }; - return [ - { - type: "Partner", - suggestions: sort ? mainSuggestionList.sort(sortFunc) : mainSuggestionList, - }, - { - type: "Partner", - suggestions: sort ? extraSuggestionList.sort(sortFunc) : extraSuggestionList, - }, - ]; + return { + type: "Partner", + mainSuggestions: sort ? mainSuggestionList.sort(sortFunc) : mainSuggestionList, + extraSuggestions: sort ? extraSuggestionList.sort(sortFunc) : extraSuggestionList, + }; } searchCannedResponseSuggestions(cleanedSearchTerm, sort) { @@ -332,12 +320,10 @@ class SuggestionService { } return c1.id - c2.id; }; - return [ - { - type: "CannedResponse", - suggestions: sort ? cannedResponses.sort(sortFunc) : cannedResponses, - }, - ]; + return { + type: "CannedResponse", + mainSuggestions: sort ? cannedResponses.sort(sortFunc) : cannedResponses, + }; } searchChannelSuggestions(cleanedSearchTerm, thread, sort) { @@ -400,12 +386,10 @@ class SuggestionService { } return c1.id - c2.id; }; - return [ - { - type: "Thread", - suggestions: sort ? suggestionList.sort(sortFunc) : suggestionList, - }, - ]; + return { + type: "Thread", + mainSuggestions: sort ? suggestionList.sort(sortFunc) : suggestionList, + }; } } diff --git a/addons/mail/static/src/discuss/channel_selector.js b/addons/mail/static/src/discuss/channel_selector.js index 9aa4615f01b..918c68481f9 100644 --- a/addons/mail/static/src/discuss/channel_selector.js +++ b/addons/mail/static/src/discuss/channel_selector.js @@ -179,16 +179,12 @@ export class ChannelSelector extends Component { anchorRef: this.rootRef.el, position: "bottom", onSelect: (ev, option) => this.onSelect(option), - sources: [ - { - placeholder: "Loading", - optionTemplate: - this.props.category.id === "channels" - ? "mail.ChannelSelector.channel" - : "mail.ChannelSelector.chat", - options: this.fetchSuggestions(this.state.value), - }, - ], + placeholder: _t("Loading"), + optionTemplate: + this.props.category.id === "channels" + ? "mail.ChannelSelector.channel" + : "mail.ChannelSelector.chat", + options: this.fetchSuggestions(this.state.value), }; } } diff --git a/addons/mail/static/tests/composer/composer_tests.js b/addons/mail/static/tests/composer/composer_tests.js index 9d4879db6e5..5419686f27b 100644 --- a/addons/mail/static/tests/composer/composer_tests.js +++ b/addons/mail/static/tests/composer/composer_tests.js @@ -364,7 +364,7 @@ QUnit.test( }); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "/"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", " is user?"); assert.verifySteps([], "No rpc done"); } @@ -378,10 +378,10 @@ QUnit.test("add an emoji after a command", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "/"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "/who ", @@ -404,11 +404,11 @@ QUnit.test("add an emoji after a canned response", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", ":"); - assert.containsOnce($, ".o-composer-suggestion"); - await click(".o-composer-suggestion"); + assert.containsOnce($, ".o-mail-Composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "Hello! How are you? ", @@ -438,12 +438,12 @@ QUnit.test("add an emoji after a partner mention", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "T"); await insertText(".o-mail-Composer-input", "e"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val().replace(/\s/, " "), "@TestPartner "); await click("button[aria-label='Emojis']"); @@ -459,7 +459,7 @@ QUnit.test("mention a channel after some text", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "bluhbluh "); assert.strictEqual( @@ -468,8 +468,8 @@ QUnit.test("mention a channel after some text", async (assert) => { "text content of composer should have content" ); await insertText(".o-mail-Composer-input", "#"); - assert.containsOnce($, ".o-composer-suggestion"); - await click(".o-composer-suggestion"); + assert.containsOnce($, ".o-mail-Composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "bluhbluh #General ", @@ -485,11 +485,11 @@ QUnit.test("add an emoji after a channel mention", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "#"); - assert.containsOnce($, ".o-composer-suggestion"); - await click(".o-composer-suggestion"); + assert.containsOnce($, ".o-mail-Composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "#General ", diff --git a/addons/mail/static/tests/discuss/inbox_tests.js b/addons/mail/static/tests/discuss/inbox_tests.js index fe679542831..d45e0fea0ff 100644 --- a/addons/mail/static/tests/discuss/inbox_tests.js +++ b/addons/mail/static/tests/discuss/inbox_tests.js @@ -112,9 +112,9 @@ QUnit.test("reply: discard on pressing escape", async (assert) => { // Escape on suggestion prompt does not stop replying await insertText(".o-mail-Composer-input", "@"); - assert.containsOnce($, ".o-composer-suggestion-list .o-open"); + assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); await afterNextRender(() => triggerHotkey("Escape")); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.containsOnce($, ".o-mail-Composer"); click(".o-mail-Composer-input").catch(() => {}); diff --git a/addons/mail/static/tests/message/message_tests.js b/addons/mail/static/tests/message/message_tests.js index 3b2e59ad3c2..ed13e821c7c 100644 --- a/addons/mail/static/tests/message/message_tests.js +++ b/addons/mail/static/tests/message/message_tests.js @@ -1170,7 +1170,7 @@ QUnit.test("Chat with partner should be opened after clicking on their mention", await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "T"); await insertText(".o-mail-Composer-input", "e"); - await click(".o-composer-suggestion:contains(Test Partner)"); + await click(".o-mail-Composer-suggestion:contains(Test Partner)"); await click(".o-mail-Composer-send"); await click(".o_mail_redirect"); assert.containsOnce($, ".o-mail-ChatWindow-content"); @@ -1216,7 +1216,7 @@ QUnit.test("Channel should be opened after clicking on its mention", async (asse await openFormView("res.partner", partnerId); await click("button:contains(Send message)"); await insertText(".o-mail-Composer-input", "#"); - await click(".o-composer-suggestion:contains(my-channel)"); + await click(".o-mail-Composer-suggestion:contains(my-channel)"); await click(".o-mail-Composer-send"); await click(".o_channel_redirect"); assert.containsOnce($, ".o-mail-ChatWindow-content"); diff --git a/addons/mail/static/tests/suggestion/suggestion_tests.js b/addons/mail/static/tests/suggestion/suggestion_tests.js index 3686bd92a30..bff5f6a4561 100644 --- a/addons/mail/static/tests/suggestion/suggestion_tests.js +++ b/addons/mail/static/tests/suggestion/suggestion_tests.js @@ -41,10 +41,10 @@ QUnit.test('display partner mention suggestions on typing "@"', async (assert) = }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", "@"); - assert.containsN($, ".o-composer-suggestion", 3); + assert.containsN($, ".o-mail-Composer-suggestion", 3); }); QUnit.test( @@ -70,13 +70,13 @@ QUnit.test( }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", "first message"); triggerHotkey("Enter"); await nextTick(); await insertText(".o-mail-Composer-input", "@"); - assert.containsN($, ".o-composer-suggestion", 3); + assert.containsN($, ".o-mail-Composer-suggestion", 3); } ); @@ -85,9 +85,9 @@ QUnit.test('display partner mention suggestions on typing "@" in chatter', async const { openFormView } = await start(); await openFormView("res.partner", pyEnv.currentPartnerId); await click("button:contains(Send message)"); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); await insertText(".o-mail-Composer-input", "@"); - assert.containsOnce($, ".o-composer-suggestion:contains(Mitchell Admin)"); + assert.containsOnce($, ".o-mail-Composer-suggestion:contains(Mitchell Admin)"); }); QUnit.test("show other channel member in @ mention", async (assert) => { @@ -106,7 +106,7 @@ QUnit.test("show other channel member in @ mention", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); - assert.containsOnce($, ".o-composer-suggestion:contains(TestPartner)"); + assert.containsOnce($, ".o-mail-Composer-suggestion:contains(TestPartner)"); }); QUnit.test("select @ mention insert mention text in composer", async (assert) => { @@ -125,7 +125,7 @@ QUnit.test("select @ mention insert mention text in composer", async (assert) => const { openDiscuss } = await start(); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); - await click(".o-composer-suggestion:contains(TestPartner)"); + await click(".o-mail-Composer-suggestion:contains(TestPartner)"); assert.strictEqual($(".o-mail-Composer-input").val().trim(), "@TestPartner"); }); @@ -137,9 +137,9 @@ QUnit.test('display command suggestions on typing "/"', async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); await insertText(".o-mail-Composer-input", "/"); - assert.containsOnce($, ".o-composer-suggestion-list .o-open"); + assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); }); QUnit.test("use a command for a specific channel type", async (assert) => { @@ -147,10 +147,10 @@ QUnit.test("use a command for a specific channel type", async (assert) => { const channelId = pyEnv["mail.channel"].create({ channel_type: "chat" }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "/"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "/who ", @@ -168,12 +168,12 @@ QUnit.test( }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "bluhbluh "); assert.strictEqual($(".o-mail-Composer-input").val(), "bluhbluh "); await insertText(".o-mail-Composer-input", "/"); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); } ); @@ -186,9 +186,9 @@ QUnit.test('display canned response suggestions on typing ":"', async (assert) = }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); await insertText(".o-mail-Composer-input", ":"); - assert.containsOnce($, ".o-composer-suggestion-list .o-open"); + assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); }); QUnit.test("use a canned response", async (assert) => { @@ -200,11 +200,11 @@ QUnit.test("use a canned response", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", ":"); - assert.containsOnce($, ".o-composer-suggestion"); - await click(".o-composer-suggestion"); + assert.containsOnce($, ".o-mail-Composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "Hello! How are you? ", @@ -221,13 +221,13 @@ QUnit.test("use a canned response some text", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion"); + assert.containsNone($, ".o-mail-Composer-suggestion"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "bluhbluh "); assert.strictEqual($(".o-mail-Composer-input").val(), "bluhbluh "); await insertText(".o-mail-Composer-input", ":"); - assert.containsOnce($, ".o-composer-suggestion"); - await click(".o-composer-suggestion"); + assert.containsOnce($, ".o-mail-Composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "bluhbluh Hello! How are you? ", @@ -243,9 +243,9 @@ QUnit.test('display channel mention suggestions on typing "#"', async (assert) = }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); await insertText(".o-mail-Composer-input", "#"); - assert.containsOnce($, ".o-composer-suggestion-list .o-open"); + assert.containsOnce($, ".o-mail-Composer-suggestionList .o-open"); }); QUnit.test("mention a channel", async (assert) => { @@ -256,11 +256,11 @@ QUnit.test("mention a channel", async (assert) => { }); const { openDiscuss } = await start(); await openDiscuss(channelId); - assert.containsNone($, ".o-composer-suggestion-list .o-open"); + assert.containsNone($, ".o-mail-Composer-suggestionList .o-open"); assert.strictEqual($(".o-mail-Composer-input").val(), ""); await insertText(".o-mail-Composer-input", "#"); - assert.containsOnce($, ".o-composer-suggestion"); - await click(".o-composer-suggestion"); + assert.containsOnce($, ".o-mail-Composer-suggestion"); + await click(".o-mail-Composer-suggestion"); assert.strictEqual( $(".o-mail-Composer-input").val().replace(/\s/, " "), "#General ", diff --git a/addons/mail/static/tests/thread/thread_tests.js b/addons/mail/static/tests/thread/thread_tests.js index ca20a8c14bd..f5f48958722 100644 --- a/addons/mail/static/tests/thread/thread_tests.js +++ b/addons/mail/static/tests/thread/thread_tests.js @@ -210,7 +210,7 @@ QUnit.test("mention a channel with space in the name", async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce($(".o-mail-Message-body"), ".o_channel_redirect"); assert.strictEqual($(".o_channel_redirect").text(), "#General good boy"); @@ -222,7 +222,7 @@ QUnit.test('mention a channel with "&" in the name', async (assert) => { const { openDiscuss } = await start(); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce($(".o-mail-Message-body"), ".o_channel_redirect"); assert.strictEqual($(".o_channel_redirect").text(), "#General & good"); @@ -545,7 +545,7 @@ QUnit.test("Mention a partner with special character (e.g. apostrophe ')", async await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Pyn"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce( $(".o-mail-Message-body"), @@ -577,10 +577,10 @@ QUnit.test("mention 2 different partners that have the same name", async (assert await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Te"); - await click(".o-composer-suggestion:eq(0)"); + await click(".o-mail-Composer-suggestion:eq(0)"); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Te"); - await click(".o-composer-suggestion:eq(1)"); + await click(".o-mail-Composer-suggestion:eq(1)"); await click(".o-mail-Composer-send"); assert.containsOnce($, ".o-mail-Message-body"); assert.containsOnce( @@ -600,7 +600,7 @@ QUnit.test("mention a channel on a second line when the first line contains #", await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#blabla\n"); await insertText(".o-mail-Composer-input", "#"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce($(".o-mail-Message-body"), ".o_channel_redirect"); assert.strictEqual($(".o_channel_redirect").text(), "#General good"); @@ -614,7 +614,7 @@ QUnit.test( const { openDiscuss } = await start(); await openDiscuss(channelId); await insertText(".o-mail-Composer-input", "#"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); const text = $(".o-mail-Composer-input").val(); $(".o-mail-Composer-input").val(text.slice(0, -1)); await insertText(".o-mail-Composer-input", ", test"); @@ -641,10 +641,10 @@ QUnit.test("mention 2 different channels that have the same name", async (assert await openDiscuss(channelId_1); await insertText(".o-mail-Composer-input", "#"); await insertText(".o-mail-Composer-input", "m"); - await click(".o-composer-suggestion:eq(0)"); + await click(".o-mail-Composer-suggestion:eq(0)"); await insertText(".o-mail-Composer-input", "#"); await insertText(".o-mail-Composer-input", "m"); - await click(".o-composer-suggestion:eq(1)"); + await click(".o-mail-Composer-suggestion:eq(1)"); await click(".o-mail-Composer-send"); assert.containsOnce($, ".o-mail-Message-body"); assert.containsOnce( @@ -677,7 +677,7 @@ QUnit.test( await insertText(".o-mail-Composer-input", "email@odoo.com\n"); await insertText(".o-mail-Composer-input", "@"); await insertText(".o-mail-Composer-input", "Te"); - await click(".o-composer-suggestion"); + await click(".o-mail-Composer-suggestion"); await click(".o-mail-Composer-send"); assert.containsOnce( $(".o-mail-Message-body"),