[REF] web, *: change dialog service api

* iap, web_tour

This commit changes the API of the dialog service.

Before, the API gave 2 functions: "open" and "close".
Now, there is only one function "add" which is the same as "open"
but returning a callback that does the close.

closes odoo-dev/odoo#940

Related: odoo-dev/enterprise#171
Signed-off-by: Géry Debongnie (ged) <ged@openerp.com>
This commit is contained in:
Michael Mattiello (mcm)
2021-06-18 21:31:33 +02:00
committed by Aaron Bohy
parent d1b260b424
commit 580533ecfb
22 changed files with 159 additions and 170 deletions
@@ -41,7 +41,7 @@ function insufficientCreditHandler(env, error, originalError) {
}
const { data } = originalError;
if (data && data.name === "odoo.addons.iap.tools.iap_tools.InsufficientCreditError") {
env.services.dialog.open(InsufficientCreditDialog, {
env.services.dialog.add(InsufficientCreditDialog, {
errorData: JSON.parse(data.message),
});
return true;
@@ -24,6 +24,7 @@ ConfirmationDialog.props = {
body: String,
confirm: Function,
cancel: Function,
close: Function,
};
ConfirmationDialog.bodyTemplate = "web.ConfirmationDialogBody";
+1 -3
View File
@@ -15,7 +15,6 @@ export class Dialog extends Component {
);
}
this.modalRef = useRef("modal");
this.dialogService = useService("dialog");
useActiveElement("modal");
useHotkey(
"escape",
@@ -33,7 +32,6 @@ export class Dialog extends Component {
this.size = this.constructor.size;
this.technical = this.constructor.technical;
this.title = this.constructor.title;
this.__id = null;
}
/**
@@ -41,7 +39,7 @@ export class Dialog extends Component {
* @private
*/
close() {
this.dialogService.close(this.__id);
this.props.close();
}
}
@@ -16,37 +16,31 @@ ErrorHandler.template = tags.xml`<t t-component="props.dialog.class" t-props="pr
export class DialogContainer extends Component {
setup() {
this.dialogs = {};
this.props.bus.on("ADD", this, (dialog) => {
this.dialogs[dialog.id] = dialog;
this.render();
});
this.props.bus.on("CLOSE", this, (id) => {
this.closeDialog(id);
});
this.props.bus.on("ADD", this, this.add);
this.props.bus.on("REMOVE", this, this.remove);
}
__destroy() {
this.props.bus.off("ADD", this);
this.props.bus.off("REMOVE", this);
super.__destroy();
}
onDialogClosed(id) {
this.closeDialog(id);
add(params) {
this.dialogs[params.id] = params;
this.render();
}
closeDialog(id) {
remove(id) {
if (this.dialogs[id]) {
if (this.dialogs[id].options && this.dialogs[id].options.onCloseCallback) {
this.dialogs[id].options.onCloseCallback();
}
delete this.dialogs[id];
this.render();
}
}
errorCallBack(id) {
return () => this.closeDialog(id);
onDialogClosed(id) {
this.remove(id);
}
__destroy() {
this.props.bus.off("ADD", this);
this.props.bus.off("CLOSE", this);
super.__destroy();
errorCallBack(id) {
return () => this.remove(id);
}
}
DialogContainer.components = { ErrorHandler };
@@ -7,40 +7,56 @@ import { DialogContainer } from "./dialog_container";
const { core } = owl;
const { EventBus } = core;
/**
* @typedef {{
* onClose?(): void;
* }} DialogServiceInterfaceAddOptions
*/
/**
* @typedef {{
* add(
* Component: any,
* props: {},
* options?: DialogServiceInterfaceAddOptions
* ): () => void;
* }} DialogServiceInterface
*/
export const dialogService = {
/** @returns {DialogServiceInterface} */
start() {
const bus = new EventBus();
let dialogId = 0;
function open(dialogClass, props, options) {
if (!(dialogClass.prototype instanceof Dialog)) {
throw new Error(dialogClass.name + " must be a subclass of Dialog");
}
const id = ++dialogId;
class dialogController extends dialogClass {
setup() {
super.setup();
this.__id = id;
}
}
const dialog = {
id,
class: dialogController,
props,
options,
};
bus.trigger("ADD", dialog);
return id;
}
function close(id) {
bus.trigger("CLOSE", id);
}
registry.category("main_components").add("DialogContainer", {
Component: DialogContainer,
props: { bus },
});
return { open, close };
function add(dialogClass, props, options = {}) {
if (!(dialogClass.prototype instanceof Dialog)) {
throw new Error(`${dialogClass.name} must be a subclass of Dialog`);
}
const id = ++dialogId;
function close() {
if (options.onClose) {
options.onClose();
}
bus.trigger("REMOVE", id);
}
const dialog = {
id,
class: dialogClass,
props: { ...props, close },
};
bus.trigger("ADD", dialog);
return close;
}
return { add };
},
};
@@ -30,7 +30,7 @@ const errorDialogRegistry = registry.category("error_dialogs");
*/
function corsErrorHandler(env, error) {
if (error instanceof UncaughtCorsError) {
env.services.dialog.open(NetworkErrorDialog, {
env.services.dialog.add(NetworkErrorDialog, {
traceback: error.traceback,
message: error.message,
name: error.name,
@@ -51,7 +51,7 @@ errorHandlerRegistry.add("corsErrorHandler", corsErrorHandler, { sequence: 95 })
*/
function clientErrorHandler(env, error) {
if (error instanceof UncaughtClientError) {
env.services.dialog.open(ClientErrorDialog, {
env.services.dialog.add(ClientErrorDialog, {
traceback: error.traceback,
message: error.message,
name: error.name,
@@ -90,7 +90,7 @@ function rpcErrorHandler(env, error, originalError) {
ErrorComponent = errorDialogRegistry.get(exceptionName);
}
env.services.dialog.open(ErrorComponent || RPCErrorDialog, {
env.services.dialog.add(ErrorComponent || RPCErrorDialog, {
traceback: error.traceback,
message: originalError.message,
name: originalError.name,
@@ -170,7 +170,7 @@ function emptyRejectionErrorHandler(env, error) {
if (!(error instanceof UncaughtPromiseError)) {
return false;
}
env.services.dialog.open(ClientErrorDialog, {
env.services.dialog.add(ClientErrorDialog, {
traceback: error.traceback,
message: error.message,
name: error.name,
@@ -191,7 +191,7 @@ errorHandlerRegistry.add("emptyRejectionErrorHandler", emptyRejectionErrorHandle
* @returns {boolean}
*/
function defaultHandler(env, error) {
env.services.dialog.open(ErrorDialog, {
env.services.dialog.add(ErrorDialog, {
traceback: error.traceback,
message: error.message,
name: error.name,
@@ -103,7 +103,7 @@ class ActionAdapter extends ComponentAdapter {
}
}
WarningDialog.bodyTemplate = tags.xml`<t t-esc="props.message"/>`;
this.dialogs.open(WarningDialog, {
this.dialogs.add(WarningDialog, {
title: payload.title,
message: payload.message,
});
@@ -190,7 +190,10 @@ function viewRecordRules({ accessRights, action, env }) {
}
class FieldViewGetDialog extends Dialog {}
FieldViewGetDialog.props = Object.assign({}, Dialog.props, { arch: { type: String } });
FieldViewGetDialog.props = Object.assign({}, Dialog.props, {
arch: { type: String },
close: Function,
});
FieldViewGetDialog.bodyTemplate = tags.xml`<pre t-esc="props.arch"/>`;
FieldViewGetDialog.title = _lt("Fields View Get");
@@ -390,7 +393,7 @@ function fieldsViewGet({ component, env }) {
const props = {
arch: component.props.viewInfo.arch,
};
env.services.dialog.open(FieldViewGetDialog, props);
env.services.dialog.add(FieldViewGetDialog, props);
},
sequence: 340,
};
@@ -435,7 +438,7 @@ function setDefaults({ action, component, env }) {
type: "item",
description: env._t("Set Defaults"),
callback: () => {
env.services.dialog.open(SetDefaultDialog, {
env.services.dialog.add(SetDefaultDialog, {
res_model: action.res_model,
component,
});
@@ -453,7 +456,7 @@ function viewMetadata({ action, component, env }) {
type: "item",
description: env._t("View Metadata"),
callback: () => {
env.services.dialog.open(GetMetadataDialog, {
env.services.dialog.add(GetMetadataDialog, {
res_model: action.res_model,
selectedIds,
});
@@ -43,7 +43,7 @@ function legacyRPCErrorHandler(env, error, originalError) {
ErrorComponent = errorDialogRegistry.get(exceptionName);
}
env.services.dialog.open(ErrorComponent || RPCErrorDialog, {
env.services.dialog.add(ErrorComponent || RPCErrorDialog, {
traceback: originalError.traceback || originalError.stack,
message: originalError.message,
name: originalError.name,
@@ -32,6 +32,7 @@ ActionDialog.props = {
ActionComponent: { optional: true },
actionProps: { optional: true },
title: { optional: true },
close: Function,
};
/**
@@ -550,14 +550,14 @@ function makeActionManager(env) {
}
const { onClose } = dialog;
const oldDialogId = dialog.id;
if (oldDialogId) {
const removeOldDialog = dialog.remove;
if (removeOldDialog) {
dialog = {};
env.services.dialog.close(oldDialogId);
removeOldDialog();
}
const dialogId = env.services.dialog.open(ActionDialog, actionDialogProps, {
onCloseCallback: () => {
if (dialog.id) {
const removeDialog = env.services.dialog.add(ActionDialog, actionDialogProps, {
onClose: () => {
if (dialog.remove) {
if (dialog.onClose) {
dialog.onClose();
}
@@ -566,7 +566,7 @@ function makeActionManager(env) {
},
});
dialog = {
id: dialogId,
remove: removeDialog,
onClose: onClose || options.onClose,
};
return currentActionProm;
@@ -923,11 +923,11 @@ function makeActionManager(env) {
async function _executeCloseAction(params = {}) {
cleanDomFromBootstrap();
let onClose;
const dialogId = dialog.id;
if (dialogId) {
const removeDialog = dialog.remove;
if (removeDialog) {
onClose = dialog.onClose;
dialog = {};
env.services.dialog.close(dialogId);
removeDialog();
} else {
onClose = params.onClose;
}
@@ -34,4 +34,5 @@ CommandPaletteDialog.bodyTemplate = "web.CommandPaletteDialogBody";
CommandPaletteDialog.components = Object.assign({}, Dialog.components, { CommandPalette });
CommandPaletteDialog.props = {
commands: { type: Array, element: { type: Object } },
close: Function,
};
@@ -67,11 +67,11 @@ export const commandService = {
// Open palette dialog
isPaletteOpened = true;
dialog.open(
dialog.add(
CommandPaletteDialog,
{ commands },
{
onCloseCallback: () => {
onClose: () => {
isPaletteOpened = false;
},
}
@@ -44,7 +44,7 @@ function shortCutsItem(env) {
hide: env.isSmall,
description: env._t("Shortcuts"),
callback: () => {
env.services.dialog.open(ShortCutsDialog);
env.services.dialog.add(ShortCutsDialog);
},
sequence: 30,
};
@@ -123,7 +123,11 @@ QUnit.module("DebugMenu", (hooks) => {
QUnit.test("Don't display the DebugMenu if debug mode is disabled", async (assert) => {
const env = await makeTestEnv(testConfig);
const actionDialog = await mount(ActionDialog, { env, target, props: {} });
const actionDialog = await mount(ActionDialog, {
env,
target,
props: { close: () => {} },
});
registerCleanup(() => {
actionDialog.destroy();
});
@@ -173,9 +177,10 @@ QUnit.module("DebugMenu", (hooks) => {
useSubEnv({ inDialog: true });
useDebugMenu("custom", { customKey: "abc" });
}
close() {}
}
Parent.components = { ActionDialog };
Parent.template = tags.xml`<ActionDialog/>`;
Parent.template = tags.xml`<ActionDialog close="close"/>`;
patchWithCleanup(odoo, { debug: "1" });
const env = await makeTestEnv(testConfig);
const actionDialog = await mount(Parent, { env, target });
@@ -57,7 +57,7 @@ QUnit.test("Simple rendering with a single dialog", async (assert) => {
CustomDialog.title = "Welcome";
pseudoWebClient = await mount(PseudoWebClient, { env, target });
assert.containsNone(target, ".o_dialog_container .o_dialog");
env.services.dialog.open(CustomDialog);
env.services.dialog.add(CustomDialog);
await nextTick();
assert.containsOnce(target, ".o_dialog_container .o_dialog");
assert.strictEqual(target.querySelector("header .modal-title").textContent, "Welcome");
@@ -74,18 +74,18 @@ QUnit.test("Simple rendering and close a single dialog", async (assert) => {
pseudoWebClient = await mount(PseudoWebClient, { env, target });
assert.containsNone(target, ".o_dialog_container .o_dialog");
const dialogId = env.services.dialog.open(CustomDialog);
const removeDialog = env.services.dialog.add(CustomDialog);
await nextTick();
assert.containsOnce(target, ".o_dialog_container .o_dialog");
assert.strictEqual(target.querySelector("header .modal-title").textContent, "Welcome");
env.services.dialog.close(dialogId);
removeDialog();
await nextTick();
assert.containsNone(target, ".o_dialog_container .o_dialog");
// Call a second time, the close on the dialog.
// As the dialog is already close, this call is just ignored. No error should be raised.
env.services.dialog.close(dialogId);
removeDialog();
await nextTick();
});
@@ -99,11 +99,11 @@ QUnit.test("rendering with two dialogs", async (assert) => {
}
pseudoWebClient = await mount(PseudoWebClient, { env, target });
assert.containsNone(target, ".o_dialog_container .o_dialog");
env.services.dialog.open(CustomDialog, { title: "Hello" });
env.services.dialog.add(CustomDialog, { title: "Hello" });
await nextTick();
assert.containsOnce(target, ".o_dialog_container .o_dialog");
assert.strictEqual(target.querySelector("header .modal-title").textContent, "Hello");
env.services.dialog.open(CustomDialog, { title: "Sauron" });
env.services.dialog.add(CustomDialog, { title: "Sauron" });
await nextTick();
assert.containsN(target, ".o_dialog_container .o_dialog", 2);
assert.deepEqual(
@@ -125,7 +125,7 @@ QUnit.test("multiple dialogs can become the UI active element", async (assert) =
}
pseudoWebClient = await mount(PseudoWebClient, { env, target });
env.services.dialog.open(CustomDialog, { title: "Hello" });
env.services.dialog.add(CustomDialog, { title: "Hello" });
await nextTick();
let dialogModal = target.querySelector(
".o_dialog_container .o_dialog .modal:not(.o_inactive_modal)"
@@ -133,7 +133,7 @@ QUnit.test("multiple dialogs can become the UI active element", async (assert) =
assert.strictEqual(dialogModal, env.services.ui.activeElement);
env.services.dialog.open(CustomDialog, { title: "Sauron" });
env.services.dialog.add(CustomDialog, { title: "Sauron" });
await nextTick();
dialogModal = target.querySelector(
".o_dialog_container .o_dialog .modal:not(.o_inactive_modal)"
@@ -141,7 +141,7 @@ QUnit.test("multiple dialogs can become the UI active element", async (assert) =
assert.strictEqual(dialogModal, env.services.ui.activeElement);
env.services.dialog.open(CustomDialog, { title: "Rafiki" });
env.services.dialog.add(CustomDialog, { title: "Rafiki" });
await nextTick();
dialogModal = target.querySelector(
".o_dialog_container .o_dialog .modal:not(.o_inactive_modal)"
@@ -170,11 +170,11 @@ QUnit.test("Interactions between multiple dialogs", async (assert) => {
}
pseudoWebClient = await mount(PseudoWebClient, { env, target });
env.services.dialog.open(CustomDialog, { title: "Hello" });
env.services.dialog.add(CustomDialog, { title: "Hello" });
await nextTick();
env.services.dialog.open(CustomDialog, { title: "Sauron" });
env.services.dialog.add(CustomDialog, { title: "Sauron" });
await nextTick();
env.services.dialog.open(CustomDialog, { title: "Rafiki" });
env.services.dialog.add(CustomDialog, { title: "Rafiki" });
await nextTick();
let modals = document.querySelectorAll(".modal");
@@ -248,7 +248,7 @@ QUnit.test("dialog component crashes", async (assert) => {
pseudoWebClient = await mount(PseudoWebClient, { env, target });
env.services.dialog.open(FailingDialog);
env.services.dialog.add(FailingDialog);
await prom;
assert.verifySteps(["error"]);
assert.containsOnce(pseudoWebClient, ".modal");
+17 -36
View File
@@ -110,67 +110,48 @@ QUnit.module("Components", (hooks) => {
QUnit.test("click on the button x triggers the service close", async function (assert) {
assert.expect(3);
serviceRegistry.add(
"dialog",
makeFakeDialogService({
close: (id) => {
assert.step("close with id: " + id);
},
}),
{ force: true }
);
const env = await makeTestEnv();
class MyDialog extends SimpleDialog {
setup() {
super.setup();
this.__id = 666;
class MyDialog extends SimpleDialog {}
class Parent extends owl.Component {
close() {
assert.step("close");
}
}
class Parent extends owl.Component {}
Parent.template = owl.tags.xml`
<MyDialog>
Hello!
</MyDialog>
`;
<MyDialog close="close">
Hello!
</MyDialog>
`;
Parent.components = { MyDialog };
parent = await mount(Parent, { env, target });
assert.containsOnce(target, ".o_dialog");
await click(target, ".o_dialog header button.close");
assert.verifySteps(["close with id: 666"]);
assert.verifySteps(["close"]);
});
QUnit.test(
"click on the default footer button triggers the service close",
async function (assert) {
serviceRegistry.add(
"dialog",
makeFakeDialogService({
close: (id) => {
assert.step("close with id: " + id);
},
}),
{ force: true }
);
const env = await makeTestEnv();
assert.expect(3);
class MyDialog extends SimpleDialog {
setup() {
super.setup();
this.__id = 777;
class MyDialog extends SimpleDialog {}
class Parent extends owl.Component {
close() {
assert.step("close");
}
}
class Parent extends owl.Component {}
Parent.template = owl.tags.xml`
<MyDialog>
<MyDialog close="close">
Hello!
</MyDialog>
`;
`;
Parent.components = { MyDialog };
parent = await mount(Parent, { env, target });
assert.containsOnce(target, ".o_dialog");
await click(target, ".o_dialog footer button");
assert.verifySteps(["close with id: 777"]);
assert.verifySteps(["close"]);
}
);
@@ -201,16 +201,10 @@ QUnit.test("WarningDialog", async (assert) => {
});
QUnit.test("RedirectWarningDialog", async (assert) => {
serviceRegistry.add(
"dialog",
makeFakeDialogService({
close: () => {
assert.step("dialog-closed");
},
}),
{ force: true }
);
assert.expect(10);
class CloseRedirectWarningDialog extends RedirectWarningDialog {}
class Parent extends Component {
constructor() {
super(...arguments);
@@ -222,9 +216,12 @@ QUnit.test("RedirectWarningDialog", async (assert) => {
],
};
}
close() {
assert.step("dialog-closed");
}
}
Parent.components = { RedirectWarningDialog };
Parent.template = tags.xml`<RedirectWarningDialog data="data"/>`;
Parent.components = { RedirectWarningDialog: CloseRedirectWarningDialog };
Parent.template = tags.xml`<RedirectWarningDialog data="data" close="close"/>`;
const faceActionService = {
name: "action",
start() {
@@ -58,7 +58,7 @@ QUnit.test("handle RPC_ERROR of type='server' and no associated dialog class", a
error.message = "Some strange error occured";
error.data = { debug: "somewhere" };
error.subType = "strange_error";
function open(dialogClass, props) {
function addDialog(dialogClass, props) {
assert.strictEqual(dialogClass, RPCErrorDialog);
assert.deepEqual(props, {
name: "RPC_ERROR",
@@ -73,7 +73,7 @@ QUnit.test("handle RPC_ERROR of type='server' and no associated dialog class", a
traceback: error.stack,
});
}
serviceRegistry.add("dialog", makeFakeDialogService({ open: open }), { force: true });
serviceRegistry.add("dialog", makeFakeDialogService(addDialog), { force: true });
await makeTestEnv();
const errorEvent = new PromiseRejectionEvent("error", { reason: error, promise: null });
await unhandledRejectionCb(errorEvent);
@@ -90,7 +90,7 @@ QUnit.test(
error.code = 701;
error.message = "Some strange error occured";
error.exceptionName = "strange_error";
function open(dialogClass, props) {
function addDialog(dialogClass, props) {
assert.strictEqual(dialogClass, CustomDialog);
assert.deepEqual(props, {
name: "RPC_ERROR",
@@ -103,7 +103,7 @@ QUnit.test(
traceback: error.stack,
});
}
serviceRegistry.add("dialog", makeFakeDialogService({ open: open }), { force: true });
serviceRegistry.add("dialog", makeFakeDialogService(addDialog), { force: true });
await makeTestEnv();
errorDialogRegistry.add("strange_error", CustomDialog);
const errorEvent = new PromiseRejectionEvent("error", { reason: error, promise: null });
@@ -176,7 +176,7 @@ QUnit.test("handle uncaught promise errors", async (assert) => {
error.message = "This is an error test";
error.name = "TestError";
function open(dialogClass, props) {
function addDialog(dialogClass, props) {
assert.strictEqual(dialogClass, ClientErrorDialog);
assert.deepEqual(props, {
name: "UncaughtPromiseError > TestError",
@@ -184,7 +184,7 @@ QUnit.test("handle uncaught promise errors", async (assert) => {
traceback: error.stack,
});
}
serviceRegistry.add("dialog", makeFakeDialogService({ open: open }), { force: true });
serviceRegistry.add("dialog", makeFakeDialogService(addDialog), { force: true });
await makeTestEnv();
const errorEvent = new PromiseRejectionEvent("error", { reason: error, promise: null });
@@ -197,12 +197,12 @@ QUnit.test("handle uncaught client errors", async (assert) => {
error.message = "This is an error test";
error.name = "TestError";
function open(dialogClass, props) {
function addDialog(dialogClass, props) {
assert.strictEqual(dialogClass, ClientErrorDialog);
assert.strictEqual(props.name, "UncaughtClientError > TestError");
assert.strictEqual(props.message, "Uncaught Javascript Error > This is an error test");
}
serviceRegistry.add("dialog", makeFakeDialogService({ open: open }), { force: true });
serviceRegistry.add("dialog", makeFakeDialogService(addDialog), { force: true });
await makeTestEnv();
const errorEvent = new ErrorEvent("error", {
@@ -220,11 +220,11 @@ QUnit.test("handle uncaught CORS errors", async (assert) => {
error.message = "This is a cors error";
error.name = "CORS error";
function open(dialogClass, props) {
function addDialog(dialogClass, props) {
assert.strictEqual(dialogClass, NetworkErrorDialog);
assert.strictEqual(props.message, "Uncaught CORS Error");
}
serviceRegistry.add("dialog", makeFakeDialogService({ open: open }), { force: true });
serviceRegistry.add("dialog", makeFakeDialogService(addDialog), { force: true });
await makeTestEnv();
// CORS error event has no colno, no lineno and no filename
@@ -203,15 +203,11 @@ export function makeFakeNotificationService(mock) {
};
}
export function makeFakeDialogService(values = {}) {
const open = values.open || (() => {});
const close = values.close || (() => {});
export function makeFakeDialogService(addDialog) {
return {
name: "dialog",
start() {
return {
open,
close,
add: addDialog || (() => () => {}),
};
},
};
@@ -278,30 +278,23 @@ odoo.define('web.owl_dialog_tests', function (require) {
QUnit.test("Interactions between legacy owl dialogs and new owl dialogs", async function (assert) {
assert.expect(7);
const serviceRegistry = registry.category("services");
serviceRegistry.add("dialog", makeFakeDialogService({
close: (id) => {
assert.step(`dialog_${id}_closed`);
parent.dialogs.splice(parent.dialogs.findIndex(d => d.id === id), 1);
},
})
);
const { legacyEnv, env } = await makeLegacyDialogMappingTestEnv();
let id = 1;
// OwlDialog env
patchWithCleanup(Dialog.prototype, {
class OwlDialogWrapper extends owl.Component {
setup() {
this.env = legacyEnv;
this._super();
}
});
let id = 1;
}
OwlDialogWrapper.template = xml`
<Dialog t-on-dialog-closed="props.close()" />
`;
OwlDialogWrapper.components = { Dialog };
class WowlDialogSubClass extends WowlDialog{
setup(){
super.setup();
this.contentClass = this.props.contentClass;
this.__id = id;
}
}
class Parent extends Component {
@@ -311,8 +304,10 @@ odoo.define('web.owl_dialog_tests', function (require) {
}
// Handlers
_onDialogClosed(id) {
assert.step(`dialog_${id}_closed`);
this.dialogs.splice(this.dialogs.findIndex(d => d.id === id), 1);
return () => {
assert.step(`dialog_${id}_closed`);
this.dialogs.splice(this.dialogs.findIndex(d => d.id === id), 1);
};
}
}
Parent.template = xml`
@@ -320,14 +315,15 @@ odoo.define('web.owl_dialog_tests', function (require) {
<div class="o_dialog_container"/>
<t t-foreach="dialogs" t-as="dialog" t-key="dialog.id" t-component="dialog.class"
contentClass="'dialog_' + dialog.id"
t-on-dialog-closed="_onDialogClosed(dialog.id)" />
close="_onDialogClosed(dialog.id)"
/>
</div>`;
const parent = await mount(Parent, { env, target: getFixture() });
parent.dialogs.push({ id: 1, class: WowlDialogSubClass });
await nextTick();
id ++;
parent.dialogs.push({ id: 2, class: Dialog });
parent.dialogs.push({ id: 2, class: OwlDialogWrapper });
await nextTick();
id ++;
parent.dialogs.push({ id: 3, class: WowlDialogSubClass });
@@ -35,7 +35,7 @@ function startTour({ env }) {
type: "item",
description: env._t("Start Tour"),
callback: async () => {
env.services.dialog.open(ToursDialog);
env.services.dialog.add(ToursDialog);
},
sequence: 60,
};