From 4548eed71d538b8da95b84ed1f1502138fd899cb Mon Sep 17 00:00:00 2001 From: "Julien Carion (juca)" Date: Tue, 26 Mar 2024 15:22:12 +0100 Subject: [PATCH] [FIX] web, partner_autocomplete: onChange interference with selection This commit fixes an issue regarding unwanted interaction between the regular autocomplete option click selection and the onChange handler from the input field hook used in the PartnerAutoCompleteCharField component. This became an issue starting from https://github.com/odoo/odoo/pull/154203 because of the disappearance of the t-on-mousedown.prevent handler placed on the autocomplete options list which would prevent the onChange event from being triggered when clicking on an option. The issue would be that the onChange handler from the input field hook would take precedence over the option click handler of the autocomplete which would most of the time be ignored afterwards. The solution found for This problem is to prevent the immediate propagation of the change event in the autocomplete handler when an option has been clicked on so that it will never be propagated to the input field hook handler in this case. Steps to reproduce: - Go to contacts and open a company contact - Type in the name field a few characters (at least 3) - Click on any autocomplete option Most of the time, the option will not be applied and the name won't change closes odoo/odoo#160561 X-original-commit: aa1e5eabb67800fbf4155c9cae12b90d97bb75db Signed-off-by: Aaron Bohy (aab) Signed-off-by: Julien Carion (juca) Signed-off-by: Louis Baudoux (lba) --- .../tests/partner_autocomplete_tests.js | 55 ++++++++++++++++++- .../src/core/autocomplete/autocomplete.js | 5 +- .../static/tests/core/autocomplete_tests.js | 10 +++- .../many2many_tags_avatar_field_tests.js | 2 +- .../views/fields/many2one_field_tests.js | 1 + 5 files changed, 65 insertions(+), 8 deletions(-) diff --git a/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js b/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js index 6c008a8ad63..c80c30b8584 100644 --- a/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js +++ b/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js @@ -163,9 +163,9 @@ QUnit.module('partner_autocomplete', { } else if (route === "/web/dataset/call_kw/res.partner/enrich_company") { return Promise.resolve({ - "partner_gid": 1, - "website": "firstcompany.com", - "name": "First company", + "partner_gid": args.args[1], + "website": args.args[0], + "name": args.args[1] === 1 ? "First company" : "Second company", 'logo': false, "ignored": false, "vat": "Some VAT number", @@ -400,4 +400,53 @@ QUnit.module('partner_autocomplete', { "Clearbit and Odoo autocomplete options should be shown" ); }); + + QUnit.test("Partner autocomplete : onChange should not disturb option selection", async function (assert) { + await makeView(makeViewParams); + + // Set company type to Company + await editSelect(target, "[name='company_type'] > select", '"company"'); + + const input = target.querySelector("[name='name'] .dropdown input"); + const autocompleteContainer = input.parentElement; + + await click(input, null); + await editInputNoChangeEvent(input, "company"); + assert.containsN( + autocompleteContainer, + ".o-autocomplete--dropdown-item", + 6, + "Clearbit and Odoo autocomplete options should be shown" + ); + // Click on the second option (include realistic events) - "Second company" + await triggerEvent( + target.querySelectorAll(".o-autocomplete--dropdown-item")[1], + "", + "pointerdown" + ); + await triggerEvent( + target.querySelectorAll(".o-autocomplete--dropdown-item")[1], + "", + "mousedown" + ); + await triggerEvent(input, "", "change"); + await triggerEvent(input, "", "blur"); + await click(target.querySelectorAll(".o-autocomplete--dropdown-item")[1], ""); + + // Check that the fields have been filled + const expectedValues = { + "website": "secondcompany.com", + "name": "Second company", + "vat": "Some VAT number", + "street": "Some street", + "city": "Some city", + "zip": "1234", + "phone": "+0123456789", + "country_id": "United States", + "state_id": "California (US)", + }; + for (const [fieldName, expectedValue] of Object.entries(expectedValues)) { + assert.strictEqual(target.querySelector(`[name=${fieldName}] input`).value, expectedValue, `${fieldName} should be filled`); + } + }); }); diff --git a/addons/web/static/src/core/autocomplete/autocomplete.js b/addons/web/static/src/core/autocomplete/autocomplete.js index 745fe276680..b5dcb0604d1 100644 --- a/addons/web/static/src/core/autocomplete/autocomplete.js +++ b/addons/web/static/src/core/autocomplete/autocomplete.js @@ -260,7 +260,10 @@ export class AutoComplete extends Component { this.close(); } } - onInputChange() { + onInputChange(ev) { + if (this.ignoreBlur) { + ev.stopImmediatePropagation(); + } this.props.onChange({ inputValue: this.inputRef.el.value, }); diff --git a/addons/web/static/tests/core/autocomplete_tests.js b/addons/web/static/tests/core/autocomplete_tests.js index 611972ce529..896130bc0c0 100644 --- a/addons/web/static/tests/core/autocomplete_tests.js +++ b/addons/web/static/tests/core/autocomplete_tests.js @@ -540,7 +540,11 @@ QUnit.module("Components", (hooks) => { assert.strictEqual(mousedownEvent.defaultPrevented, false); await triggerEvent(input, "", "change"); await triggerEvent(input, "", "blur"); - await click(target.querySelectorAll(".o-autocomplete--dropdown-item")[1], ""); + await triggerEvent( + target.querySelectorAll(".o-autocomplete--dropdown-item")[1], + "", + "click" + ); assert.verifySteps(["change", "select Hello"]); assert.strictEqual(input, document.activeElement); @@ -550,8 +554,8 @@ QUnit.module("Components", (hooks) => { await triggerEvent(input, "", "input"); await triggerEvent(target, "", "pointerdown"); await triggerEvent(input, "", "change"); - input.blur(); - await click(target, ""); + await triggerEvent(input, "", "blur"); + await triggerEvent(target, "", "click"); assert.verifySteps(["change", "blur"]); }); diff --git a/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js b/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js index e7691d2cd75..a3dd9bdae09 100644 --- a/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js +++ b/addons/web/static/tests/views/fields/many2many_tags_avatar_field_tests.js @@ -445,7 +445,7 @@ QUnit.module("Fields", (hooks) => { ); assert.strictEqual( o_kanban_record.querySelector("img.o_m2m_avatar").dataset.src, - "/web/image/partner/4/avatar_128", + "/web/image/partner/5/avatar_128", "should have correct avatar image" ); await click( diff --git a/addons/web/static/tests/views/fields/many2one_field_tests.js b/addons/web/static/tests/views/fields/many2one_field_tests.js index e6a69015de9..74e3c98cfef 100644 --- a/addons/web/static/tests/views/fields/many2one_field_tests.js +++ b/addons/web/static/tests/views/fields/many2one_field_tests.js @@ -3145,6 +3145,7 @@ QUnit.module("Fields", (hooks) => { // select a new value then cancel the creation of the new one --> restore the previous await click(target, ".o_field_widget[name=product_id] input"); await click(target.querySelector(".ui-menu-item")); + await triggerEvent(target, ".o_field_many2one input", "blur"); assert.strictEqual( target.querySelector(".o_field_many2one input").value, "xphone",