[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) <aab@odoo.com>
This commit is contained in:
FrancoisGe
2024-01-31 17:13:52 +00:00
parent b7c68826d9
commit 9891156c3b
2 changed files with 57 additions and 2 deletions
@@ -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;
});
@@ -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");
});