diff --git a/addons/web/static/src/core/commands/command_palette.js b/addons/web/static/src/core/commands/command_palette.js index 85ed664ca35..72218535caf 100644 --- a/addons/web/static/src/core/commands/command_palette.js +++ b/addons/web/static/src/core/commands/command_palette.js @@ -82,7 +82,7 @@ export class CommandPalette extends Component { this.DefaultCommandItem = DefaultCommandItem; this.activeElement = useService("ui").activeElement; this.defaultDebounceSearch = debounce.apply(this, [this.search, 0]); - useAutofocus(); + this.inputRef = useAutofocus(); useHotkey("Enter", () => this.executeSelectedCommand(), { bypassEditableProtection: true }); useHotkey("ArrowUp", () => this.selectCommandAndScrollTo("PREV"), { @@ -267,6 +267,9 @@ export class CommandPalette extends Component { searchValue, activeElement: this.activeElement, }); + if (this.inputRef.el) { + this.inputRef.el.focus(); + } } debounceSearch(value) { @@ -299,5 +302,11 @@ export class CommandPalette extends Component { } return { namespace, searchValue }; } + + switchNamespace(namespace) { + const searchValue = `${namespace}${this.clearSearchValue}`; + this.state.searchValue = searchValue; + this.debounceSearch(searchValue); + } } CommandPalette.template = "web.CommandPalette"; diff --git a/addons/web/static/src/core/commands/command_palette.scss b/addons/web/static/src/core/commands/command_palette.scss index 2d09af1b9ee..37676908fc3 100644 --- a/addons/web/static/src/core/commands/command_palette.scss +++ b/addons/web/static/src/core/commands/command_palette.scss @@ -122,6 +122,12 @@ padding: 0.6rem 1.3em; border-top: 1px solid rgba(0, 0, 0, 0.1); + .o_namespace:hover { + color: $o-brand-primary; + cursor: pointer; + text-decoration: underline dotted; + } + .o_promote { color: $o-brand-primary; font-weight: 900; diff --git a/addons/web/static/src/core/commands/command_palette.xml b/addons/web/static/src/core/commands/command_palette.xml index 278487c8c2a..be8979e3d0c 100644 --- a/addons/web/static/src/core/commands/command_palette.xml +++ b/addons/web/static/src/core/commands/command_palette.xml @@ -37,7 +37,7 @@ diff --git a/addons/web/static/src/core/commands/command_service.js b/addons/web/static/src/core/commands/command_service.js index c0f117cd3a2..4a380e9a747 100644 --- a/addons/web/static/src/core/commands/command_service.js +++ b/addons/web/static/src/core/commands/command_service.js @@ -43,6 +43,10 @@ class DefaultFooter extends Component { .map((el) => ({ namespace: el[0], name: el[1].name })) .filter((el) => el.name); } + + onClick(namespace) { + this.props.switchNamespace(namespace); + } } DefaultFooter.template = xml` @@ -50,7 +54,9 @@ DefaultFooter.template = xml` , and - + + + `; diff --git a/addons/web/static/tests/core/commands/command_service_tests.js b/addons/web/static/tests/core/commands/command_service_tests.js index 20d11960235..61fb53c601d 100644 --- a/addons/web/static/tests/core/commands/command_service_tests.js +++ b/addons/web/static/tests/core/commands/command_service_tests.js @@ -490,6 +490,74 @@ QUnit.test("footer displays the right tips", async (assert) => { ); }); +QUnit.test("namespaces display in the footer are still clickable", async (assert) => { + const action = () => {}; + clearRegistryWithCleanup(commandProviderRegistry); + commandProviderRegistry.add("@", { + namespace: "@", + provide: () => [ + { + name: "Command@", + action, + }, + ], + }); + commandProviderRegistry.add("#", { + namespace: "#", + provide: () => [ + { + name: "Command#", + action, + }, + ], + }); + + const commandSetupRegistry = registry.category("command_setup"); + clearRegistryWithCleanup(commandSetupRegistry); + commandSetupRegistry.add("default", {}); + commandSetupRegistry.add("@", { + name: "users", + }); + commandSetupRegistry.add("#", { + name: "channels", + }); + await mount(TestComponent, target, { env }); + + // Open palette + triggerHotkey("control+k"); + await nextTick(); + assert.strictEqual( + target.querySelector(".o_command_palette_footer").textContent, + "TIP — search for @users and #channels" + ); + assert.deepEqual( + [...target.querySelectorAll(".o_command")].map((el) => el.textContent), + [] + ); + + await click(target.querySelectorAll(".o_command_palette_footer .o_namespace")[0]); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "@"); + assert.deepEqual( + [...target.querySelectorAll(".o_command")].map((el) => el.textContent), + ["Command@"] + ); + + await editSearchBar("Com"); + await click(target.querySelectorAll(".o_command_palette_footer .o_namespace")[1]); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "#Com"); + assert.deepEqual( + [...target.querySelectorAll(".o_command")].map((el) => el.textContent), + ["Command#"] + ); + + await click(target.querySelectorAll(".o_command_palette_footer .o_namespace")[0]); + assert.strictEqual(target.querySelector(".o_command_palette_search input").value, "@Com"); + assert.deepEqual( + [...target.querySelectorAll(".o_command")].map((el) => el.textContent), + ["Command@"] + ); +}); + QUnit.test("defined multiple providers with the same namespace", async (assert) => { assert.expect(2);