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();
+ });
+
});
});