From 9891156c3b6993afb2b2d2f69032c142f6c9e0ce Mon Sep 17 00:00:00 2001 From: FrancoisGe Date: Tue, 30 Jan 2024 13:32:01 +0100 Subject: [PATCH] [FIX] web: CP keep the correct searchValue Before this commit, in the command palette, during a search, it is possible that the value entered is modified. The problem only occurs if the namespace has a debounceDelay bigger than 0 and at least one of its command providers is async. How to reproduce: ================= - Go to the command palette with the @ namespace. - Type a few characters - Re-enter a few characters If you repeat this operation several times, some characters may disappear. Why: ==== The value of the input is modified at each "input" event, but the searchValue in the state is only modified when the debounce trigger "search". Since the command palette template is contained in a slot, each state change causes a complete rendering of the command palette. When the search is executed, the commands are added to the state after each provider has generated them, so a rendering takes place and it is the value contained in state.searchValue that is used for the input and not the value contained in it before the rendering. Solution: ========= No longer modify state.searchValue during the search, but at each input event. closes odoo/odoo#151906 X-original-commit: f4e1350e3a03c8a3a2b6bab8f4f74e278b1d2905 Signed-off-by: Aaron Bohy (aab) --- .../src/core/commands/command_palette.js | 5 +- .../core/commands/command_palette_tests.js | 54 +++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/core/commands/command_palette.js b/addons/web/static/src/core/commands/command_palette.js index 607df17fbdb..9fed6b8185f 100644 --- a/addons/web/static/src/core/commands/command_palette.js +++ b/addons/web/static/src/core/commands/command_palette.js @@ -182,6 +182,7 @@ export class CommandPalette extends Component { const { namespace, searchValue } = this.processSearchValue(config.searchValue || ""); this.switchNamespace(namespace); + this.state.searchValue = searchValue; await this.race.add(this.search(searchValue)); } @@ -306,7 +307,6 @@ export class CommandPalette extends Component { } async search(searchValue) { - this.state.searchValue = searchValue; await this.setCommands(this.state.namespace, { searchValue, activeElement: this.activeElement, @@ -322,7 +322,7 @@ export class CommandPalette extends Component { if (namespace !== "default" && this.state.namespace !== namespace) { this.switchNamespace(namespace); } - this.inputRef.el.value = searchValue; + this.state.searchValue = searchValue; this.searchValuePromise = this.lastDebounceSearch(searchValue).catch(() => { this.searchValuePromise = null; }); @@ -335,6 +335,7 @@ export class CommandPalette extends Component { onKeyDown(ev) { if (ev.key.toLowerCase() === "backspace" && !ev.target.value.length && !ev.repeat) { this.switchNamespace("default"); + this.state.searchValue = ""; this.searchValuePromise = this.lastDebounceSearch("").catch(() => { this.searchValuePromise = null; }); diff --git a/addons/web/static/tests/core/commands/command_palette_tests.js b/addons/web/static/tests/core/commands/command_palette_tests.js index e306a914761..a27d5fae5de 100644 --- a/addons/web/static/tests/core/commands/command_palette_tests.js +++ b/addons/web/static/tests/core/commands/command_palette_tests.js @@ -1595,3 +1595,57 @@ QUnit.test("checks that href is correctly used", async (assert) => { await nextTick(); assert.verifySteps(["command_with_link_clicked"]); }); + +QUnit.test("searchValue must not change without edition", async (assert) => { + const provideDef = makeDeferred(); + let debounceDef; + + patchWithCleanup(browser, { + clearTimeout: () => {}, + setTimeout: async (later) => { + // debounce the search "deb" + await debounceDef; + later(); + }, + }); + + mount(TestComponent, target, { env }); + const providers = [ + { + provide: async (env, { searchValue }) => { + if (searchValue === "abc") { + await provideDef; + } + return [ + { + name: searchValue, + action: () => {}, + }, + ]; + }, + }, + ]; + const config = { + providers, + }; + env.services.dialog.add(CommandPalette, { + config, + }); + + await nextTick(); + + await editSearchBar("abc"); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "abc"); + + debounceDef = makeDeferred(); + await editSearchBar("deb"); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "deb"); + + provideDef.resolve(); + await nextTick(); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "deb"); + + debounceDef.resolve(); + await nextTick(); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "deb"); +});