[FIX] web: unselect row inside x2many dialog

Have an x2many with an x2many editable list inside the form view
containing at least a row. Open a record, click discard. Open it
again, and click on a row of the x2many to switch it to edit mode.
Before this commit, the row could never be switched back to
readonly. This was the list renderer didn't correctly compute its
active element (it must wait for a micro tick for the mounted hook
of the dialog, its ancestor, to be called).

After fixing this issue, a second one arise: the row can now be
unselected, but it disappears if it is a new record. This is
because we lost the dirty flag when clicking on discard. This is
fixed by storing the dirty state alongside changes in the
savepoint.

Part-of: odoo/odoo#114024
This commit is contained in:
Aaron Bohy
2023-07-24 20:17:51 +02:00
parent ad1539b20b
commit cd6d54d7bf
3 changed files with 20 additions and 8 deletions
@@ -37,7 +37,6 @@ export class Record extends DataPoint {
this._unsetRequiredFields = markRaw(new Set());
this._closeInvalidFieldsNotification = () => {};
let savePoint;
const missingFields = this.fieldNames.filter((fieldName) => !(fieldName in data));
const vals = this._parseServerValues({
...this._getDefaultValues(missingFields),
@@ -46,13 +45,14 @@ export class Record extends DataPoint {
if (this.resId) {
this._values = markRaw(vals);
this._changes = markRaw({});
savePoint = {};
} else {
this._values = markRaw({});
this._changes = markRaw(vals);
savePoint = { ...this._changes };
}
this._savePoint = markRaw(savePoint);
this._savePoint = markRaw({
dirty: false,
changes: { ...this._changes },
});
this.data = { ...this._values, ...this._changes };
const parentRecord = this._parentRecord;
@@ -258,7 +258,8 @@ export class Record extends DataPoint {
// -------------------------------------------------------------------------
_addSavePoint() {
Object.assign(this._savePoint, this._changes);
this._savePoint.dirty = this.dirty;
Object.assign(this._savePoint.changes, this._changes);
for (const fieldName in this._changes) {
if (["one2many", "many2many"].includes(this.fields[fieldName].type)) {
this._changes[fieldName]._addSavePoint();
@@ -400,13 +401,13 @@ export class Record extends DataPoint {
}
_discard() {
this.dirty = false;
for (const fieldName in this._changes) {
if (["one2many", "many2many"].includes(this.fields[fieldName].type)) {
this._changes[fieldName]._discard();
}
}
this._changes = { ...this._savePoint };
this.dirty = this._savePoint.dirty;
this._changes = { ...this._savePoint.changes };
this.data = { ...this._values, ...this._changes };
this._setEvalContext();
this._invalidFields.clear();
@@ -110,7 +110,10 @@ export class ListRenderer extends Component {
this.cellToFocus = null;
this.activeRowId = null;
onMounted(() => {
onMounted(async () => {
// Due to the way elements are mounted in the DOM by Owl (bottom-to-top),
// we need to wait the next micro task tick to set the activeElement.
await Promise.resolve();
this.activeElement = this.uiService.activeElement;
});
onWillPatch(() => {
@@ -11323,6 +11323,14 @@ QUnit.module("Fields", (hooks) => {
"second partner turtle is Michelangelo"
);
await clickDiscard(target.querySelector(".modal"));
// re-open, edit michelangelo row, click out -> row still there, in readonly
await click(target.querySelectorAll(".o_data_row")[1].querySelector(".o_data_cell"));
await click(target.querySelector(".modal .o_data_row .o_data_cell"));
assert.containsOnce(target, ".modal .o_selected_row");
await click(target.querySelector(".modal"));
assert.containsOnce(target, ".modal .o_data_row");
assert.strictEqual(target.querySelector(".modal .o_data_cell").innerText, "Michelangelo");
});
QUnit.test("quickly switch between pages in one2many list", async function (assert) {