[FIX] web: fix traceback when shift-tab on list field

**Before**

A traceback is raised when pressing shift-tab on the first column of
the first row in a focused list field as shown in the vid:
https://youtu.be/g97ZTp1QdBE

**After**

shift-tab on the first column of first row will now unselect the list field
and immediately focuses on the previous field.

**Additional change**

We now allow skipping focus on the dropdown toggler by introducing new
props.

closes odoo/odoo#103485

X-original-commit: 9ba9977e42d201035b5299bc13ba03211cddbc1f
Signed-off-by: Bruno Boi (boi) <boi@odoo.com>
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
Co-authored-by: Bruno Boi <boi@odoo.com>
This commit is contained in:
Joseph Caburnay
2022-10-19 12:10:41 +02:00
co-authored by Bruno Boi
parent 8a2a171aa4
commit 390955b751
8 changed files with 77 additions and 4 deletions
@@ -305,6 +305,10 @@ Dropdown.props = {
optional: true,
validate: (prop) => ["parent"].includes(prop),
},
skipTogglerTabbing: {
type: Boolean,
optional: true,
},
startOpen: {
type: Boolean,
optional: true,
@@ -24,6 +24,7 @@
t-att-title="props.title"
t-att-data-hotkey="props.hotkey"
t-att-data-tooltip="props.tooltip"
t-att-tabindex="props.skipTogglerTabbing ? -1 : 0"
t-ref="togglerRef"
>
<t t-slot="toggler" />
@@ -1256,6 +1256,7 @@ export class ListRenderer extends Component {
futureRecord.switchMode("edit");
}
} else {
list.unselectRecord(true);
return false;
}
} else {
@@ -47,6 +47,7 @@
<Dropdown t-if="displayOptionalFields"
class="'o_optional_columns_dropdown text-center border-top-0'"
togglerClass="'btn p-0'"
skipTogglerTabbing="true"
position="'bottom-end'">
<t t-set-slot="toggler">
<i class="o_optional_columns_dropdown_toggle oi oi-fw oi-settings-adjust"/>
@@ -50,7 +50,7 @@ QUnit.module("Components", ({ beforeEach }) => {
await mount(Parent, target, { env });
assert.strictEqual(
target.querySelector(".dropdown").outerHTML,
'<div class="o-dropdown dropdown o-dropdown--no-caret"><button class="dropdown-toggle"></button></div>'
'<div class="o-dropdown dropdown o-dropdown--no-caret"><button class="dropdown-toggle" tabindex="0"></button></div>'
);
assert.containsOnce(target, "button.dropdown-toggle");
assert.containsNone(target, ".dropdown-menu");
+6 -2
View File
@@ -489,6 +489,7 @@ export function editSelect(el, selector, value) {
* @param {string} hotkey
* @param {boolean} addOverlayModParts
* @param {KeyboardEventInit} eventAttrs
* @returns {{ keydownEvent: KeyboardEvent, keyupEvent: KeyboardEvent }}
*/
export function triggerHotkey(hotkey, addOverlayModParts = false, eventAttrs = {}) {
eventAttrs.key = hotkey.split("+").pop();
@@ -517,8 +518,11 @@ export function triggerHotkey(hotkey, addOverlayModParts = false, eventAttrs = {
eventAttrs.bubbles = true;
}
document.activeElement.dispatchEvent(new KeyboardEvent("keydown", eventAttrs));
document.activeElement.dispatchEvent(new KeyboardEvent("keyup", eventAttrs));
const keydownEvent = new KeyboardEvent("keydown", eventAttrs);
const keyupEvent = new KeyboardEvent("keyup", eventAttrs);
document.activeElement.dispatchEvent(keydownEvent);
document.activeElement.dispatchEvent(keyupEvent);
return { keydownEvent, keyupEvent };
}
export async function legacyExtraNextTick() {
@@ -6730,6 +6730,68 @@ QUnit.module("Fields", (hooks) => {
await clickSave(target);
});
QUnit.test(
"one2many list editable - should properly unselect the list field after shift+tab",
async function (assert) {
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: /* xml */ `
<form>
<group>
<field name="display_name"/>
<field name="turtles">
<tree editable="bottom">
<field name="turtle_foo"/>
<field name="turtle_bar" optional="hide"/>
</tree>
</field>
</group>
</form>`,
resId: 1,
});
await click(target, ".o_data_row td:first-child");
assert.containsOnce(target, ".o_selected_row", "should have selected row");
const { keydownEvent } = triggerHotkey("Shift+Tab");
await nextTick();
assert.containsNone(target, ".o_selected_row", "list should not be in edition");
// We also check the event is not default prevented, to make sure that the
// event flows and selection goes to the previous field.
assert.ok(!keydownEvent.defaultPrevented);
}
);
QUnit.test(
"one2many list editable - should not allow tab navigation focus on the optional field toggler",
async function (assert) {
await makeView({
type: "form",
resModel: "partner",
serverData,
arch: /* xml */ `
<form>
<group>
<field name="display_name"/>
<field name="turtles">
<tree editable="bottom">
<field name="turtle_foo"/>
<field name="turtle_bar" optional="hide"/>
</tree>
</field>
</group>
</form>`,
resId: 1,
});
assert.strictEqual(
target.querySelector(".o_optional_columns_dropdown .dropdown-toggle").tabIndex,
-1
);
}
);
QUnit.test('one2many list edition, no "Remove" button in modal', async function (assert) {
serverData.models.partner.fields.foo.default = false;
@@ -6701,7 +6701,7 @@ QUnit.module("Views", (hooks) => {
await toggleColumnActions(0);
assert.containsNone(target, '[tabindex]:not([tabindex="-1"])');
assert.containsN(target, ".o_kanban_record", 16);
assert.hasClass(document.activeElement, "o_searchview_input");
await triggerEvent(document.activeElement, null, "keydown", { key: "ArrowDown" });