diff --git a/addons/web/static/src/legacy/js/owl_compatibility.js b/addons/web/static/src/legacy/js/owl_compatibility.js index 809f81c6446..5fb07bec237 100644 --- a/addons/web/static/src/legacy/js/owl_compatibility.js +++ b/addons/web/static/src/legacy/js/owl_compatibility.js @@ -289,7 +289,8 @@ odoo.define('web.OwlCompatibility', function (require) { } } - function standaloneAdapter(props) { + const bodyRef = { get el() { return document.body } }; + function standaloneAdapter(props = {}, ref=bodyRef) { const env = owl.Component.env; const app = new App(null, { templates: window.__OWL_TEMPLATES__, @@ -298,8 +299,16 @@ odoo.define('web.OwlCompatibility', function (require) { translatableAttributes: ["data-tooltip"], translateFn: env._t, }); - const node = app.makeNode(ComponentAdapter, props); - return node.component; + if (!("Component" in props)) { + props.Component = owl.Component; + } + const component = app.makeNode(ComponentAdapter, props).component; + Object.defineProperty(component, "el", { + get() { + return ref.el; + } + }); + return component; } /** diff --git a/addons/web/static/src/legacy/legacy_service_provider.js b/addons/web/static/src/legacy/legacy_service_provider.js index a776135a7b8..a2aac62617f 100644 --- a/addons/web/static/src/legacy/legacy_service_provider.js +++ b/addons/web/static/src/legacy/legacy_service_provider.js @@ -6,6 +6,7 @@ import Context from "web.Context"; import { browser } from "../core/browser/browser"; import { registry } from "../core/registry"; import { mapDoActionOptionAPI } from "./backend_utils"; +import { wrapSuccessOrFail } from "@web/legacy/utils"; export const legacyServiceProvider = { dependencies: ["effect", "action"], @@ -25,6 +26,29 @@ export const legacyServiceProvider = { const legacyOptions = mapDoActionOptionAPI(payload.options); services.action.doAction(payload.action, legacyOptions); }); + + browser.addEventListener("execute-action", (ev) => { + const payload = ev.detail; + const buttonContext = new Context(payload.action_data.context).eval(); + const envContext = new Context(payload.env.context).eval(); + wrapSuccessOrFail( + services.action.doActionButton({ + args: payload.action_data.args, + buttonContext: buttonContext, + context: envContext, + close: payload.action_data.close, + resModel: payload.env.model, + name: payload.action_data.name, + resId: payload.env.currentID || null, + resIds: payload.env.resIDs, + special: payload.action_data.special, + type: payload.action_data.type, + onClose: payload.on_closed, + effect: payload.action_data.effect, + }), + payload + ); + }); }, }; diff --git a/addons/web/static/tests/legacy/owl_compatibility_tests.js b/addons/web/static/tests/legacy/owl_compatibility_tests.js index 8296fd01041..c6355e1b133 100644 --- a/addons/web/static/tests/legacy/owl_compatibility_tests.js +++ b/addons/web/static/tests/legacy/owl_compatibility_tests.js @@ -9,13 +9,18 @@ odoo.define('web.OwlCompatibilityTests', function (require) { ComponentAdapter, ComponentWrapper, WidgetAdapterMixin, + standaloneAdapter, } = require('web.OwlCompatibility'); const testUtils = require('web.test_utils'); const Widget = require('web.Widget'); + const Dialog = require("web.Dialog"); const { registry } = require("@web/core/registry"); const { LegacyComponent } = require("@web/legacy/legacy_component"); const { mapLegacyEnvToWowlEnv, useWowlService } = require("@web/legacy/utils"); + const { legacyServiceProvider } = require("@web/legacy/legacy_service_provider"); + const { click } = require("@web/../tests/helpers/utils"); + const makeTestEnvironment = require("web.test_env"); const { makeTestEnv } = require("@web/../tests/helpers/mock_env"); const { getFixture, mount, useLogLifeCycle, destroy } = require("@web/../tests/helpers/utils"); @@ -807,6 +812,69 @@ odoo.define('web.OwlCompatibilityTests', function (require) { assert.verifySteps(["GED", "AAB", "MCM"]); }); + QUnit.test("standaloneAdapter can trigger in the DOM and execute action", async (assert) => { + assert.expect(3) + const done = assert.async(); + + const MyDialog = Dialog.extend({ + async start() { + const res = await this._super(...arguments); + const btn = document.createElement("button"); + btn.classList.add("myButton"); + btn.addEventListener("click", () => { + this.trigger_up("execute_action", { + action_data: {}, + env: {}, + }); + }); + this.el.appendChild(btn); + return res; + } + }); + + const dialogOpened = makeTestPromise(); + class MyComp extends Component { + setup() { + onWillDestroy(() => { + this.dialog.destroy(); + }) + } + async spawnDialog() { + const parent = standaloneAdapter(); + this.dialog = new MyDialog(parent); + await this.dialog.open(); + dialogOpened.resolve(); + } + } + MyComp.template = xml`