[FIX] web: close button in form view dialog discard
The goal of this commit is to ensure that the form view dialog close button performs the same behaviour as the discard button. To enable this, we had to add the dialogData.dismiss, which lets you add a callBack that will only be executed when a dialog is closed because of the "x" or the "escape" shortcut. closes odoo/odoo#135116 Related: odoo/enterprise#47283 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
@@ -8,17 +8,17 @@ import { Component } from "@odoo/owl";
|
||||
|
||||
export class ConfirmationDialog extends Component {
|
||||
setup() {
|
||||
this.env.dialogData.close = () => this._cancel();
|
||||
this.env.dialogData.dismiss = () => this._cancel();
|
||||
this.modalRef = useChildRef();
|
||||
this.isProcess = false;
|
||||
}
|
||||
|
||||
async _cancel() {
|
||||
this.execButton(this.props.cancel);
|
||||
return this.execButton(this.props.cancel);
|
||||
}
|
||||
|
||||
async _confirm() {
|
||||
this.execButton(this.props.confirm);
|
||||
return this.execButton(this.props.confirm);
|
||||
}
|
||||
|
||||
setButtonsDisabled(disabled) {
|
||||
|
||||
@@ -90,7 +90,14 @@ export class Dialog extends Component {
|
||||
}
|
||||
|
||||
onEscape() {
|
||||
this.data.close();
|
||||
return this.dismiss();
|
||||
}
|
||||
|
||||
async dismiss() {
|
||||
if (this.data.dismiss) {
|
||||
await this.data.dismiss();
|
||||
}
|
||||
return this.data.close();
|
||||
}
|
||||
}
|
||||
Dialog.template = "web.Dialog";
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
<header t-if="props.header" class="modal-header">
|
||||
<t t-slot="header" close="data.close" isFullscreen="isFullscreen">
|
||||
<t t-call="web.Dialog.header">
|
||||
<t t-set="close" t-value="() => this.data.close()"/>
|
||||
<t t-set="fullscreen" t-value="isFullscreen"/>
|
||||
</t>
|
||||
</t>
|
||||
@@ -38,13 +37,13 @@
|
||||
|
||||
<t t-name="web.Dialog.header">
|
||||
<t t-if="fullscreen">
|
||||
<button class="btn oi oi-arrow-left" data-bs-dismiss="modal" aria-label="Close" t-on-click="close" />
|
||||
<button class="btn oi oi-arrow-left" data-bs-dismiss="modal" aria-label="Close" t-on-click="dismiss" />
|
||||
</t>
|
||||
<h4 class="modal-title text-break" t-att-class="{ 'me-auto': fullscreen }">
|
||||
<t t-esc="props.title"/>
|
||||
</h4>
|
||||
<t t-if="!fullscreen">
|
||||
<button type="button" class="btn-close" aria-label="Close" tabindex="-1" t-on-click="close"></button>
|
||||
<button type="button" class="btn-close" aria-label="Close" tabindex="-1" t-on-click="dismiss"></button>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
|
||||
@@ -301,7 +301,7 @@ export class Many2XAutocomplete extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.props.noSearchMore && records.length > 0) {
|
||||
if (!this.props.noSearchMore && records.length > 0) {
|
||||
options.push({
|
||||
label: _t("Search More..."),
|
||||
action: this.onSearchMore.bind(this, request),
|
||||
@@ -508,7 +508,7 @@ export class X2ManyFieldDialog extends Component {
|
||||
this.title = this.props.title;
|
||||
this.contentClass = computeViewClassName("form", this.archInfo.xmlDoc);
|
||||
useSubEnv({ config: this.props.config });
|
||||
this.env.dialogData.close = () => this.discard();
|
||||
this.env.dialogData.dismiss = () => this.discard();
|
||||
|
||||
useBus(this.record.model.bus, "update", () => this.render(true));
|
||||
|
||||
@@ -633,7 +633,7 @@ X2ManyFieldDialog.props = {
|
||||
save: Function,
|
||||
title: String,
|
||||
delete: { optional: true },
|
||||
deleteButtonLabel: {optional: true},
|
||||
deleteButtonLabel: { optional: true },
|
||||
config: Object,
|
||||
};
|
||||
X2ManyFieldDialog.template = "web.X2ManyFieldDialog";
|
||||
|
||||
@@ -11,6 +11,7 @@ export class FormViewDialog extends Component {
|
||||
super.setup();
|
||||
|
||||
this.modalRef = useChildRef();
|
||||
this.env.dialogData.dismiss = () => this.discardRecord();
|
||||
|
||||
const buttonTemplate = this.props.isToMany
|
||||
? "web.FormViewDialog.ToMany.buttons"
|
||||
@@ -28,12 +29,7 @@ export class FormViewDialog extends Component {
|
||||
viewId: this.props.viewId || false,
|
||||
preventCreate: this.props.preventCreate,
|
||||
preventEdit: this.props.preventEdit,
|
||||
discardRecord: async () => {
|
||||
if (this.props.onRecordDiscarded) {
|
||||
await this.props.onRecordDiscarded();
|
||||
}
|
||||
this.props.close();
|
||||
},
|
||||
discardRecord: this.discardRecord.bind(this),
|
||||
saveRecord: async (record, { saveAndNew }) => {
|
||||
const saved = await record.save({ reload: false });
|
||||
if (saved) {
|
||||
@@ -70,6 +66,13 @@ export class FormViewDialog extends Component {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async discardRecord() {
|
||||
if (this.props.onRecordDiscarded) {
|
||||
await this.props.onRecordDiscarded();
|
||||
}
|
||||
this.props.close();
|
||||
}
|
||||
}
|
||||
|
||||
FormViewDialog.components = { Dialog, View };
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Dialog title="props.title" withBodyPadding="false">
|
||||
<t t-set-slot="header" t-slot-scope="scope">
|
||||
<t t-call="web.Dialog.header">
|
||||
<t t-set="close" t-value="scope.close"/>
|
||||
<t t-set="dismiss" t-value="scope.close"/>
|
||||
<t t-set="fullscreen" t-value="scope.isFullscreen"/>
|
||||
</t>
|
||||
<button t-if="this.canUnselect" class="btn o_clear_button" t-on-click="() => this.unselect()">Clear</button>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
</xpath>
|
||||
<xpath expr="//t[@t-slot='header']" position="replace">
|
||||
<t t-call="web.ActionDialog.header">
|
||||
<t t-set="close" t-value="() => this.data.close()"/>
|
||||
<t t-set="fullscreen" t-value="props.isFullscreen"/>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
triggerEvent,
|
||||
triggerHotkey,
|
||||
dragAndDrop,
|
||||
nextTick,
|
||||
} from "../helpers/utils";
|
||||
import { makeFakeDialogService } from "../helpers/mock_services";
|
||||
|
||||
@@ -81,6 +82,7 @@ QUnit.module("Components", (hooks) => {
|
||||
|
||||
const env = await makeDialogTestEnv();
|
||||
env.dialogData.close = () => assert.step("close");
|
||||
env.dialogData.dismiss = () => assert.step("dismiss");
|
||||
parent = await mount(Parent, target, { env });
|
||||
assert.strictEqual(
|
||||
target.querySelector("header .modal-title").textContent,
|
||||
@@ -89,7 +91,8 @@ QUnit.module("Components", (hooks) => {
|
||||
assert.strictEqual(target.querySelector("footer button").textContent, "Ok");
|
||||
// Same effect as clicking on the x button
|
||||
triggerHotkey("escape");
|
||||
assert.verifySteps(["close"]);
|
||||
await nextTick();
|
||||
assert.verifySteps(["dismiss", "close"]);
|
||||
// Same effect as clicking on the Ok button
|
||||
triggerHotkey("control+enter");
|
||||
assert.verifySteps(["close"]);
|
||||
@@ -123,9 +126,10 @@ QUnit.module("Components", (hooks) => {
|
||||
});
|
||||
|
||||
QUnit.test("click on the button x triggers the service close", async function (assert) {
|
||||
assert.expect(3);
|
||||
assert.expect(4);
|
||||
const env = await makeDialogTestEnv();
|
||||
env.dialogData.close = () => assert.step("close");
|
||||
env.dialogData.dismiss = () => assert.step("dismiss");
|
||||
|
||||
class Parent extends Component {}
|
||||
Parent.template = xml`
|
||||
@@ -137,19 +141,20 @@ QUnit.module("Components", (hooks) => {
|
||||
parent = await mount(Parent, target, { env });
|
||||
assert.containsOnce(target, ".o_dialog");
|
||||
await click(target, ".o_dialog header button.btn-close");
|
||||
assert.verifySteps(["close"]);
|
||||
assert.verifySteps(["dismiss", "close"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"click on the button x triggers the close defined by a Child component",
|
||||
"click on the button x triggers the close and dismiss defined by a Child component",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
assert.expect(4);
|
||||
const env = await makeDialogTestEnv();
|
||||
class Child extends Component {
|
||||
static template = xml`<div>Hello</div>`;
|
||||
|
||||
setup() {
|
||||
this.env.dialogData.close = () => assert.step("close");
|
||||
this.env.dialogData.dismiss = () => assert.step("dismiss");
|
||||
}
|
||||
}
|
||||
class Parent extends Component {}
|
||||
@@ -163,7 +168,7 @@ QUnit.module("Components", (hooks) => {
|
||||
assert.containsOnce(target, ".o_dialog");
|
||||
|
||||
await click(target, ".o_dialog header button.btn-close");
|
||||
assert.verifySteps(["close"]);
|
||||
assert.verifySteps(["dismiss", "close"]);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -172,6 +177,7 @@ QUnit.module("Components", (hooks) => {
|
||||
async function (assert) {
|
||||
const env = await makeDialogTestEnv();
|
||||
env.dialogData.close = () => assert.step("close");
|
||||
env.dialogData.dismiss = () => assert.step("dismiss");
|
||||
assert.expect(3);
|
||||
class Parent extends Component {}
|
||||
|
||||
|
||||
@@ -386,4 +386,36 @@ QUnit.module("ViewDialogs", (hooks) => {
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_dialog .o_form_view");
|
||||
});
|
||||
|
||||
QUnit.test("FormViewDialog with discard button", async function (assert) {
|
||||
serverData.views = {
|
||||
"partner,false,form": `<form><field name="foo"/></form>`,
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
webClient.env.services.dialog.add(FormViewDialog, {
|
||||
resModel: "partner",
|
||||
resId: 1,
|
||||
onRecordDiscarded: () => assert.step("discard"),
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
assert.containsOnce(target, ".o_dialog .o_form_view");
|
||||
assert.containsOnce(target, ".o_dialog .modal-footer .o_form_button_cancel");
|
||||
await click(target.querySelector(".o_dialog .modal-footer .o_form_button_cancel"));
|
||||
assert.verifySteps(["discard"]);
|
||||
assert.containsNone(target, ".o_dialog .o_form_view");
|
||||
|
||||
webClient.env.services.dialog.add(FormViewDialog, {
|
||||
resModel: "partner",
|
||||
resId: 1,
|
||||
onRecordDiscarded: () => assert.step("discard"),
|
||||
});
|
||||
await nextTick();
|
||||
|
||||
assert.containsOnce(target, ".o_dialog .o_form_view");
|
||||
await click(target.querySelector(".o_dialog .btn-close"));
|
||||
assert.verifySteps(["discard"]);
|
||||
assert.containsNone(target, ".o_dialog .o_form_view");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user