diff --git a/addons/web/static/src/views/list/list_renderer.js b/addons/web/static/src/views/list/list_renderer.js
index e6aabcfeed3..7d0e7d2ce7e 100644
--- a/addons/web/static/src/views/list/list_renderer.js
+++ b/addons/web/static/src/views/list/list_renderer.js
@@ -1377,44 +1377,6 @@ export class ListRenderer extends Component {
return false;
}
- async onCreateAction(context) {
- // TO DISCUSS: is it a use case for owl `batched()` ?
- if (this.createProm) {
- return;
- }
- this.props.onAdd({ context });
- this.createProm = Promise.resolve();
- this.createProm.then(() => {
- this.lastCreatingAction = true;
- });
- await this.createProm;
- this.createProm = null;
- }
-
- /**
- * @param {FocusEvent & {
- * target: HTMLElement,
- * relatedTarget: HTMLElement | null
- * }} ev
- */
- onFocusIn(ev) {
- const { relatedTarget, target } = ev;
- const fromOutside = !this.rootRef.el.contains(relatedTarget);
- if (!fromOutside) {
- return;
- }
-
- const isX2MRowAdder =
- target.tagName === "A" &&
- target.parentElement.classList.contains("o_field_x2many_list_row_add");
- const withinSameUIActiveElement =
- this.uiService.getActiveElementOf(relatedTarget) === this.activeElement;
- if (withinSameUIActiveElement && isX2MRowAdder) {
- const { context } = this.creates[0];
- this.onCreateAction(context);
- }
- }
-
setDirty(isDirty) {
this.lastIsDirty = isDirty;
}
diff --git a/addons/web/static/src/views/list/list_renderer.xml b/addons/web/static/src/views/list/list_renderer.xml
index dc1c3883761..9b2ed6fdbf8 100644
--- a/addons/web/static/src/views/list/list_renderer.xml
+++ b/addons/web/static/src/views/list/list_renderer.xml
@@ -6,7 +6,6 @@
class="o_list_renderer"
t-att-class="{'table-responsive': showTable}"
t-attf-class="{{ props.archInfo.className or '' }}"
- t-on-focusin="onFocusIn"
tabindex="-1"
t-ref="root"
>
@@ -48,7 +47,7 @@
role="button"
t-att-class="create_index !== 0 ? 'ml16' : ''"
t-att-tabindex="props.list.editedRecord ? '-1' : '0'"
- t-on-click.stop.prevent="() => this.onCreateAction(create.context)"
+ t-on-click.stop.prevent="() => this.props.onAdd({context: create.context})"
>
diff --git a/addons/web/static/tests/views/fields/one2many_field_tests.js b/addons/web/static/tests/views/fields/one2many_field_tests.js
index 0eb73b9c506..7afc91d63d3 100644
--- a/addons/web/static/tests/views/fields/one2many_field_tests.js
+++ b/addons/web/static/tests/views/fields/one2many_field_tests.js
@@ -11671,7 +11671,7 @@ QUnit.module("Fields", (hooks) => {
);
QUnit.test(
- "when Navigating to a one2many with tabs, it receives the focus and adds a new line",
+ "when Navigating to a one2many with tabs, the button add a line receives the focus",
async function (assert) {
await makeView({
type: "form",
@@ -11698,21 +11698,19 @@ QUnit.module("Fields", (hooks) => {
});
await clickEdit(target);
-
assert.strictEqual(target.querySelector("[name=qux] input"), document.activeElement);
getNextTabableElement(target).focus(); // go inside one2many
await nextTick();
-
assert.strictEqual(
- target.querySelector("[name=turtle_foo] input"),
+ target.querySelector(".o_field_x2many_list_row_add a"),
document.activeElement
);
}
);
QUnit.test(
- "when Navigating to a one2many with tabs, not filling any field and hitting tab, we should not add a first line but navigate to the next control",
+ "Navigate to a one2many with tab then tab again focus the next field",
async function (assert) {
serverData.models.partner.records[0].turtles = [];
@@ -11752,16 +11750,85 @@ QUnit.module("Fields", (hooks) => {
await nextTick();
assert.strictEqual(
+ target.querySelector(".o_field_x2many_list_row_add a"),
+ document.activeElement
+ );
+ assert.containsNone(target, "[name=turtles] .o_selected_row");
+
+ const nextInput = target.querySelector("[name=foo] input");
+ // trigger Tab event and check that the default behavior can happen.
+ const event = triggerEvent(
document.activeElement,
- target.querySelector("[name=turtle_foo] input")
+ null,
+ "keydown",
+ { key: "Tab" },
+ { fast: true }
+ );
+ assert.strictEqual(getNextTabableElement(target), nextInput);
+ assert.ok(!event.defaultPrevented);
+ nextInput.focus();
+ await nextTick();
+ assert.strictEqual(document.activeElement, nextInput);
+ }
+ );
+
+ QUnit.test(
+ "when Navigating to a one2many with tabs, not filling any field and hitting tab, no line is added and the next field is focused",
+ async function (assert) {
+ serverData.models.partner.records[0].turtles = [];
+
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ serverData,
+ arch: `
+
`,
+ resId: 1,
+ });
+
+ await clickEdit(target);
+
+ assert.strictEqual(document.activeElement, target.querySelector("[name=qux] input"));
+
+ getNextTabableElement(target).focus(); // go inside one2many
+ await nextTick();
+
+ assert.strictEqual(
+ target.querySelector(".o_field_x2many_list_row_add a"),
+ document.activeElement
+ );
+ assert.containsNone(target, "[name=turtles] .o_selected_row");
+
+ await addRow(target);
+ assert.strictEqual(
+ target.querySelector("[name=turtle_foo] input"),
+ document.activeElement
);
triggerHotkey("Tab"); // go to turtle_description field
await nextTick();
-
assert.strictEqual(
- document.activeElement,
- target.querySelector("[name=turtle_description] textarea")
+ target.querySelector("[name=turtle_description] textarea"),
+ document.activeElement
);
const nextInput = target.querySelector("[name=foo] input");
@@ -11778,9 +11845,6 @@ QUnit.module("Fields", (hooks) => {
nextInput.focus();
await nextTick();
assert.strictEqual(document.activeElement, nextInput);
- // the default behavior happens but the list renderer makes the edited record
- // pass in readonly mode.
- assert.containsNone(target, "[name=turtles] .o_selected_row");
}
);
@@ -11828,7 +11892,12 @@ QUnit.module("Fields", (hooks) => {
getNextTabableElement(target).focus(); // go inside one2many
await nextTick();
+ assert.strictEqual(
+ target.querySelector(".o_field_x2many_list_row_add a"),
+ document.activeElement
+ );
+ await addRow(target);
assert.strictEqual(
target.querySelector(".modal [name=turtle_foo] input"),
document.activeElement
@@ -11937,17 +12006,6 @@ QUnit.module("Fields", (hooks) => {
firstCreateActionLink.focus(); // goes inside one2many
await nextTick();
- assert.containsOnce(target, "[name=p] .o_selected_row");
- assert.strictEqual(
- document.activeElement,
- target.querySelector("[name=p] .o_selected_row input")
- );
-
- // press ESC to cancel 1st control click (create)
- triggerHotkey("Escape");
- await nextTick();
-
- assert.containsNone(target, "[name=p] .o_selected_row");
assert.strictEqual(
document.activeElement,
target.querySelector(".o_field_x2many_list_row_add a")