[IMP] web: domain selector: use TagsList in TagInput

We make TagInput use the generic component TagsList. This gives a better
looking/working to the editors used for the operators "in"/"not in" for
char fields.

Part-of: odoo/odoo#126350
This commit is contained in:
Mathieu Duckerts-Antoine
2023-07-03 16:58:55 +02:00
parent 1b94eefe26
commit 2b23f73f51
4 changed files with 71 additions and 51 deletions
@@ -147,22 +147,6 @@
height: 100%;
}
}
.o_domain_leaf_value_tags {
display: flex;
> * {
flex: 0 0 auto;
}
> input {
flex: 1 1 auto;
width: 0;
min-width: 50px;
}
.o_domain_leaf_value_remove_tag_button {
cursor: pointer;
}
}
}
}
@@ -1,6 +1,6 @@
/** @odoo-module **/
import { Component, useRef } from "@odoo/owl";
import { Component } from "@odoo/owl";
import { selectOperators } from "@web/core/domain_selector/domain_selector_operators";
import {
deserializeDate,
@@ -14,6 +14,7 @@ import { toPyValue } from "@web/core/py_js/py_utils";
import { registry } from "@web/core/registry";
import { DateTimeInput } from "../datetime/datetime_input";
import { DomainValueExpr } from "./domain_selector_nodes";
import { TagsList } from "@web/core/tags_list/tags_list";
const { DateTime } = luxon;
@@ -47,32 +48,25 @@ class Select extends Component {
}
class TagInput extends Component {
static components = { TagsList };
static props = ["value", "update"];
static template = "web.DomainSelector.TagInput";
setup() {
this.inputRef = useRef("input");
get value() {
return Array.isArray(this.props.value) ? this.props.value : [this.props.value];
}
getTagValue(tag) {
return tag instanceof DomainValueExpr ? tag.expr : tag;
get tags() {
return this.value.map((val, index) => ({
text: val instanceof DomainValueExpr ? val.expr : String(val),
colorIndex: typeof val === "string" ? 0 : 2,
onDelete: () => {
this.props.update([...this.value.slice(0, index), ...this.value.slice(index + 1)]);
},
}));
}
removeTag(tagIndex) {
return this.props.update([
...this.props.value.slice(0, tagIndex),
...this.props.value.slice(tagIndex + 1),
]);
}
addTag(value) {
return this.props.update([...this.props.value, value]);
}
onBtnClick() {
const value = this.inputRef.el.value;
this.inputRef.el.value = "";
return this.addTag(value);
onChange(ev) {
const newVal = ev.currentTarget.value;
ev.currentTarget.value = "";
this.props.update([...this.value, newVal]);
}
}
@@ -331,7 +325,7 @@ const PROPERTIES_RELATIONAL = {
// ----------------------------------------------------------------------------
const JSON_FIELD = {
operators: ["equal", "not_equal", "ilike", "not_ilike","set", "not_set"],
operators: ["equal", "not_equal", "ilike", "not_ilike", "set", "not_set"],
editors: {
default: makeEditor(Input),
},
@@ -29,17 +29,9 @@
</t>
<t t-name="web.DomainSelector.TagInput" owl="1">
<div class="o_domain_leaf_value_input">
<t t-foreach="props.value" t-as="tag" t-key="tag_index">
<span class="badge rounded-pill">
<t t-esc="getTagValue(tag)" />
<i class="o_domain_leaf_value_remove_tag_button fa fa-times" role="img" aria-label="Remove tag" title="Remove tag" t-on-click="() => this.removeTag(tag_index)" />
</span>
</t>
</div>
<div class="o_domain_leaf_value_tags">
<input type="text" class="o_input" placeholder="Add new value" t-ref="input" />
<button class="btn btn-sm btn-primary fa fa-plus o_domain_leaf_value_add_tag_button" aria-label="Add tag" title="Add tag" t-on-click="onBtnClick" />
<div class="d-flex flex-wrap">
<TagsList tags="tags"/>
<input class="o_input o_domain_leaf_value_input flex-grow-1" t-on-change="onChange"/>
</div>
</t>
@@ -5,6 +5,7 @@ import {
editInput,
editSelect,
getFixture,
getNodesTextContent,
mount,
nextTick,
patchDate,
@@ -1304,4 +1305,53 @@ QUnit.module("Components", (hooks) => {
await parent.set(`[("bar","=",true)]`);
assert.strictEqual(target.querySelector(".o_domain_leaf").textContent, `Baris set`);
});
QUnit.test("Edit the value for field char and an operator in", async (assert) => {
const parent = await makeDomainSelector({
resModel: "partner",
domain: `[("foo", "in", ["a", "b", uid])]`,
update: (domain) => {
assert.step(domain);
},
});
assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_ds_value_cell .o_tag")), [
"a",
"b",
"uid",
]);
assert.deepEqual(
[...target.querySelectorAll(".o_ds_value_cell .o_tag")].map((el) => el.dataset.color),
["0", "0", "2"]
);
assert.containsOnce(target, ".o_domain_leaf_value_input");
await editInput(target, ".o_domain_leaf_value_input", "c");
assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_ds_value_cell .o_tag")), [
"a",
"b",
"uid",
"c",
]);
assert.verifySteps([`[("foo", "in", ["a", "b", uid, "c"])]`]);
await click(target.querySelectorAll(".o_tag .o_delete")[2]);
assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_ds_value_cell .o_tag")), [
"a",
"b",
"c",
]);
assert.verifySteps([`[("foo", "in", ["a", "b", "c"])]`]);
await parent.set(`[("foo", "in", "a")]`);
assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_ds_value_cell .o_tag")), [
"a",
]);
await editInput(target, ".o_domain_leaf_value_input", "b");
assert.deepEqual(getNodesTextContent(target.querySelectorAll(".o_ds_value_cell .o_tag")), [
"a",
"b",
]);
assert.verifySteps([`[("foo", "in", ["a", "b"])]`]);
});
});