From 28eadb3d6ee11bcee9bfe498387467c70953382a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Sat, 20 May 2017 15:10:21 +0200 Subject: [PATCH] [FIX] web: improve navigation in field many2one Before this commit, when the user write some text in a many2one, then press ENTER, the key event will trigger a navigation move, which will validate the field, and move the focus to the next line. The same issue happens when pressing TAB. With this commit, we simply stop the propagation, and preventdefault the event that cause the selection. --- .../static/src/js/fields/relational_fields.js | 8 +- .../tests/fields/relational_fields_tests.js | 78 +++++++++++++++++++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index d48fa64466c..e89fb563f21 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -88,6 +88,7 @@ var FieldMany2One = AbstractField.extend({ 'click .o_external_button': '_onExternalButtonClick', 'click': '_onClick', }), + AUTOCOMPLETE_DELAY: 200, init: function () { this._super.apply(this, arguments); @@ -147,6 +148,11 @@ var FieldMany2One = AbstractField.extend({ }); }, select: function (event, ui) { + // we do not want the select event to trigger any additional + // effect, such as navigating to another field. + event.stopImmediatePropagation(); + event.preventDefault(); + var item = ui.item; self.floating = false; if (item.id) { @@ -162,7 +168,7 @@ var FieldMany2One = AbstractField.extend({ autoFocus: true, html: true, minLength: 0, - delay: 200, + delay: this.AUTOCOMPLETE_DELAY, }); this.$input.autocomplete("option", "position", { my : "left top", at: "left bottom" }); this.autocomplete_bound = true; diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 62fe6b823e7..f1eb12d8516 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -1,8 +1,10 @@ odoo.define('web.relational_fields_tests', function (require) { "use strict"; +var concurrency = require('web.concurrency'); var FormView = require('web.FormView'); var ListView = require('web.ListView'); +var relationalFields = require('web.relational_fields'); var testUtils = require('web.test_utils'); var createView = testUtils.createView; @@ -629,6 +631,82 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('pressing enter in a m2o in an editable list', function (assert) { + assert.expect(9); + var done = assert.async(); + var M2O_DELAY = relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY; + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = 0; + + var list = createView({ + View: ListView, + model: 'partner', + data: this.data, + arch: '', + }); + + list.$('td.o_data_cell:first').click(); + assert.strictEqual(list.$('.o_selected_row').length, 1, + "should have a row in edit mode"); + + // we now write 'a' and press enter to check that the selection is + // working, and prevent the navigation + list.$('td.o_data_cell input:first').val('a').trigger('input'); + concurrency.delay(0).then(function () { + var $input = list.$('td.o_data_cell input:first'); + var $dropdown = $input.autocomplete('widget'); + assert.ok($dropdown.is(':visible'), "autocomplete dropdown should be visible"); + + // we now trigger ENTER to select first choice + $input.trigger($.Event('keydown', { + which: $.ui.keyCode.ENTER, + keyCode: $.ui.keyCode.ENTER, + })); + assert.strictEqual($input[0], document.activeElement, + "input should still be focused"); + + // we now trigger again ENTER to make sure we can move to next line + $input.trigger($.Event('keydown', { + which: $.ui.keyCode.ENTER, + keyCode: $.ui.keyCode.ENTER, + })); + + assert.notOk(document.contains($input[0]), + "input should no longer be in dom"); + assert.ok(list.$('tr.o_data_row:eq(1)').hasClass('o_selected_row'), + "second row should now be selected"); + + // we now write again 'a' in the cell to select xpad. We will now + // test with the tab key + list.$('td.o_data_cell input:first').val('a').trigger('input'); + return concurrency.delay(0); + }).then(function () { + var $input = list.$('td.o_data_cell input:first'); + var $dropdown = $input.autocomplete('widget'); + assert.ok($dropdown.is(':visible'), "autocomplete dropdown should be visible"); + $input.trigger($.Event('keydown', { + which: $.ui.keyCode.TAB, + keyCode: $.ui.keyCode.TAB, + })); + assert.strictEqual($input[0], document.activeElement, + "input should still be focused"); + + // we now trigger again ENTER to make sure we can move to next line + $input.trigger($.Event('keydown', { + which: $.ui.keyCode.TAB, + keyCode: $.ui.keyCode.TAB, + })); + + assert.notOk(document.contains($input[0]), + "input should no longer be in dom"); + assert.ok(list.$('tr.o_data_row:eq(2)').hasClass('o_selected_row'), + "third row should now be selected"); + list.destroy(); + relationalFields.FieldMany2One.prototype.AUTOCOMPLETE_DELAY = M2O_DELAY; + done(); + }); + }); + + QUnit.module('FieldOne2Many'); QUnit.test('one2many basic properties', function (assert) {