diff --git a/addons/base_import/static/src/import_action/import_action.js b/addons/base_import/static/src/import_action/import_action.js
index a71e6a3cd7a..ae46e037e93 100644
--- a/addons/base_import/static/src/import_action/import_action.js
+++ b/addons/base_import/static/src/import_action/import_action.js
@@ -191,8 +191,9 @@ export class ImportAction extends Component {
// Fields
//--------------------------------------------------------------------------
- onFieldChanged(column, value) {
- this.model.setColumnField(column, value);
+ onFieldChanged(column, fieldId) {
+ const selection = this.model.fields.find((f) => f.id === fieldId);
+ this.model.setColumnField(column, selection);
}
isFieldSet(column) {
diff --git a/addons/base_import/static/src/import_data_content/import_data_content.js b/addons/base_import/static/src/import_data_content/import_data_content.js
index 3e4dd5a114b..9ff34dda12c 100644
--- a/addons/base_import/static/src/import_data_content/import_data_content.js
+++ b/addons/base_import/static/src/import_data_content/import_data_content.js
@@ -46,7 +46,7 @@ export class ImportDataContent extends Component {
makeChoices(fields) {
return fields.map((field) => ({
label: field.label,
- value: field,
+ value: field.id,
}));
}
diff --git a/addons/base_import/static/src/import_data_content/import_data_content.xml b/addons/base_import/static/src/import_data_content/import_data_content.xml
index 6725a853ddc..270538945ee 100644
--- a/addons/base_import/static/src/import_data_content/import_data_content.xml
+++ b/addons/base_import/static/src/import_data_content/import_data_content.xml
@@ -60,9 +60,9 @@
diff --git a/addons/base_import/static/tests/import_action_tests.js b/addons/base_import/static/tests/import_action_tests.js
index 6f0eee642e7..4d73482348f 100644
--- a/addons/base_import/static/tests/import_action_tests.js
+++ b/addons/base_import/static/tests/import_action_tests.js
@@ -160,70 +160,70 @@ function executeFailingImport(field, isMultiline, field_path = "") {
ids: false,
messages: isMultiline
? [
- {
- field,
- field_name: serverData.models.partner.fields[field].string,
- field_path,
- message: "Invalid value",
- moreInfo,
- record: 0,
- rows: { from: 0, to: 0 },
- value: "Invalid value",
- priority: "info",
- },
- {
- field,
- field_name: serverData.models.partner.fields[field].string,
- field_path,
- message: "Duplicate value",
- moreInfo,
- record: 0,
- rows: { from: 1, to: 1 },
- priority: "error",
- },
- {
- field,
- field_name: serverData.models.partner.fields[field].string,
- field_path,
- message: "Wrong values",
- moreInfo,
- record: 0,
- rows: { from: 2, to: 3 },
- priority: "warning",
- },
- {
- field,
- field_name: serverData.models.partner.fields[field].string,
- field_path,
- message: "Bad value here",
- moreInfo,
- record: 0,
- rows: { from: 4, to: 4 },
- value: "Bad value",
- priority: "warning",
- },
- {
- field,
- field_name: serverData.models.partner.fields[field].string,
- field_path,
- message: "Duplicate value",
- moreInfo,
- record: 0,
- rows: { from: 5, to: 5 },
- priority: "error",
- },
- ]
+ {
+ field,
+ field_name: serverData.models.partner.fields[field].string,
+ field_path,
+ message: "Invalid value",
+ moreInfo,
+ record: 0,
+ rows: { from: 0, to: 0 },
+ value: "Invalid value",
+ priority: "info",
+ },
+ {
+ field,
+ field_name: serverData.models.partner.fields[field].string,
+ field_path,
+ message: "Duplicate value",
+ moreInfo,
+ record: 0,
+ rows: { from: 1, to: 1 },
+ priority: "error",
+ },
+ {
+ field,
+ field_name: serverData.models.partner.fields[field].string,
+ field_path,
+ message: "Wrong values",
+ moreInfo,
+ record: 0,
+ rows: { from: 2, to: 3 },
+ priority: "warning",
+ },
+ {
+ field,
+ field_name: serverData.models.partner.fields[field].string,
+ field_path,
+ message: "Bad value here",
+ moreInfo,
+ record: 0,
+ rows: { from: 4, to: 4 },
+ value: "Bad value",
+ priority: "warning",
+ },
+ {
+ field,
+ field_name: serverData.models.partner.fields[field].string,
+ field_path,
+ message: "Duplicate value",
+ moreInfo,
+ record: 0,
+ rows: { from: 5, to: 5 },
+ priority: "error",
+ },
+ ]
: [
- {
- field,
- field_name: serverData.models.partner.fields[field].string,
- field_path,
- message: "Incorrect value",
- moreInfo,
- record: 0,
- rows: { from: 0, to: 0 },
- },
- ],
+ {
+ field,
+ field_name: serverData.models.partner.fields[field].string,
+ field_path,
+ message: "Incorrect value",
+ moreInfo,
+ record: 0,
+ rows: { from: 0, to: 0 },
+ },
+ ],
name: ["Some invalid content", "Wrong content", "Bad content"],
nextrow: 0,
};
@@ -579,7 +579,7 @@ QUnit.module("Base Import Tests", (hooks) => {
registerFakeHTTPService();
const notificationMock = (message) => {
assert.step(message);
- return () => { };
+ return () => {};
};
registry
.category("services")
@@ -755,7 +755,6 @@ QUnit.module("Base Import Tests", (hooks) => {
"base_import.import/execute_import": (route, args) => {
if (shouldFail) {
shouldFail = false;
- // return executeFailingImport(args, args[1][0]);
return executeFailingImport(args[1][0]);
}
assert.deepEqual(
@@ -1181,57 +1180,90 @@ QUnit.module("Base Import Tests", (hooks) => {
);
});
- QUnit.test("Import view: relational fields correctly mapped on preview", async function (assert) {
- assert.expect(4);
+ QUnit.test(
+ "Import view: relational fields correctly mapped on preview",
+ async function (assert) {
+ assert.expect(4);
- registerFakeHTTPService();
- await createImportAction({
- "base_import.import/parse_preview": async (route, args) => {
- return customParsePreview(args[1], {
- fields: [
- { id: "id", name: "id", string: "External ID", fields: [], type: "id" },
- { id: "display_name", name: "display_name", string: "Display Name", fields: [], type: "id" },
- {
- id: "many2many_field", name: "many2many_field", string: "Many2Many", fields: [
- { id: "id", name: "id", string: "External ID", fields: [], type: "id" },
- ], type: "id"
+ registerFakeHTTPService();
+ await createImportAction({
+ "base_import.import/parse_preview": async (route, args) => {
+ return customParsePreview(args[1], {
+ fields: [
+ { id: "id", name: "id", string: "External ID", fields: [], type: "id" },
+ {
+ id: "display_name",
+ name: "display_name",
+ string: "Display Name",
+ fields: [],
+ type: "id",
+ },
+ {
+ id: "many2many_field",
+ name: "many2many_field",
+ string: "Many2Many",
+ fields: [
+ {
+ id: "id",
+ name: "id",
+ string: "External ID",
+ fields: [],
+ type: "id",
+ },
+ ],
+ type: "id",
+ },
+ ],
+ headers: ["id", "display_name", "many2many_field/id"],
+ rowCount: 5,
+ matches: {
+ 0: ["id"],
+ 1: ["display_name"],
+ 2: ["many2many_field", "id"],
},
- ],
- headers: ["id", "display_name", "many2many_field/id"],
- rowCount: 5,
- matches: {
- 0: ["id"],
- 1: ["display_name"],
- 2: ["many2many_field", "id"]
- },
- preview: [
- ["0", "1", "2"],
- ["Name 1", "Name 2", "Name 3",],
- ["", "1", "2"],
- ],
- });
- },
- "base_import.import/execute_import": (route, args) => {
- assert.deepEqual(args[1], ["id", "display_name", "many2many_field/id"],
- "The proper arguments are given for the import");
- assert.deepEqual(args[2], ["id", "display_name", "many2many_field/id"],
- "The proper arguments are given for the import");
- return executeImport(args);
- },
- });
+ preview: [
+ ["0", "1", "2"],
+ ["Name 1", "Name 2", "Name 3"],
+ ["", "1", "2"],
+ ],
+ });
+ },
+ "base_import.import/execute_import": (route, args) => {
+ assert.deepEqual(
+ args[1],
+ ["id", "display_name", "many2many_field/id"],
+ "The proper arguments are given for the import"
+ );
+ assert.deepEqual(
+ args[2],
+ ["id", "display_name", "many2many_field/id"],
+ "The proper arguments are given for the import"
+ );
+ return executeImport(args);
+ },
+ });
- // Set and trigger the change of a file for the input
- const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" });
- await editInput(target, "input[type='file']", file);
+ // Set and trigger the change of a file for the input
+ const file = new File(["fake_file"], "fake_file.xls", { type: "text/plain" });
+ await editInput(target, "input[type='file']", file);
- assert.strictEqual(target.querySelector("tr:nth-child(3) .o_import_file_column_cell span.text-truncate").innerText, "many2many_field/id",
- "The third row should be the relational field");
+ assert.strictEqual(
+ target.querySelector(
+ "tr:nth-child(3) .o_import_file_column_cell span.text-truncate"
+ ).innerText,
+ "many2many_field/id",
+ "The third row should be the relational field"
+ );
- assert.strictEqual(target.querySelector("tr:nth-child(3) .o_select_menu_toggler_slot span").innerText, "Many2Many / External ID",
- "The relational field should be selected by default and the name should be the full path.");
+ assert.strictEqual(
+ target.querySelector("tr:nth-child(3) .o_select_menu_toggler_slot span").innerText,
+ "Many2Many / External ID",
+ "The relational field should be selected by default and the name should be the full path."
+ );
- await click(target.querySelector(".o_control_panel button:first-child"));
- });
+ await click(target.querySelector(".o_control_panel button:first-child"));
+ }
+ );
QUnit.test("Import view: import errors with relational fields", async function (assert) {
registerFakeHTTPService();
@@ -1244,11 +1276,27 @@ QUnit.module("Base Import Tests", (hooks) => {
return customParsePreview(args[1], {
fields: [
{ id: "id", name: "id", string: "External ID", fields: [], type: "id" },
- { id: "display_name", name: "display_name", string: "Display Name", fields: [], type: "id" },
{
- id: "many2many_field", name: "many2many_field", string: "Many2Many", fields: [
- { id: "id", name: "id", string: "External ID", fields: [], type: "id" },
- ], type: "id"
+ id: "display_name",
+ name: "display_name",
+ string: "Display Name",
+ fields: [],
+ type: "id",
+ },
+ {
+ id: "many2many_field",
+ name: "many2many_field",
+ string: "Many2Many",
+ fields: [
+ {
+ id: "id",
+ name: "id",
+ string: "External ID",
+ fields: [],
+ type: "id",
+ },
+ ],
+ type: "id",
},
],
headers: ["id", "display_name", "many2many_field/id"],
@@ -1256,11 +1304,11 @@ QUnit.module("Base Import Tests", (hooks) => {
matches: {
0: ["id"],
1: ["display_name"],
- 2: ["many2many_field", "id"]
+ 2: ["many2many_field", "id"],
},
preview: [
["0", "1", "2"],
- ["Name 1", "Name 2", "Name 3",],
+ ["Name 1", "Name 2", "Name 3"],
["", "1", "2"],
],
});
@@ -1283,17 +1331,30 @@ QUnit.module("Base Import Tests", (hooks) => {
"A message is shown if the import was blocked"
);
- assert.strictEqual(target.querySelector("tr:nth-child(3) .o_import_file_column_cell span.text-truncate").innerText, "many2many_field/id",
- "The third row should be the relational field");
+ assert.strictEqual(
+ target.querySelector("tr:nth-child(3) .o_import_file_column_cell span.text-truncate")
+ .innerText,
+ "many2many_field/id",
+ "The third row should be the relational field"
+ );
- assert.strictEqual(target.querySelector("tr:nth-child(3) .o_select_menu_toggler_slot span").innerText, "Many2Many / External ID",
- "The relational field is properly mapped");
+ assert.strictEqual(
+ target.querySelector("tr:nth-child(3) .o_select_menu_toggler_slot span").innerText,
+ "Many2Many / External ID",
+ "The relational field is properly mapped"
+ );
- assert.containsOnce(target, "tr:nth-child(3) .o_import_report.alert",
- "The relational field should have error messages on his row");
+ assert.containsOnce(
+ target,
+ "tr:nth-child(3) .o_import_report.alert",
+ "The relational field should have error messages on his row"
+ );
- assert.strictEqual(target.querySelector("tr:nth-child(3) .o_import_report.alert p b").innerText, "Many2Many / External ID",
- "The error should contain the full path of the relational field");
+ assert.strictEqual(
+ target.querySelector("tr:nth-child(3) .o_import_report.alert p b").innerText,
+ "Many2Many / External ID",
+ "The error should contain the full path of the relational field"
+ );
});
QUnit.test("Import view: date format should be converted to strftime", async function (assert) {
@@ -1340,4 +1401,31 @@ QUnit.module("Base Import Tests", (hooks) => {
await click(target.querySelector(".o_control_panel button:first-child"));
assert.verifySteps(["execute_import"]);
});
+
+ QUnit.test("Import action: field selection has a clear button", async function (assert) {
+ registerFakeHTTPService();
+ patchWithCleanup(browser, {
+ setTimeout: (fn) => fn(),
+ });
+
+ await createImportAction();
+
+ // Set and trigger the change of a file for the input
+ const file = new File(["fake_file"], "fake_file.xlsx", { type: "text/plain" });
+ await editInput(target, "input[type='file']", file);
+ await editSelectMenu(target, ".o_import_data_content .o_select_menu", "Bar");
+ assert.containsN(
+ target,
+ ".o_select_menu_toggler_clear",
+ 2,
+ "clear button is present for each field to unselect it"
+ );
+
+ await click(target.querySelector(".o_select_menu_toggler_clear"));
+ assert.strictEqual(
+ target.querySelector("tr:nth-child(2) .o_select_menu").textContent,
+ "To import, select a field...",
+ "field has been unselected"
+ );
+ });
});
diff --git a/addons/web/static/src/core/select_menu/select_menu.js b/addons/web/static/src/core/select_menu/select_menu.js
index 6c2b4edc987..82924d1b68e 100644
--- a/addons/web/static/src/core/select_menu/select_menu.js
+++ b/addons/web/static/src/core/select_menu/select_menu.js
@@ -4,7 +4,6 @@ import { Component, useState, useRef, onWillUpdateProps } from "@odoo/owl";
import { Dropdown } from "@web/core/dropdown/dropdown";
import { DropdownItem } from "@web/core/dropdown/dropdown_item";
import { _lt } from "@web/core/l10n/translation";
-import { shallowEqual } from "@web/core/utils/objects";
import { useDebounced } from "@web/core/utils/timing";
import { scrollTo } from "@web/core/utils/scrolling";
import { fuzzyLookup } from "@web/core/utils/search";
@@ -172,12 +171,7 @@ export class SelectMenu extends Component {
getSelectedChoice(props) {
if (props.value) {
const choices = [...props.choices, ...props.groups.flatMap((g) => g.choices)];
- return choices.find((c) => {
- if (typeof c.value === "object" && typeof props.value === "object") {
- return shallowEqual(c.value, props.value);
- }
- return c.value === props.value;
- });
+ return choices.find((c) => c.value === props.value);
} else {
return undefined;
}
diff --git a/addons/web/static/tests/core/select_menu_tests.js b/addons/web/static/tests/core/select_menu_tests.js
index 07383ef4348..a0559dbee06 100644
--- a/addons/web/static/tests/core/select_menu_tests.js
+++ b/addons/web/static/tests/core/select_menu_tests.js
@@ -175,58 +175,6 @@ QUnit.module("Web Components", (hooks) => {
assert.equal(document.activeElement, target.querySelector(".o_select_menu_input input"));
});
- QUnit.test("value props accept array", async (assert) => {
- class Parent extends Component {
- setup() {
- this.choices = [
- { label: "Z", value: [1, 2] },
- { label: "A", value: [1, 3] },
- ];
- this.value = [1, 2];
- }
- }
- Parent.components = { SelectMenu };
- Parent.template = xml`
-
- `;
-
- await mount(Parent, target, { env });
- assert.equal(
- target.querySelector(".o_select_menu_toggler_slot").innerText,
- "Z",
- `The select value shoud be "Z"`
- );
- });
-
- QUnit.test("value props accept object", async (assert) => {
- class Parent extends Component {
- setup() {
- this.choices = [
- { label: "Z", value: { hello: "world" } },
- { label: "A", value: { paper: "company" } },
- ];
- this.value = { paper: "company" };
- }
- }
- Parent.components = { SelectMenu };
- Parent.template = xml`
-
- `;
-
- await mount(Parent, target, { env });
- assert.equal(
- target.querySelector(".o_select_menu_toggler_slot").innerText,
- "A",
- `The select value shoud be "A"`
- );
- });
-
QUnit.test(
"Value with no corresponding choices displays as if no choice was selected",
async (assert) => {
@@ -266,10 +214,10 @@ QUnit.module("Web Components", (hooks) => {
`;
setup() {
this.choices = [
- { label: "Z", value: { hello: "world" } },
- { label: "A", value: { paper: "company" } },
+ { label: "Z", value: "world" },
+ { label: "A", value: "company" },
];
- this.state = useState({ value: { paper: "company" } });
+ this.state = useState({ value: "company" });
}
setValue(newValue) {
this.state.value = newValue;
@@ -279,7 +227,7 @@ QUnit.module("Web Components", (hooks) => {
const comp = await mount(Parent, target, { env });
assert.equal(getValue(), "A", `The select value shoud be "A"`);
- comp.setValue({ hello: "world" });
+ comp.setValue("world");
await nextTick();
assert.equal(
getValue(),
|