diff --git a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
index c57e85fb726..d52fd91af4f 100644
--- a/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
+++ b/addons/partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js
@@ -73,6 +73,9 @@ export class PartnerAutoCompleteCharField extends CharField {
}
});
this.props.record.update(data.company);
+ if (this.props.setDirty) {
+ this.props.setDirty(false);
+ }
}
}
diff --git a/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js b/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js
index c80c30b8584..727b8b404df 100644
--- a/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js
+++ b/addons/partner_autocomplete/static/tests/partner_autocomplete_tests.js
@@ -4,11 +4,11 @@ import { browser } from "@web/core/browser/browser";
import { registry } from "@web/core/registry";
import {
click,
+ editInput,
editSelect,
getFixture,
patchWithCleanup,
triggerEvent,
- editInput,
} from "@web/../tests/helpers/utils";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { loadJS } from "@web/core/assets";
@@ -348,18 +348,20 @@ QUnit.module('partner_autocomplete', {
}
});
- QUnit.test("Show confirmation dialog on input blur", async function (assert) {
- assert.expect(1);
+ QUnit.test("Click out after edition", async function (assert) {
+ assert.expect(2);
await makeView(makeViewParams);
const input = target.querySelector("[name=parent_id] input.o-autocomplete--input.o_input");
await triggerEvent(input, null, "focus");
await click(input);
await editInput(input, null, "go");
+ assert.strictEqual(input.value, "go");
+ await triggerEvent(target, null, "pointerdown");
await triggerEvent(input, null, "blur");
- assert.containsOnce(target, ".o_dialog");
+ assert.strictEqual(input.value, "");
});
- QUnit.test("Hide auto complate suggestion for no create", async function (assert) {
+ QUnit.test("Hide auto complete suggestion for no create", async function (assert) {
const partnerMakeViewParams = {
...makeViewParams,
arch:
diff --git a/addons/sale/static/src/js/sale_product_field.js b/addons/sale/static/src/js/sale_product_field.js
index cd0856ce332..eec807b165d 100644
--- a/addons/sale/static/src/js/sale_product_field.js
+++ b/addons/sale/static/src/js/sale_product_field.js
@@ -20,7 +20,7 @@ export class SaleOrderLineProductField extends Many2OneField {
isInternalUpdate = true;
return updateRecord.call(this, value);
};
- useEffect(value => {
+ useEffect(value => {
if (!isMounted) {
isMounted = true;
} else if (value && isInternalUpdate) {
diff --git a/addons/sale/static/tests/sale_product_field_tests.js b/addons/sale/static/tests/sale_product_field_tests.js
index f899c70468f..6bc027df492 100644
--- a/addons/sale/static/tests/sale_product_field_tests.js
+++ b/addons/sale/static/tests/sale_product_field_tests.js
@@ -1,7 +1,13 @@
/** @odoo-module **/
import {
- getFixture, patchWithCleanup, addRow, editInput, triggerEvent, click } from "@web/../tests/helpers/utils";
+ getFixture,
+ patchWithCleanup,
+ addRow,
+ editInput,
+ triggerHotkey,
+ nextTick
+} from "@web/../tests/helpers/utils";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { browser } from "@web/core/browser/browser";
@@ -88,7 +94,7 @@ QUnit.module("Fields", (hooks) => {
QUnit.module("Sale product field");
- QUnit.test("blurring input with incomplete text will propose to create product", async function (assert) {
+ QUnit.test("pressing tab with incomplete text will create a product", async function (assert) {
await makeView({
type: "form",
@@ -112,23 +118,16 @@ QUnit.module("Fields", (hooks) => {
// add a line and enter new product name
await addRow(target, ".o_field_x2many_list");
await editInput(target, "[name='product_template_id'] input", "new product");
-
- // blur input => should ask for confirmation if we want to create product
- await triggerEvent(target, "[name='product_template_id'] input", "blur");
- assert.containsOnce(target, ".modal:contains(Create new product as a new Product)")
+ await triggerHotkey("tab");
+ await nextTick();
assert.verifySteps([
"get_views",
"onchange",
"onchange",
"name_search",
- ]);
-
- await click(target, ".modal button.btn-primary");
- assert.verifySteps([
"name_create",
"get_single_product_variant",
]);
-
});
});
diff --git a/addons/web/static/src/core/autocomplete/autocomplete.js b/addons/web/static/src/core/autocomplete/autocomplete.js
index b5dcb0604d1..9e23db6d0fb 100644
--- a/addons/web/static/src/core/autocomplete/autocomplete.js
+++ b/addons/web/static/src/core/autocomplete/autocomplete.js
@@ -104,6 +104,16 @@ export class AutoComplete extends Component {
this.state.activeSourceOption = null;
}
+ cancel() {
+ if (this.inputRef.el.value.length) {
+ if (this.props.autoSelect) {
+ this.inputRef.el.value = this.props.value;
+ this.props.onCancel();
+ }
+ }
+ this.close();
+ }
+
async loadSources(useInput) {
this.sources = [];
this.state.activeSourceOption = null;
@@ -237,21 +247,10 @@ export class AutoComplete extends Component {
this.ignoreBlur = false;
return;
}
- const value = this.inputRef.el.value;
- if (
- this.props.autoSelect &&
- this.state.activeSourceOption &&
- value.length > 0 &&
- value !== this.props.value
- ) {
- this.selectOption(this.state.activeSourceOption, { triggeredOnBlur: true });
- } else {
- this.props.onBlur({
- inputValue: value,
- });
- this.inEdition = false;
- this.close();
- }
+ this.props.onBlur({
+ inputValue: this.inputRef.el.value,
+ });
+ this.inEdition = false;
}
onInputClick() {
if (!this.isOpened) {
@@ -325,7 +324,7 @@ export class AutoComplete extends Component {
if (!this.isOpened) {
return;
}
- this.close();
+ this.cancel();
break;
case "tab":
if (!this.isOpened) {
@@ -373,7 +372,7 @@ export class AutoComplete extends Component {
externalClose(ev) {
if (this.isOpened && !this.root.el.contains(ev.target)) {
- this.close();
+ this.cancel();
}
}
}
@@ -397,6 +396,7 @@ Object.assign(AutoComplete, {
placeholder: { type: String, optional: true },
autoSelect: { type: Boolean, optional: true },
resetOnSelect: { type: Boolean, optional: true },
+ onCancel: { type: Function, optional: true },
onInput: { type: Function, optional: true },
onChange: { type: Function, optional: true },
onBlur: { type: Function, optional: true },
@@ -411,6 +411,7 @@ Object.assign(AutoComplete, {
placeholder: "",
autoSelect: false,
dropdown: true,
+ onCancel: () => {},
onInput: () => {},
onChange: () => {},
onBlur: () => {},
diff --git a/addons/web/static/src/views/fields/many2one/many2one_field.js b/addons/web/static/src/views/fields/many2one/many2one_field.js
index 97f4609d252..296245967d0 100644
--- a/addons/web/static/src/views/fields/many2one/many2one_field.js
+++ b/addons/web/static/src/views/fields/many2one/many2one_field.js
@@ -134,10 +134,7 @@ export class Many2OneField extends Component {
};
if (this.props.canQuickCreate) {
- this.quickCreate = (name, params = {}) => {
- if (params.triggeredOnBlur) {
- return this.openConfirmationDialog(name);
- }
+ this.quickCreate = (name) => {
this.state.isFloating = false;
return this.updateRecord([false, name]);
};
@@ -368,7 +365,7 @@ export const many2OneField = {
extractProps({ attrs, context, decorations, options, string }, dynamicInfo) {
const hasCreatePermission = attrs.can_create ? evaluateBooleanExpr(attrs.can_create) : true;
const hasWritePermission = attrs.can_write ? evaluateBooleanExpr(attrs.can_write) : true;
- const canCreate = options.no_create ? false : hasCreatePermission
+ const canCreate = options.no_create ? false : hasCreatePermission;
return {
placeholder: attrs.placeholder,
canOpen: !options.no_open,
diff --git a/addons/web/static/src/views/fields/properties/property_value.js b/addons/web/static/src/views/fields/properties/property_value.js
index 87e7e41007b..67bbb6cfaf4 100644
--- a/addons/web/static/src/views/fields/properties/property_value.js
+++ b/addons/web/static/src/views/fields/properties/property_value.js
@@ -293,10 +293,6 @@ export class PropertyValue extends Component {
* @param {object} params
*/
async onQuickCreate(name, params = {}) {
- if (params.triggeredOnBlur) {
- this.onValueChange(false);
- return;
- }
const result = await this.orm.call(this.props.comodel, "name_create", [name], {
context: this.props.context,
});
diff --git a/addons/web/static/src/views/fields/relational_utils.js b/addons/web/static/src/views/fields/relational_utils.js
index 09f20f90acc..d0994f3f68d 100644
--- a/addons/web/static/src/views/fields/relational_utils.js
+++ b/addons/web/static/src/views/fields/relational_utils.js
@@ -253,6 +253,9 @@ export class Many2XAutocomplete extends Component {
this.props.setInputFloats(true);
}
}
+ onCancel() {
+ this.props.setInputFloats(false);
+ }
onSelect(option, params = {}) {
if (option.action) {
diff --git a/addons/web/static/src/views/fields/relational_utils.xml b/addons/web/static/src/views/fields/relational_utils.xml
index 8a20593f518..224868015dc 100644
--- a/addons/web/static/src/views/fields/relational_utils.xml
+++ b/addons/web/static/src/views/fields/relational_utils.xml
@@ -59,6 +59,7 @@
dropdown="props.dropdown"
autofocus="props.autofocus"
resetOnSelect="props.value === ''"
+ onCancel.bind="onCancel"
/>
diff --git a/addons/web/static/tests/core/autocomplete_tests.js b/addons/web/static/tests/core/autocomplete_tests.js
index 896130bc0c0..6802c900341 100644
--- a/addons/web/static/tests/core/autocomplete_tests.js
+++ b/addons/web/static/tests/core/autocomplete_tests.js
@@ -164,7 +164,7 @@ QUnit.module("Components", (hooks) => {
assert.containsOnce(target, ".o-autocomplete--dropdown-menu");
});
- QUnit.test("close dropdown on escape keydown", async (assert) => {
+ QUnit.test("cancel result on escape keydown", async (assert) => {
class Parent extends Component {}
Parent.components = { AutoComplete };
Parent.template = xml`
@@ -172,17 +172,21 @@ QUnit.module("Components", (hooks) => {
value="'Hello'"
sources="[{ options: [{ label: 'World' }, { label: 'Hello' }] }]"
onSelect="() => {}"
+ autoSelect="true"
/>
`;
await mount(Parent, target, { env });
assert.containsNone(target, ".o-autocomplete--dropdown-menu");
+ assert.strictEqual(target.querySelector(".o-autocomplete--input").value, "Hello");
await triggerEvents(target, ".o-autocomplete--input", ["focus", "click"]);
assert.containsOnce(target, ".o-autocomplete--dropdown-menu");
+ await editInput(target, ".o-autocomplete--input", "H");
await triggerEvent(target, ".o-autocomplete--input", "keydown", { key: "Escape" });
assert.containsNone(target, ".o-autocomplete--dropdown-menu");
+ assert.strictEqual(target.querySelector(".o-autocomplete--input").value, "Hello");
});
QUnit.test("select input text on first focus", async (assert) => {
@@ -198,7 +202,7 @@ QUnit.module("Components", (hooks) => {
assert.strictEqual(el.value.substring(el.selectionStart, el.selectionEnd), "Bar");
});
- QUnit.test("scroll outside should close dropdown", async (assert) => {
+ QUnit.test("scroll outside should cancel result", async (assert) => {
class Parent extends Component {}
Parent.components = { AutoComplete };
Parent.template = xml`
@@ -206,17 +210,21 @@ QUnit.module("Components", (hooks) => {
value="'Hello'"
sources="[{ options: [{ label: 'World' }, { label: 'Hello' }] }]"
onSelect="() => {}"
+ autoSelect="true"
/>
`;
await mount(Parent, target, { env });
assert.containsNone(target, ".o-autocomplete--dropdown-menu");
+ assert.strictEqual(target.querySelector(".o-autocomplete--input").value, "Hello");
await click(target, ".o-autocomplete--input");
assert.containsOnce(target, ".o-autocomplete--dropdown-menu");
+ await editInput(target, ".o-autocomplete--input", "H");
await triggerEvent(target, null, "scroll");
assert.containsNone(target, ".o-autocomplete--dropdown-menu");
+ assert.strictEqual(target.querySelector(".o-autocomplete--input").value, "Hello");
});
QUnit.test("scroll inside should keep dropdown open", async (assert) => {
@@ -240,7 +248,33 @@ QUnit.module("Components", (hooks) => {
assert.containsOnce(target, ".o-autocomplete--dropdown-menu");
});
- QUnit.test("losing focus should close dropdown", async (assert) => {
+ QUnit.test("losing focus should cancel result", async (assert) => {
+ class Parent extends Component {}
+ Parent.components = { AutoComplete };
+ Parent.template = xml`
+