diff --git a/addons/web/static/src/core/commands/command_category.js b/addons/web/static/src/core/commands/command_category.js
index 0481e1535db..691f104eeb6 100644
--- a/addons/web/static/src/core/commands/command_category.js
+++ b/addons/web/static/src/core/commands/command_category.js
@@ -8,5 +8,6 @@ commandCategoryRegistry
.add("smart_action", {}, { sequence: 15 })
.add("actions", {}, { sequence: 30 })
.add("navbar", {}, { sequence: 40 })
+ .add("view_switcher", {}, { sequence: 50 })
.add("default", {}, { sequence: 100 })
.add("debug", {}, { sequence: 110 });
diff --git a/addons/web/static/src/search/control_panel/control_panel.js b/addons/web/static/src/search/control_panel/control_panel.js
index 4ba141f1a34..248e7cc49bf 100644
--- a/addons/web/static/src/search/control_panel/control_panel.js
+++ b/addons/web/static/src/search/control_panel/control_panel.js
@@ -10,6 +10,8 @@ import { FilterMenu } from "../filter_menu/filter_menu";
import { GroupByMenu } from "../group_by_menu/group_by_menu";
import { SearchBar } from "../search_bar/search_bar";
import { Dropdown } from "@web/core/dropdown/dropdown";
+import { useCommand } from "@web/core/commands/command_hook";
+import { sprintf } from "@web/core/utils/strings";
import { Component, useState, onMounted, useExternalListener, useRef, useEffect } from "@odoo/owl";
@@ -40,6 +42,18 @@ export class ControlPanel extends Component {
this.onScrollThrottledBound = this.onScrollThrottled.bind(this);
+ const { viewSwitcherEntries, viewType } = this.env.config;
+ for (const view of viewSwitcherEntries || []) {
+ useCommand(
+ sprintf(this.env._t("Show %s view"), view.name),
+ () => this.onViewClicked(view.type),
+ {
+ category: "view_switcher",
+ isAvailable: () => view.type !== viewType,
+ }
+ );
+ }
+
useExternalListener(window, "click", this.onWindowClick);
useEffect(() => {
if (
diff --git a/addons/web/static/tests/search/helpers.js b/addons/web/static/tests/search/helpers.js
index aa826c3cb28..a1b6e14017d 100644
--- a/addons/web/static/tests/search/helpers.js
+++ b/addons/web/static/tests/search/helpers.js
@@ -12,6 +12,7 @@ import {
} from "@web/../tests/helpers/utils";
import { dialogService } from "@web/core/dialog/dialog_service";
import { Component, xml } from "@odoo/owl";
+import { commandService } from "@web/core/commands/command_service";
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
import { notificationService } from "@web/core/notifications/notification_service";
import { ormService } from "@web/core/orm_service";
@@ -32,6 +33,7 @@ export function setupControlPanelServiceRegistry() {
serviceRegistry.add("notification", notificationService);
serviceRegistry.add("orm", ormService);
serviceRegistry.add("view", viewService);
+ serviceRegistry.add("command", commandService);
}
export function setupControlPanelFavoriteMenuRegistry() {
diff --git a/addons/web/static/tests/views/helpers.js b/addons/web/static/tests/views/helpers.js
index a38a152e113..eedd7a05d5c 100644
--- a/addons/web/static/tests/views/helpers.js
+++ b/addons/web/static/tests/views/helpers.js
@@ -1,25 +1,24 @@
/** @odoo-module **/
-import { Dialog } from "@web/core/dialog/dialog";
-import { registry } from "@web/core/registry";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import { getFixture, mount, nextTick } from "@web/../tests/helpers/utils";
-import { getDefaultConfig, View } from "@web/views/view";
+import { createDebugContext } from "@web/core/debug/debug_context";
+import { Dialog } from "@web/core/dialog/dialog";
import { MainComponentsContainer } from "@web/core/main_components_container";
-import {
- setupControlPanelFavoriteMenuRegistry,
- setupControlPanelServiceRegistry,
-} from "../search/helpers";
-import { addLegacyMockEnvironment } from "../webclient/helpers";
+import { popoverService } from "@web/core/popover/popover_service";
+import { registry } from "@web/core/registry";
+import { getDefaultConfig, View } from "@web/views/view";
import {
fakeCompanyService,
makeFakeLocalizationService,
makeFakeRouterService,
- makeFakeUserService,
+ makeFakeUserService
} from "../helpers/mock_services";
-import { commandService } from "@web/core/commands/command_service";
-import { popoverService } from "@web/core/popover/popover_service";
-import { createDebugContext } from "@web/core/debug/debug_context";
+import {
+ setupControlPanelFavoriteMenuRegistry,
+ setupControlPanelServiceRegistry
+} from "../search/helpers";
+import { addLegacyMockEnvironment } from "../webclient/helpers";
import { Component, useSubEnv, xml } from "@odoo/owl";
@@ -150,7 +149,6 @@ export function setupViewRegistries() {
serviceRegistry.add("localization", makeFakeLocalizationService()), { force: true };
serviceRegistry.add("popover", popoverService), { force: true };
serviceRegistry.add("company", fakeCompanyService);
- serviceRegistry.add("command", commandService);
}
/**
diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js
index 9a3124fd012..1e8b2c2aedc 100644
--- a/addons/web/static/tests/views/list_view_tests.js
+++ b/addons/web/static/tests/views/list_view_tests.js
@@ -6149,6 +6149,55 @@ QUnit.module("Views", (hooks) => {
await click(target, ".o_back_button");
});
+ QUnit.test(
+ "Navigate between the list and kanban view using the command palette",
+ async (assert) => {
+ serverData.views = {
+ "foo,false,search": ``,
+ "foo,false,list": `
`,
+ "foo,false,kanban": `
+
+
+
+
+
+
+ `,
+ };
+ registry.category("command_categories").add("view_switcher", {});
+
+ const wc = await createWebClient({ serverData });
+ await doAction(wc, {
+ res_model: "foo",
+ type: "ir.actions.act_window",
+ views: [
+ [false, "list"],
+ [false, "kanban"],
+ ],
+ });
+ assert.containsOnce(target, ".o_cp_switch_buttons");
+ assert.containsN(target, ".o_switch_view", 2);
+ assert.containsOnce(target, ".o_list_view");
+
+ triggerHotkey("control+k");
+ await nextTick();
+ assert.containsOnce(target.querySelector(".o_command_category"), ".o_command");
+ let command = target.querySelector(".o_command_category .o_command");
+ assert.strictEqual(command.textContent, "Show Kanban view");
+
+ await click(command);
+ assert.containsOnce(target, ".o_kanban_view");
+
+ triggerHotkey("control+k");
+ await nextTick();
+ assert.containsOnce(target.querySelector(".o_command_category"), ".o_command");
+ command = target.querySelector(".o_command_category .o_command");
+ assert.strictEqual(command.textContent, "Show List view");
+ await click(command);
+ assert.containsOnce(target, ".o_list_view");
+ }
+ );
+
QUnit.test("can sort records when clicking on header", async function (assert) {
serverData.models.foo.fields.foo.sortable = true;