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 185a385a812..07363b2febd 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
@@ -478,7 +478,8 @@ ListRenderer.include({
*/
_moveToPreviousLine: function () {
if (this.currentRow > 0) {
- this._selectCell(this.currentRow - 1, this.columns.length - 1);
+ // FORWARD-PORT THIS COMMIT UP TO SAAS-12.2
+ this._selectCell(this.currentRow - 1, this.columns.length - 1, {inc: -1});
} else {
this.unselectRow().then(this.trigger_up.bind(this, 'add_record'));
}
@@ -692,6 +693,8 @@ ListRenderer.include({
* @param {boolean} [options.force=false] if true, force selecting the cell
* even if seems to be already the selected one (useful after a re-
* rendering, to reset the focus on the correct field)
+ * @param {integer} [options.inc=1] the increment to use when searching for
+ * the "next" possible cell (if the cell to select can't be selected)
* @return {Deferred} fails if no cell could be selected
*/
_selectCell: function (rowIndex, fieldIndex, options) {
@@ -715,7 +718,7 @@ ListRenderer.include({
var oldFieldIndex = self.currentFieldIndex;
self.currentFieldIndex = fieldIndex;
fieldIndex = self._activateFieldWidget(record, fieldIndex, {
- inc: 1,
+ inc: options.inc || 1,
wrap: wrap,
event: options && options.event,
});
@@ -868,7 +871,7 @@ ListRenderer.include({
switch (ev.data.direction) {
case 'previous':
if (this.currentFieldIndex > 0) {
- this._selectCell(this.currentRow, this.currentFieldIndex - 1, {wrap: false})
+ this._selectCell(this.currentRow, this.currentFieldIndex - 1, {inc: -1, wrap: false})
.fail(this._moveToPreviousLine.bind(this));
} else {
this._moveToPreviousLine();
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index 3d2b2881197..13b5740b66a 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -2859,6 +2859,87 @@ QUnit.module('Views', {
list.destroy();
});
+ QUnit.test('pressing SHIFT-TAB in editable list with a readonly field [REQUIRE FOCUS]', function (assert) {
+ assert.expect(4);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '' +
+ '' +
+ '' +
+ '' +
+ '',
+ });
+
+ // start on 'qux', line 3
+ testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:nth(2)'));
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=qux]')[0]);
+
+ // Press 'shift-Tab' -> should go to first cell (same line)
+ $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true});
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=foo]')[0]);
+
+ list.destroy();
+ });
+
+ QUnit.test('pressing SHIFT-TAB in editable list with a readonly field in first column [REQUIRE FOCUS]', function (assert) {
+ assert.expect(4);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '' +
+ '' +
+ '' +
+ '' +
+ '',
+ });
+
+ // start on 'foo', line 3
+ testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:nth(1)'));
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=foo]')[0]);
+
+ // Press 'shift-Tab' -> should go to previous line (last cell)
+ $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true});
+ assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(1) .o_data_cell input[name=qux]')[0]);
+
+ list.destroy();
+ });
+
+ QUnit.test('pressing SHIFT-TAB in editable list with a readonly field in last column [REQUIRE FOCUS]', function (assert) {
+ assert.expect(4);
+
+ var list = createView({
+ View: ListView,
+ model: 'foo',
+ data: this.data,
+ arch: '' +
+ '' +
+ '' +
+ '' +
+ '',
+ });
+
+ // start on 'int_field', line 3
+ testUtils.dom.click(list.$('.o_data_row:nth(2) .o_data_cell:first'));
+ assert.hasClass(list.$('.o_data_row:nth(2)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(2) .o_data_cell input[name=int_field]')[0]);
+
+ // Press 'shift-Tab' -> should go to previous line ('foo' field)
+ $(document.activeElement).trigger({type: 'keydown', which: $.ui.keyCode.TAB, shiftKey: true});
+ assert.hasClass(list.$('.o_data_row:nth(1)'), 'o_selected_row');
+ assert.strictEqual(document.activeElement, list.$('.o_data_row:nth(1) .o_data_cell input[name=foo]')[0]);
+
+ list.destroy();
+ });
+
QUnit.test('skip invisible fields when navigating list view with TAB', function (assert) {
assert.expect(2);