From 38260f3262db5a2ded12269f6037c176facfcdd5 Mon Sep 17 00:00:00 2001 From: Bastien PIERRE Date: Mon, 28 Aug 2023 08:22:25 +0000 Subject: [PATCH] [FIX] web: prevent command palette to lose focus The command palette has the priority when useAutoFocus is call. Like that, the command palette will not lose focus when clicking on an app on the home menu and then directly opening the command palette before the app mounting TASK ID: 3338117 closes odoo/odoo#139124 X-original-commit: 660d27ac38c225559ea3697a24a34ac70ede8ec1 Signed-off-by: Florent Dardenne (dafl) --- addons/web/static/src/core/utils/hooks.js | 6 ++- .../static/tests/core/utils/hooks_tests.js | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/core/utils/hooks.js b/addons/web/static/src/core/utils/hooks.js index 135acc90f33..5ae0be99334 100644 --- a/addons/web/static/src/core/utils/hooks.js +++ b/addons/web/static/src/core/utils/hooks.js @@ -29,7 +29,7 @@ import { status, useComponent, useEffect, useRef, onWillUnmount } from "@odoo/ow // ----------------------------------------------------------------------------- /** - * Focus an element referenced by a t-ref="autofocus" in the current component + * Focus an element referenced by a t-ref="autofocus" in the active component * as soon as it appears in the DOM and if it was not displayed before. * If it is an input/textarea, set the selection at the end. * @param {Object} [params] @@ -41,6 +41,8 @@ import { status, useComponent, useEffect, useRef, onWillUnmount } from "@odoo/ow export function useAutofocus({ refName, selectAll, mobile } = {}) { const comp = useComponent(); const ref = useRef(refName || "autofocus"); + const uiService = useService("ui"); + // Prevent autofocus in mobile if (!mobile && comp.env.isSmall) { return ref; @@ -52,7 +54,7 @@ export function useAutofocus({ refName, selectAll, mobile } = {}) { // LEGACY useEffect( (el) => { - if (el) { + if (el && (!uiService.activeElement || uiService.activeElement.contains(el))) { el.focus(); if (["INPUT", "TEXTAREA"].includes(el.tagName) && el.type !== "number") { el.selectionEnd = el.value.length; diff --git a/addons/web/static/tests/core/utils/hooks_tests.js b/addons/web/static/tests/core/utils/hooks_tests.js index 8a05c527e89..1ab1b37ad81 100644 --- a/addons/web/static/tests/core/utils/hooks_tests.js +++ b/addons/web/static/tests/core/utils/hooks_tests.js @@ -14,6 +14,9 @@ import { makeTestEnv } from "@web/../tests/helpers/mock_env"; import { destroy, getFixture, makeDeferred, mount, nextTick } from "@web/../tests/helpers/utils"; import { Component, onMounted, useState, xml } from "@odoo/owl"; +import { dialogService } from "@web/core/dialog/dialog_service"; +import { hotkeyService } from "@web/core/hotkeys/hotkey_service"; +import { CommandPalette } from "@web/core/commands/command_palette"; const serviceRegistry = registry.category("services"); QUnit.module("utils", () => { @@ -273,6 +276,45 @@ QUnit.module("utils", () => { assert.strictEqual(comp.inputRef.el.selectionEnd, 10); }); + QUnit.test("useAutofocus: autofocus outside of active element doesn't work (CommandPalette)", async function (assert) { + class MyComponent extends Component { + setup() { + this.inputRef = useAutofocus(); + } + get OverlayContainer() { + return registry.category("main_components").get("OverlayContainer"); + } + } + MyComponent.template = xml` +
+ +
+ +
+ `; + + registry.category("services").add("ui", uiService); + registry.category("services").add("dialog", dialogService); + registry.category("services").add("hotkey", hotkeyService); + + const config = { providers: [] }; + const env = await makeTestEnv(); + const target = getFixture(); + const comp = await mount(MyComponent, target , { env }); + await nextTick(); + + assert.strictEqual(document.activeElement, comp.inputRef.el); + + env.services.dialog.add(CommandPalette, { config }); + await nextTick(); + assert.containsOnce(target, ".o_command_palette"); + assert.notStrictEqual(document.activeElement, comp.inputRef.el); + + comp.render(); + await nextTick(); + assert.notStrictEqual(document.activeElement, comp.inputRef.el); + }); + QUnit.module("useBus"); QUnit.test("useBus hook: simple usecase", async function (assert) {