[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) <aab@odoo.com>
Signed-off-by: Julien Carion (juca) <juca@odoo.com>
Signed-off-by: Louis Baudoux (lba) <lba@odoo.com>
This commit is contained in:
Julien Carion (juca)
2024-04-06 09:09:12 +00:00
parent 7211ba49de
commit 4548eed71d
5 changed files with 65 additions and 8 deletions
@@ -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`);
}
});
});
@@ -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,
});
@@ -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"]);
});
@@ -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(
@@ -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",