From 51483c3e7d7effcd86105436af73e4718b02e71d Mon Sep 17 00:00:00 2001 From: Julien Mougenot Date: Fri, 29 Nov 2019 10:37:34 +0000 Subject: [PATCH] [IMP] web/list: multi edition ux This commit introcudes two changes to the multi edition in list views: 1. Editing one record does not require confirmation through a dialog 2. Successfully saving multiple records unselects all currently checked records Task 2146429 closes odoo/odoo#41099 Signed-off-by: Aaron Bohy (aab) --- .../src/js/views/list/list_controller.js | 35 +++++++++++++------ .../js/views/list/list_editable_renderer.js | 8 ++--- addons/web/static/tests/views/list_tests.js | 22 ++++++------ 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js index fbd5e4a2af0..51ecf42007d 100644 --- a/addons/web/static/src/js/views/list/list_controller.js +++ b/addons/web/static/src/js/views/list/list_controller.js @@ -400,6 +400,25 @@ var ListController = BasicController.extend({ return result; }, []); return new Promise((resolve, reject) => { + const saveRecords = () => { + this.model.saveRecords(this.handle, recordId, validRecordIds, fieldName) + .then(async () => { + this._updateButtons('readonly'); + const state = this.model.get(this.handle); + // We need to check the current multi-editable state here + // in case the selection is changed. If there are changes + // and the list was multi-editable, we do not want to select + // the next row. + this.selectedRecords = []; + await this.renderer.updateState(state, { + keepWidths: true, + selectedRecords: [], + }); + this.renderer.focusCell(recordId, node); + resolve(!Object.keys(changes).length); + }) + .guardedCatch(discardAndReject); + }; const discardAndReject = () => { this.model.discardChanges(recordId); this._confirmSave(recordId).then(() => { @@ -408,18 +427,12 @@ var ListController = BasicController.extend({ }); }; if (validRecordIds.length > 0) { + if (recordIds.length === 1) { + // Save without prompt + return saveRecords(); + } const dialogOptions = { - confirm_callback: () => { - this.model.saveRecords(this.handle, recordId, validRecordIds, fieldName) - .then(async () => { - this._updateButtons('readonly'); - const state = this.model.get(this.handle); - await this.renderer.updateState(state, { keepWidths: true }); - await this.renderer.focusCell(recordId, node); - resolve(Object.keys(changes)); - }) - .guardedCatch(discardAndReject); - }, + confirm_callback: saveRecords, cancel_callback: discardAndReject, }; const record = this.model.get(recordId); diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 5cdde3e12dd..62f715d3967 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -507,12 +507,12 @@ ListRenderer.include({ onSuccess: resolve, onFailure: reject, }); - }).then(changedFields => { + }).then(selectNextRow => { this._enableRecordSelectors(); // If any field has changed and if the list is in multiple edition, // we send a truthy boolean to _selectRow to tell it not to select // the following record. - return changedFields && changedFields.length && this.isInMultipleRecordEdition(recordID); + return selectNextRow; }).guardedCatch(() => { toggleWidgets(false); }); @@ -1300,8 +1300,8 @@ ListRenderer.include({ var recordId = this._getRecordID(rowIndex); // To select a row, the currently selected one must be unselected first var self = this; - return this.unselectRow().then(noSelectNext => { - if (noSelectNext) { + return this.unselectRow().then((selectNextRow = true) => { + if (!selectNextRow) { return Promise.resolve(); } if (!recordId) { diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index d42bd2e1edd..2e2235306d1 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -5495,7 +5495,7 @@ QUnit.module('Views', { }); QUnit.test('editable list view: multi edition', async function (assert) { - assert.expect(24); + assert.expect(26); var list = await createView({ View: ListView, @@ -5549,6 +5549,8 @@ QUnit.module('Views', { await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); assert.containsOnce(document.body, '.modal', "modal appears when switching cells"); await testUtils.dom.click($('.modal .btn:contains(Cancel)')); + assert.containsN(list, '.o_list_record_selector input:checked', 2, + "Selection should remain unchanged"); assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), 'yop10', "changes have been discarded and row is back to readonly"); assert.strictEqual(document.activeElement, list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')[0], @@ -5559,6 +5561,7 @@ QUnit.module('Views', { assert.ok($('.modal').text().includes('those 2 records'), "the number of records should be correctly displayed"); await testUtils.dom.click($('.modal .btn-primary')); assert.containsNone(list, '.o_data_cell input.o_field_widget', "no field should be editable anymore"); + assert.containsNone(list, '.o_list_record_selector input:checked', "no record should be selected anymore"); assert.verifySteps(['write', 'read']); assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "yop666", "the first row should be updated"); @@ -5626,18 +5629,16 @@ QUnit.module('Views', { await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), 'hi'); - await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(1)')); - - assert.containsOnce(document.body, '.modal'); // save dialog - await testUtils.dom.click($('.modal .btn-primary')); + assert.containsNone(document.body, '.modal'); assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "hi2"); assert.strictEqual(list.$('.o_data_row:eq(1) .o_data_cell').text(), "blip9"); assert.verifySteps(['write', 'read']); // select the second record (the first one is still selected) - assert.ok(list.$('.o_data_row:eq(0) .o_list_record_selector input').is(':checked')); + assert.containsNone(list, '.o_list_record_selector input:checked'); + await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); await testUtils.dom.click(list.$('.o_data_row:eq(1) .o_list_record_selector input')); // edit foo, first row @@ -6063,7 +6064,7 @@ QUnit.module('Views', { list.destroy(); }); - QUnit.test('editable readonly list view: single edition behaves like a multi-edition', async function (assert) { + QUnit.test('editable readonly list view: single edition does not behave like a multi-edition', async function (assert) { assert.expect(3); const list = await createView({ @@ -6083,17 +6084,14 @@ QUnit.module('Views', { await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), ""); - assert.containsOnce($('body'),'.modal', "should have a modal"); + assert.containsOnce($('body'),'.modal', "should have a modal (invalid fields)"); await testUtils.dom.click($('.modal button.btn')); // edit a field await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_data_cell:eq(0)')); await testUtils.fields.editInput(list.$('.o_field_widget[name=foo]'), "bar"); - assert.containsOnce($('body'),'.modal', "should have a modal"); - await testUtils.dom.click($('.modal button.btn.btn-primary')); - - + assert.containsNone($('body'),'.modal', "should not have a modal"); assert.strictEqual(list.$('.o_data_row:eq(0) .o_data_cell').text(), "bar", "the first row should be updated");