[FIX] web: CP lose init seach value

Before this commit, in the HomeMenu, typing several characters very
quickly could result in some not being present in the command palette
when it was opened.

Exemple:
In the HomeMenu,if I type "a" "b" "c" quickly the command palette will
open with the search value "/a" or "/ac". So we have lost the second
and/or third character.

Why:
All characters typed between the first and the moment the command pallet
is not yet mounted are lost because the search bar of the command pallet
is not yet available.

Solution:
If you use openPalette and the command palette is already open.
The configuration of the command pallet will be overwritten by the one
given to openPalette.
In our case, we can pass new configurations with the correct searchValue
until the command palette is returned.

closes odoo/odoo#95492

Task-id: 2906623
Related: odoo/enterprise#29230
Signed-off-by: Géry Debongnie <ged@odoo.com>
This commit is contained in:
FrancoisGe
2022-07-12 04:21:33 +02:00
parent 2ee4251d07
commit 1f4e583ba2
11 changed files with 118 additions and 31 deletions
+5 -5
View File
@@ -96,7 +96,7 @@ export const Many2OneAvatarUser = Many2OneAvatar.extend(M2XAvatarMixin, {
hotkey: "alt+i",
},
async action() {
return env.services.command.openPalette({
return {
configByNamespace: {
default: {
emptyMessage: env._t("No users found"),
@@ -104,7 +104,7 @@ export const Many2OneAvatarUser = Many2OneAvatar.extend(M2XAvatarMixin, {
},
placeholder: env._t("Select a user..."),
providers: [{ provide }],
});
};
},
});
core.bus.trigger("set_legacy_command", "web.Many2OneAvatar.assignTo", getCommandDefinition, self.el);
@@ -277,7 +277,7 @@ export const Many2ManyAvatarUser = FieldMany2ManyTagsAvatar.extend(M2MAvatarMixi
hotkey: "alt+i",
},
action() {
return env.services.command.openPalette({
return {
configByNamespace: {
default: {
emptyMessage: env._t("No users found"),
@@ -285,9 +285,9 @@ export const Many2ManyAvatarUser = FieldMany2ManyTagsAvatar.extend(M2MAvatarMixi
},
placeholder: env._t("Select a user..."),
providers: [{ provide }],
});
};
},
})
});
core.bus.trigger("set_legacy_command", "web.FieldMany2ManyTagsAvatar.assignTo", getCommandDefinition)
getCommandDefinition = (env) => ({
@@ -10,7 +10,15 @@ import { fuzzyLookup } from "@web/core/utils/search";
import { debounce } from "@web/core/utils/timing";
import { escapeRegExp } from "../utils/strings";
const { Component, onWillStart, useRef, useState, markRaw, useExternalListener } = owl;
const {
Component,
onWillStart,
onWillDestroy,
useRef,
useState,
markRaw,
useExternalListener,
} = owl;
const DEFAULT_PLACEHOLDER = _lt("Search...");
const DEFAULT_EMPTY_MESSAGE = _lt("No result found");
@@ -81,6 +89,12 @@ DefaultCommandItem.template = "web.DefaultCommandItem";
export class CommandPalette extends Component {
setup() {
if (this.props.bus) {
const setConfig = ({ detail }) => this.setCommandPaletteConfig(detail);
this.props.bus.addEventListener(`SET-CONFIG`, setConfig);
onWillDestroy(() => this.props.bus.removeEventListener(`SET-CONFIG`, setConfig));
}
this.keyId = 1;
this.keepLast = new KeepLast();
this._sessionId = CommandPalette.lastSessionId++;
@@ -3,7 +3,7 @@
import { registry } from "@web/core/registry";
import { CommandPalette } from "./command_palette";
const { Component, xml } = owl;
const { Component, xml, EventBus } = owl;
/**
* @typedef {import("./command_palette").CommandPaletteConfig} CommandPaletteConfig
@@ -65,6 +65,7 @@ export const commandService = {
const registeredCommands = new Map();
let nextToken = 0;
let isPaletteOpened = false;
const bus = new EventBus();
hotkeyService.add("control+k", openMainPalette, {
bypassEditableProtection: true,
@@ -125,11 +126,11 @@ export const commandService = {
/**
* @param {CommandPaletteConfig} config
* @param {Function} onClose called when the command palette is closed
* @returns config if the command palette is already open
*/
function openPalette(config, onClose) {
if (isPaletteOpened) {
return config;
bus.trigger("SET-CONFIG", config);
return;
}
// Open Command Palette dialog
@@ -138,6 +139,7 @@ export const commandService = {
CommandPalette,
{
config,
bus,
},
{
onClose: () => {
@@ -161,16 +163,18 @@ export const commandService = {
}
const registration = Object.assign({}, command, options);
if (registration.hotkey) {
registration.removeHotkey = hotkeyService.add(
registration.hotkey,
registration.action,
{
activeElement: registration.activeElement,
global: registration.global,
const action = async () => {
const commandService = env.services.command;
const config = await command.action();
if (!isPaletteOpened && config) {
commandService.openPalette(config);
}
);
};
registration.removeHotkey = hotkeyService.add(registration.hotkey, action, {
activeElement: registration.activeElement,
global: registration.global,
});
}
const token = nextToken++;
@@ -50,7 +50,7 @@ export class DebugMenu extends DebugMenuBasic {
configByNamespace,
providers: [provider],
};
return this.command.openPalette(commandPaletteConfig);
return commandPaletteConfig;
},
{
category: "debug",
@@ -2577,10 +2577,10 @@ var PriorityWidget = AbstractField.extend({
hotkey: "alt+r",
},
action() {
return env.services.command.openPalette({
return {
placeholder: env._t("Set a priority..."),
providers: [{ provide }],
})
};
},
});
core.bus.trigger("set_legacy_command", "web.PriorityWidget.setPriority", getCommandDefinition);
@@ -2784,10 +2784,10 @@ var StateSelectionWidget = AbstractField.extend({
hotkey: "alt+shift+r",
},
action() {
return env.services.command.openPalette({
return {
placeholder: env._t("Set a kanban state..."),
providers: [{ provide }],
})
}
},
});
core.bus.trigger("set_legacy_command", "web.StateSelectionWidget.setKanbanState", getCommandDefinition);
@@ -3221,11 +3221,10 @@ var FieldStatus = AbstractField.extend({
hotkey: "alt+shift+x",
},
action() {
return env.services.command.openPalette({
return {
placeholder: statusLabel,
providers: [{ provide }],
});
},
} },
});
core.bus.trigger("set_legacy_command", "web.FieldStatus.moveToStage", getCommandDefinition);
}
@@ -56,10 +56,10 @@ export class PriorityField extends Component {
};
const name = this.env._t("Set priority...");
const action = () => {
return commandService.openPalette({
return {
placeholder: this.env._t("Set a priority..."),
providers: [{ provide }],
});
};
};
const options = {
category: "smart_action",
@@ -57,10 +57,10 @@ export class StateSelectionField extends Component {
};
const name = this.env._t("Set kanban state...");
const action = () => {
return commandService.openPalette({
return {
placeholder: this.env._t("Set a kanban state..."),
providers: [{ provide }],
});
};
};
const options = {
category: "smart_action",
@@ -128,10 +128,10 @@ export class StatusBarField extends Component {
};
const name = sprintf(this.env._t(`Move to %s...`), escape(this.props.displayName));
const action = () => {
return commandService.openPalette({
return {
placeholder: name,
providers: [{ provide }],
});
};
};
const options = {
category: "smart_action",
@@ -218,6 +218,39 @@ QUnit.test("global command with hotkey", async (assert) => {
assert.verifySteps([globalHotkey]);
});
QUnit.test("open command palette with command config", async (assert) => {
const hotkey = "alt+a";
const action = () => {};
const provide = () => [
{
name: "Command1",
action,
},
];
const providers = [{ provide }];
env.services.command.add(
"test",
() => {
return {
providers,
};
},
{
hotkey,
}
);
await mount(TestComponent, target, { env });
triggerHotkey("alt+a");
await nextTick();
assert.containsN(target, ".o_command", 1);
assert.deepEqual(
[...target.querySelectorAll(".o_command span:first-child")].map((el) => el.textContent),
["Command1"]
);
});
QUnit.test("data-hotkey added to command palette", async (assert) => {
assert.expect(8);
@@ -1046,3 +1079,39 @@ QUnit.test("openMainPalette with onClose", async (assert) => {
await nextTick();
assert.verifySteps(["onClose"]);
});
QUnit.test("uses openPalette to modify the config used by the command palette", async (assert) => {
const action = () => {};
env.services.command.add("Command1", action);
await mount(TestComponent, target, { env });
triggerHotkey("control+k");
await nextTick();
assert.deepEqual(target.querySelector(".o_command_palette_search input").value, "");
assert.containsN(target, ".o_command", 1);
assert.deepEqual(
[...target.querySelectorAll(".o_command span:first-child")].map((el) => el.textContent),
["Command1"]
);
const provide = () => [
{
name: "Command2",
action,
},
];
const providers = [{ provide }];
const configCustom = {
searchValue: "Command",
providers,
};
env.services.command.openPalette(configCustom);
await nextTick();
assert.containsN(target, ".o_command", 1);
assert.deepEqual(
[...target.querySelectorAll(".o_command span:first-child")].map((el) => el.textContent),
["Command2"]
);
assert.deepEqual(target.querySelector(".o_command_palette_search input").value, "Command");
});
@@ -50,6 +50,7 @@ function prepareRegistriesWithCleanup() {
cloneRegistryWithCleanup(registry.category("views"));
cloneRegistryWithCleanup(registry.category("error_handlers"));
cloneRegistryWithCleanup(registry.category("command_provider"));
cloneRegistryWithCleanup(registry.category("command_setup"));
cloneRegistryWithCleanup(registry.category("view_widgets"));
cloneRegistryWithCleanup(registry.category("fields"));