diff --git a/addons/web/static/src/core/ui/ui_service.js b/addons/web/static/src/core/ui/ui_service.js index c0ff1528b19..b6545bfaf5c 100644 --- a/addons/web/static/src/core/ui/ui_service.js +++ b/addons/web/static/src/core/ui/ui_service.js @@ -30,10 +30,16 @@ export function useActiveElement(refName) { const uiService = useService("ui"); const owner = useRef(refName); - let lastTabableEl, firstTabableEl; - function trapFocus(e) { - switch (getActiveHotkey(e)) { + const hotkey = getActiveHotkey(e); + if (!["tab", "shift+tab"].includes(hotkey)) { + return; + } + const el = e.currentTarget; + const tabableEls = getTabableElements(el); + const firstTabableEl = tabableEls[0] || el; + const lastTabableEl = tabableEls[tabableEls.length - 1] || el; + switch (hotkey) { case "tab": if (document.activeElement === lastTabableEl) { firstTabableEl.focus(); @@ -66,8 +72,7 @@ export function useActiveElement(refName) { */ el.tabIndex = -1; } - firstTabableEl = tabableEls[0] || el; - lastTabableEl = tabableEls[tabableEls.length - 1] || el; + const firstTabableEl = tabableEls[0] || el; el.addEventListener("keydown", trapFocus); diff --git a/addons/web/static/tests/core/ui_service_tests.js b/addons/web/static/tests/core/ui_service_tests.js index d0ae28ff375..37e6d00b0f8 100644 --- a/addons/web/static/tests/core/ui_service_tests.js +++ b/addons/web/static/tests/core/ui_service_tests.js @@ -7,7 +7,7 @@ import { makeTestEnv } from "../helpers/mock_env"; import { makeFakeLocalizationService } from "../helpers/mock_services"; import { getFixture, mount, nextTick, triggerEvent } from "../helpers/utils"; -import { Component, xml } from "@odoo/owl"; +import { Component, useState, xml } from "@odoo/owl"; const serviceRegistry = registry.category("services"); let target; @@ -231,3 +231,50 @@ QUnit.test("UI active element: trap focus - no focus element", async (assert) => assert.strictEqual(event.defaultPrevented, true); assert.strictEqual(document.activeElement, target.querySelector("div[id=idActiveElement]")); }); + +QUnit.test("UI active element: trap focus - first or last tabable changes", async (assert) => { + class MyComponent extends Component { + setup() { + this.show = useState({ a: true, c: false }); + useActiveElement("delegatedRef"); + } + } + MyComponent.template = xml` +