[FIX] web: trapfocus if first/last tabable changes
**Before this commit** If a UI active element taker has its first or last tabable element rendered conditionnaly, the focus trap mechanism will not work properly. **After this commit** It works as intended. Part-of: odoo/odoo#140885
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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`
|
||||
<div>
|
||||
<h1>My Component</h1>
|
||||
<input type="text" name="outer"/>
|
||||
<div id="idActiveElement" t-ref="delegatedRef">
|
||||
<div>
|
||||
<input type="text" name="a" t-if="show.a"/>
|
||||
<input type="text" name="b"/>
|
||||
<input type="text" name="c" t-if="show.c"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
const env = await makeTestEnv({ ...baseConfig });
|
||||
const comp = await mount(MyComponent, target, { env });
|
||||
|
||||
assert.strictEqual(document.activeElement, target.querySelector("input[name=a]"));
|
||||
// Pressing 'Shift + Tab'
|
||||
let event = await triggerEvent(document.activeElement, null, "keydown", {
|
||||
key: "Tab",
|
||||
shiftKey: true,
|
||||
});
|
||||
assert.strictEqual(event.defaultPrevented, true);
|
||||
assert.strictEqual(document.activeElement, target.querySelector("input[name=b]"));
|
||||
|
||||
comp.show.a = false;
|
||||
comp.show.c = true;
|
||||
await nextTick();
|
||||
assert.strictEqual(document.activeElement, target.querySelector("input[name=b]"));
|
||||
|
||||
// Pressing 'Shift + Tab'
|
||||
event = await triggerEvent(document.activeElement, null, "keydown", {
|
||||
key: "Tab",
|
||||
shiftKey: true,
|
||||
});
|
||||
assert.strictEqual(event.defaultPrevented, true);
|
||||
assert.strictEqual(document.activeElement, target.querySelector("input[name=c]"));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user