From 06db778cfd5c49d53b861db06ea00f2b0b374a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sun, 23 Apr 2017 23:27:35 +0200 Subject: [PATCH] [FIX] web: pressing enter in list editable create new line The editable list view (and the x2manys) have been reimplemented, but the user experience was not totally (this is an understatement) polished. With this commit, we reintroduce a good functionality: pressing enter on the last line of the list creates a new line. Also, this commit removes a 'debug: true', leftover by someone with a trigram starting by F and ending by WI. --- .../src/js/views/list/list_controller.js | 61 ++++++++++++++++--- .../js/views/list/list_editable_renderer.js | 12 ++++ addons/web/static/tests/views/form_tests.js | 1 - addons/web/static/tests/views/list_tests.js | 35 ++++++++++- 4 files changed, 99 insertions(+), 10 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 06c27a61e94..0d66eb3d4f1 100644 --- a/addons/web/static/src/js/views/list/list_controller.js +++ b/addons/web/static/src/js/views/list/list_controller.js @@ -18,6 +18,7 @@ var qweb = core.qweb; var ListController = BasicController.extend({ custom_events: _.extend({}, BasicController.prototype.custom_events, { + add_record: '_onAddRecord', button_clicked: '_onButtonClicked', change_mode: '_onChangeMode', selection_changed: '_onSelectionChanged', @@ -149,6 +150,28 @@ var ListController = BasicController.extend({ // Private //-------------------------------------------------------------------------- + /** + * Add a record to the list + * + * @private + */ + _addRecord: function () { + var self = this; + this.model.addDefaultRecord(this.handle, {position: this.editable}).then(function (recordID) { + self._toggleNoContentHelper(false); + var state = self.model.get(self.handle); + self.renderer.updateState(state); + self.renderer.editRecord(recordID); + }); + }, + /** + * Archive the current selection + * + * @private + * @param {string[]} ids + * @param {boolean} archive + * @returns {Deferred} + */ _archive: function (ids, archive) { if (ids.length === 0) { return $.when(); @@ -204,9 +227,20 @@ var ListController = BasicController.extend({ // Handlers //-------------------------------------------------------------------------- + /** + * Add a record to the list + * + * @private + * @param {OdooEvent} event + */ + _onAddRecord: function (event) { + event.stopPropagation(); + this._addRecord(); + }, /** * Handles a click on a button by performing its action. * + * @private * @param {OdooEvent} event */ _onButtonClicked: function (event) { @@ -218,6 +252,7 @@ var ListController = BasicController.extend({ * edit, and vice versa. In that case, we need to make sure that the buttons * displayed in the control panel are correct. * + * @private * @param {OdooEvent} event */ _onChangeMode: function (event) { @@ -228,6 +263,7 @@ var ListController = BasicController.extend({ * can switch to the form view with no active res_id, so it is in 'create' * mode, or we can edit inline. * + * @private * @param {MouseEvent} event */ _onCreateRecord: function (event) { @@ -236,33 +272,36 @@ var ListController = BasicController.extend({ // list editable renderer and would unselect the newly created row event.stopPropagation(); - var self = this; if (this.editable) { - this.model.addDefaultRecord(this.handle).then(function (recordID) { - self._toggleNoContentHelper(false); - var state = self.model.get(self.handle); - self.renderer.updateState(state); - self.renderer.editRecord(recordID); - }); + this._addRecord(); } else { this.trigger_up('switch_view', {view_type: 'form', res_id: undefined}); } }, /** * Called when the 'delete' action is clicked on in the side bar. + * + * @private */ _onDeleteSelectedRecords: function () { this._deleteRecords(this.selectedRecords); }, /** * Handler called when the user clicked on the 'Discard' button. + * + * @private + * @param {MouseEvent} event */ - _onDiscard: function () { + _onDiscard: function (event) { + event.stopPropagation(); this.model.discardChanges(this.handle); + this._updateButtons('readonly'); this.update(this.handle, {reload: false}); }, /** * Opens the Export Dialog + * + * @private */ _onExportData: function () { var record = this.model.get(this.handle); @@ -274,6 +313,7 @@ var ListController = BasicController.extend({ * This should be moved in basic controller, and shared between basic views. * * @override + * @private * @param {OdooEvent} event */ _onFieldChanged: function (event) { @@ -287,6 +327,7 @@ var ListController = BasicController.extend({ * row, which is done when clicking on 'Save' (anywhere outside the row * actually), so this function should only switch back to readonly mode * + * @private * @param {MouseEvent} event */ _onSave: function (event) { @@ -300,6 +341,7 @@ var ListController = BasicController.extend({ * When the current selection changes (by clicking on the checkboxes on the * left), we need to display (or hide) the 'sidebar'. * + * @private * @param {OdooEvent} event */ _onSelectionChanged: function (event) { @@ -309,6 +351,7 @@ var ListController = BasicController.extend({ /** * Called when clicking on 'Archive' or 'Unarchive' in the sidebar. * + * @private * @param {boolean} archive */ _onToggleArchiveState: function (archive) { @@ -319,6 +362,7 @@ var ListController = BasicController.extend({ * tell the model to sort itself properly, to update the pager and to * rerender the view. * + * @private * @param {OdooEvent} event */ _onToggleColumnOrder: function (event) { @@ -335,6 +379,7 @@ var ListController = BasicController.extend({ * This method just transfer the request to the model, then update the * renderer. * + * @private * @param {OdooEvent} event */ _onToggleGroup: function (event) { 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 df7a953c632..b24ec6a0c29 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 @@ -271,6 +271,15 @@ ListRenderer.include({ } return $rows; }, + /** + * @override + * @private + * @returns {Deferred} this deferred is resolved immediately + */ + _renderView: function () { + this.currentRow = null; + return this._super.apply(this, arguments); + }, /** * This is one of the trickiest method in the editable renderer. It has to * do a lot of stuff: it has to determine which cell should be selected (if @@ -563,6 +572,9 @@ ListRenderer.include({ _onMoveNextLine: function () { if (this.currentRow < this.state.data.length - 1) { this._selectCell(this.currentRow + 1, 0); + } else { + this._unselectRow(); + this.trigger_up('add_record'); } }, /** diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 00154717e5f..c780d2afa6f 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -3394,7 +3394,6 @@ QUnit.module('Views', { } return this._super(route, args); }, - debug: true, }); form.$buttons.find('.o_form_button_save').click(); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index a3abe59892b..6ec2f56a102 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -1302,7 +1302,6 @@ QUnit.module('Views', { arch: '', }); - assert.strictEqual(list.$('tr.o_data_row').length, 4, "should have 4 records"); list.$buttons.find('.o_list_button_add').click(); @@ -1528,6 +1527,40 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('pressing enter on last line of editable list view', function (assert) { + assert.expect(7); + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '', + mockRPC: function (route, args) { + assert.step(route); + return this._super.apply(this, arguments); + }, + }); + + // click on 3rd line + list.$('td:contains(gnap)').click(); + assert.ok(list.$('tr.o_data_row:eq(2)').hasClass('o_selected_row'), + "3rd row should be selected"); + + // press enter in input + list.$('tr.o_selected_row input').trigger({type: 'keydown', which: 13}); // enter + assert.ok(list.$('tr.o_data_row:eq(3)').hasClass('o_selected_row'), + "4rd row should be selected"); + assert.notOk(list.$('tr.o_data_row:eq(2)').hasClass('o_selected_row'), + "3rd row should no longer be selected"); + + // press enter on last row + list.$('tr.o_selected_row input').trigger({type: 'keydown', which: 13}); // enter + assert.strictEqual(list.$('tr.o_data_row').length, 5, "should have created a 5th row"); + + assert.verifySteps(['/web/dataset/search_read', '/web/dataset/call_kw/foo/default_get']); + list.destroy(); + }); + }); });