diff --git a/addons/web/static/src/legacy/action_adapters.js b/addons/web/static/src/legacy/action_adapters.js
index e210f4abfb4..b8b7ad46a79 100644
--- a/addons/web/static/src/legacy/action_adapters.js
+++ b/addons/web/static/src/legacy/action_adapters.js
@@ -120,6 +120,8 @@ class ActionAdapter extends ComponentAdapter {
type: "warning",
});
}
+ } else if (ev.name === "history_back") {
+ this.wowlEnv.config.historyBack();
} else {
super._trigger_up(ev);
}
diff --git a/addons/web/static/src/views/view.js b/addons/web/static/src/views/view.js
index ee4f4282fb4..5f411d49b45 100644
--- a/addons/web/static/src/views/view.js
+++ b/addons/web/static/src/views/view.js
@@ -44,6 +44,7 @@ export function getDefaultConfig() {
],
getDisplayName: () => displayName,
getPagerProps: () => {},
+ historyBack: () => {},
setDisplayName: (newDisplayName) => {
displayName = newDisplayName;
},
diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js
index 4edb86640b1..ed519c31427 100644
--- a/addons/web/static/src/webclient/actions/action_service.js
+++ b/addons/web/static/src/webclient/actions/action_service.js
@@ -75,7 +75,7 @@ const CTX_KEY_REGEX = /^(?:(?:default_|search_default_|show_).+|.+_view_ref|grou
// only register this template once for all dynamic classes ControllerComponent
const ControllerComponentTemplate = xml``;
+/>`;
function makeActionManager(env) {
const keepLast = new KeepLast();
@@ -539,6 +539,14 @@ function makeActionManager(env) {
env.services.title.setParts({ action: controller.displayName });
}
};
+ controller.config.historyBack = () => {
+ const previousController = controllerStack[controllerStack.length - 2];
+ if (previousController && !dialog) {
+ restore(previousController.jsId);
+ } else {
+ _executeCloseAction();
+ }
+ };
class ControllerComponent extends Component {
setup() {
@@ -652,14 +660,6 @@ function makeActionManager(env) {
dialogCloseResolve();
}
}
- onHistoryBack() {
- const previousController = controllerStack[controllerStack.length - 2];
- if (previousController && !dialog) {
- restore(previousController.jsId);
- } else {
- _executeCloseAction();
- }
- }
}
ControllerComponent.template = ControllerComponentTemplate;
ControllerComponent.Component = controller.Component;
diff --git a/addons/web/static/tests/webclient/actions/close_action_tests.js b/addons/web/static/tests/webclient/actions/close_action_tests.js
index 6e4b0540117..0437384464e 100644
--- a/addons/web/static/tests/webclient/actions/close_action_tests.js
+++ b/addons/web/static/tests/webclient/actions/close_action_tests.js
@@ -1,8 +1,15 @@
/** @odoo-module **/
import testUtils from "web.test_utils";
-import { click, legacyExtraNextTick, nextTick } from "../../helpers/utils";
+import { click, legacyExtraNextTick, nextTick, patchWithCleanup } from "../../helpers/utils";
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
+import FormController from "web.FormController";
+import { makeFakeUserService } from "@web/../tests/helpers/mock_services";
+import ListController from "web.ListController";
+import { PivotView } from "@web/views/pivot/pivot_view";
+import { registry } from "@web/core/registry";
+
+const serviceRegistry = registry.category("services");
let serverData;
@@ -94,6 +101,13 @@ QUnit.module("ActionManager", (hooks) => {
QUnit.test("history back calls on_close handler of dialog action", async function (assert) {
assert.expect(4);
+ let controller;
+ patchWithCleanup(FormController.prototype, {
+ init() {
+ this._super(...arguments);
+ controller = this;
+ },
+ });
const webClient = await createWebClient({ serverData });
function onClose() {
assert.step("on_close");
@@ -101,8 +115,7 @@ QUnit.module("ActionManager", (hooks) => {
// open a new dialog form
await doAction(webClient, 5, { onClose });
assert.containsOnce(webClient.el, ".modal");
- const ev = new Event("history-back", { bubbles: true, cancelable: true });
- webClient.el.querySelector(".o_view_controller").dispatchEvent(ev);
+ controller.trigger_up("history_back");
assert.verifySteps(["on_close"], "should have called the on_close handler");
await nextTick();
assert.containsNone(webClient.el, ".modal");
@@ -110,6 +123,13 @@ QUnit.module("ActionManager", (hooks) => {
QUnit.test("history back called within on_close", async function (assert) {
assert.expect(7);
+ let controller;
+ patchWithCleanup(ListController.prototype, {
+ init() {
+ this._super(...arguments);
+ controller = this;
+ },
+ });
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1);
@@ -118,8 +138,7 @@ QUnit.module("ActionManager", (hooks) => {
assert.containsOnce(webClient, ".o_list_view");
function onClose() {
- const ev = new Event("history-back", { bubbles: true, cancelable: true });
- webClient.el.querySelector(".o_view_controller").dispatchEvent(ev);
+ controller.trigger_up("history_back");
assert.step("on_close");
}
// open a new dialog form
@@ -138,6 +157,13 @@ QUnit.module("ActionManager", (hooks) => {
"history back calls on_close handler of dialog action with 2 breadcrumbs",
async function (assert) {
assert.expect(7);
+ let controller;
+ patchWithCleanup(ListController.prototype, {
+ init() {
+ this._super(...arguments);
+ controller = this;
+ },
+ });
const webClient = await createWebClient({ serverData });
await doAction(webClient, 1); // kanban
await doAction(webClient, 3); // list
@@ -149,8 +175,7 @@ QUnit.module("ActionManager", (hooks) => {
await doAction(webClient, 5, { onClose });
assert.containsOnce(webClient.el, ".modal");
assert.containsOnce(webClient.el, ".o_list_view");
- const ev = new Event("history-back", { bubbles: true, cancelable: true });
- webClient.el.querySelector(".o_view_controller").dispatchEvent(ev);
+ controller.trigger_up("history_back");
assert.verifySteps(["on_close"], "should have called the on_close handler");
await nextTick();
await legacyExtraNextTick();
@@ -159,6 +184,46 @@ QUnit.module("ActionManager", (hooks) => {
}
);
+ QUnit.test(
+ "history back calls on_close handler of dialog action with 2 breadcrumbs (2)",
+ async function (assert) {
+ assert.expect(7);
+ let pivot;
+ patchWithCleanup(PivotView.prototype, {
+ setup() {
+ this._super(...arguments);
+ pivot = this;
+ },
+ });
+ serverData.views["partner,false,pivot"] = "";
+ serviceRegistry.add("user", makeFakeUserService());
+ const webClient = await createWebClient({ serverData });
+ await doAction(webClient, 1); // kanban
+ await doAction(webClient, {
+ id: 3,
+ xml_id: "action_3",
+ name: "Partners",
+ res_model: "partner",
+ type: "ir.actions.act_window",
+ views: [[false, "pivot"]],
+ }); // pivot
+ assert.containsOnce(webClient.el, ".o_pivot_view");
+ function onClose() {
+ assert.step("on_close");
+ }
+ // open a new dialog form
+ await doAction(webClient, 5, { onClose });
+ assert.containsOnce(webClient.el, ".modal");
+ assert.containsOnce(webClient.el, ".o_pivot_view");
+ pivot.env.config.historyBack();
+ assert.verifySteps(["on_close"], "should have called the on_close handler");
+ await nextTick();
+ await legacyExtraNextTick();
+ assert.containsOnce(webClient.el, ".o_pivot_view");
+ assert.containsNone(webClient.el, ".modal");
+ }
+ );
+
QUnit.test("web client is not deadlocked when a view crashes", async function (assert) {
assert.expect(3);
const readOnFirstRecordDef = testUtils.makeTestPromise();
diff --git a/addons/web/static/tests/webclient/actions/misc_tests.js b/addons/web/static/tests/webclient/actions/misc_tests.js
index 03b8eb5ea0b..bb74b579762 100644
--- a/addons/web/static/tests/webclient/actions/misc_tests.js
+++ b/addons/web/static/tests/webclient/actions/misc_tests.js
@@ -21,6 +21,7 @@ import {
setupWebClientRegistries,
} from "./../helpers";
import * as cpHelpers from "@web/../tests/search/helpers";
+import ListController from "web.ListController";
let serverData;
// legacy stuff
@@ -456,13 +457,18 @@ QUnit.module("ActionManager", (hooks) => {
QUnit.test('handles "history_back" event', async function (assert) {
assert.expect(3);
+ let controller;
+ patchWithCleanup(ListController.prototype, {
+ init() {
+ this._super(...arguments);
+ controller = this;
+ },
+ });
const webClient = await createWebClient({ serverData });
await doAction(webClient, 4);
await doAction(webClient, 3);
assert.containsN(webClient, ".o_control_panel .breadcrumb-item", 2);
- // simulate an "history-back" event
- const ev = new Event("history-back", { bubbles: true, cancelable: true });
- webClient.el.querySelector(".o_view_controller").dispatchEvent(ev);
+ controller.trigger_up("history_back");
await testUtils.nextTick();
await legacyExtraNextTick();
assert.containsOnce(webClient, ".o_control_panel .breadcrumb-item");