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) {