[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:
FrancoisGe
2023-09-13 15:34:40 +00:00
parent 9aa16f4072
commit 2ee329f6ca
9 changed files with 70 additions and 24 deletions
@@ -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) {
+8 -1
View File
@@ -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";
+2 -3
View File
@@ -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>
+12 -6
View File
@@ -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");
});
});