From 73a9154be870abbef1f93739f744e66e863571c8 Mon Sep 17 00:00:00 2001 From: "Julien Carion (juca)" Date: Fri, 16 Jun 2023 08:16:57 +0000 Subject: [PATCH] [IMP] web: add ctrl+enter shortcut to dialogs This commit adds the ctrl+enter shortcut to dialogs which will trigger a click on the first visible button in the dialog's footer. Task-3369676 closes odoo/odoo#125330 Signed-off-by: Mathieu Duckerts-Antoine (dam) --- addons/web/static/src/core/dialog/dialog.js | 22 +++++++++++- addons/web/static/tests/core/dialog_tests.js | 35 +++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/core/dialog/dialog.js b/addons/web/static/src/core/dialog/dialog.js index 879032544f9..310f72ca9f6 100644 --- a/addons/web/static/src/core/dialog/dialog.js +++ b/addons/web/static/src/core/dialog/dialog.js @@ -37,6 +37,22 @@ export class Dialog extends Component { useActiveElement("modalRef"); this.data = useState(this.env.dialogData); useHotkey("escape", () => this.onEscape()); + useHotkey( + "control+enter", + () => { + const btns = document.querySelectorAll( + ".o_dialog:not(.o_inactive_modal) .modal-footer button" + ); + const firstVisibleBtn = Array.from(btns).find((btn) => { + const styles = getComputedStyle(btn); + return styles.display !== "none"; + }); + if (firstVisibleBtn) { + firstVisibleBtn.click(); + } + }, + { bypassEditableProtection: true } + ); this.id = `dialog_${this.data.id}`; useChildSubEnv({ inDialog: true, dialogId: this.id, closeDialog: () => this.data.close() }); this.position = useState({ left: 0, top: 0 }); @@ -84,7 +100,11 @@ Dialog.props = { fullscreen: { type: Boolean, optional: true }, footer: { type: Boolean, optional: true }, header: { type: Boolean, optional: true }, - size: { type: String, optional: true, validate: (s) => ["sm", "md", "lg", "xl", "fs"].includes(s) }, + size: { + type: String, + optional: true, + validate: (s) => ["sm", "md", "lg", "xl", "fs"].includes(s), + }, technical: { type: Boolean, optional: true }, title: { type: String, optional: true }, modalRef: { type: Function, optional: true }, diff --git a/addons/web/static/tests/core/dialog_tests.js b/addons/web/static/tests/core/dialog_tests.js index 7079c2536c9..bd8f35d4b19 100644 --- a/addons/web/static/tests/core/dialog_tests.js +++ b/addons/web/static/tests/core/dialog_tests.js @@ -6,7 +6,15 @@ import { uiService } from "@web/core/ui/ui_service"; import { hotkeyService } from "@web/core/hotkeys/hotkey_service"; import { Dialog } from "@web/core/dialog/dialog"; import { makeTestEnv } from "../helpers/mock_env"; -import { click, destroy, getFixture, mount, triggerEvent, dragAndDrop } from "../helpers/utils"; +import { + click, + destroy, + getFixture, + mount, + triggerEvent, + triggerHotkey, + dragAndDrop, +} from "../helpers/utils"; import { makeFakeDialogService } from "../helpers/mock_services"; import { Component, useState, onMounted, xml } from "@odoo/owl"; @@ -71,6 +79,31 @@ QUnit.module("Components", (hooks) => { assert.strictEqual(target.querySelector("footer button").textContent, "Ok"); }); + QUnit.test("hotkeys work on dialogs", async function (assert) { + class Parent extends Component {} + Parent.components = { Dialog }; + Parent.template = xml` + + Hello! + + `; + + const env = await makeDialogTestEnv(); + env.dialogData.close = () => assert.step("close"); + parent = await mount(Parent, target, { env }); + assert.strictEqual( + target.querySelector("header .modal-title").textContent, + "Wow(l) Effect" + ); + assert.strictEqual(target.querySelector("footer button").textContent, "Ok"); + // Same effect as clicking on the x button + triggerHotkey("escape"); + assert.verifySteps(["close"]); + // Same effect as clicking on the Ok button + triggerHotkey("control+enter"); + assert.verifySteps(["close"]); + }); + QUnit.test("simple rendering with two dialogs", async function (assert) { assert.expect(3); class Parent extends Component {}