[IMP] web: DomainSelector improvements
The objective of this commit is twofold: - improve the style of the DomainSelector component, in particular, the style of the control buttons "Add node", "Add branch", and "Delete node". - add the possibility to reset the domain when the current domain is not supported. The debug input is also always visible in that case if the debug mode is active. It is displayed in readonly iff the corresponding DomainSelector prop is set to true. Task ID: 3269923 closes odoo/odoo#118057 Related: odoo/enterprise#39512 Signed-off-by: Géry Debongnie <ged@odoo.com> Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com> Co-authored-by: Michaël Mattiello <mcm@odoo.com>
This commit is contained in:
co-authored by
Michaël Mattiello
parent
1c374b9b05
commit
b307e360be
@@ -51,6 +51,12 @@ export class DomainSelector extends Component {
|
||||
onWillUpdateProps((np) => this.onPropsUpdated(np));
|
||||
}
|
||||
|
||||
get className() {
|
||||
return `${this.props.readonly ? "o_read_mode" : "o_edit_mode"} ${
|
||||
this.props.className
|
||||
}`.trim();
|
||||
}
|
||||
|
||||
async onPropsUpdated(p) {
|
||||
try {
|
||||
// try to parse and execute the domain, if it fails then the domain is not supported.
|
||||
@@ -67,7 +73,7 @@ export class DomainSelector extends Component {
|
||||
this.tree.isSupported = true;
|
||||
} catch {
|
||||
this.tree.isSupported = false;
|
||||
this.tree.root = null;
|
||||
this.tree.root = this.treeBuilder.build(new Domain([]), {});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
.o_domain_selector:not(.o_domain_selector_legacy) {
|
||||
cursor: default;
|
||||
|
||||
&.o_domain_node, .o_domain_node {
|
||||
$o-domain-selector-indent: 32px;
|
||||
$o-domain-selector-panel-space: 60px;
|
||||
$o-domain-selector-row-height: 35px; // should be greater than a "normal" row so that "header" parts are the same size of rows
|
||||
$o-domain-animation-bar-height: 15px;
|
||||
|
||||
position: relative;
|
||||
|
||||
// Rows in a domain node (selector) have a minimum height and holds the control panel
|
||||
.o_domain_selector_row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: $o-domain-selector-row-height;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
> .o_domain_node_control_panel {
|
||||
@include o-position-absolute($top: 0, $bottom: 0, $right: -$o-domain-selector-panel-space);
|
||||
|
||||
> button {
|
||||
float: left;
|
||||
width: $o-domain-selector-panel-space/2;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
opacity: 0.2;
|
||||
cursor: pointer;
|
||||
|
||||
&.o_domain_delete_node_button {
|
||||
font-size: 12px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rules specific to domain parts which contain sub-domain parts
|
||||
&.o_domain_tree {
|
||||
> .o_domain_tree_header {
|
||||
position: relative;
|
||||
}
|
||||
.o_domain_tree_operator_caret::after {
|
||||
@include o-caret-down;
|
||||
}
|
||||
|
||||
& .o_domain_node_children_container {
|
||||
padding-left: $o-domain-selector-indent;
|
||||
}
|
||||
|
||||
// Rules specific to domain root
|
||||
&.o_domain_selector {
|
||||
> .o_domain_tree_header {
|
||||
min-height: 22px; // prevents "records button" to overflow 1-row domain
|
||||
}
|
||||
|
||||
> .o_domain_node_children_container {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
&.o_edit_mode > .o_domain_node_children_container {
|
||||
padding-right: $o-domain-selector-panel-space; // delete button is over the row
|
||||
}
|
||||
|
||||
> .o_domain_debug_container {
|
||||
display: block;
|
||||
margin-top: 16px;
|
||||
padding: 8px 10px 12px;
|
||||
background: var(--DomainDebugContainer-background-colomap-get, #{$o-gray-900});
|
||||
color: rgba(white, 0.5);
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
|
||||
> textarea {
|
||||
border: none;
|
||||
padding-top: 8px;
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rules specific to domain parts which do not contain sub-domain parts
|
||||
&.o_domain_leaf {
|
||||
&.o_read_mode {
|
||||
display: inline-flex;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
> .o_domain_leaf_info {
|
||||
background: $o-brand-lightsecondary;
|
||||
border: 1px solid darken($o-brand-lightsecondary, 10%);
|
||||
padding: 2px 4px;
|
||||
|
||||
> .o_field_selector {
|
||||
float: left;
|
||||
margin-right: 4px;
|
||||
|
||||
> .o_field_selector_value > .o_field_selector_chain_part {
|
||||
border: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
.o_domain_leaf_operator {
|
||||
font-style: italic;
|
||||
}
|
||||
.o_domain_leaf_value {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
> .o_domain_leaf_edition {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 4px ($o-domain-selector-panel-space/2) 4px 0;
|
||||
|
||||
> * {
|
||||
width: auto;
|
||||
background-color: white;
|
||||
|
||||
&.o_field_selector {
|
||||
flex: 0 1 40%;
|
||||
width: 40%;
|
||||
|
||||
> .o_field_selector_value > .o_field_selector_chain_part {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child { // field value
|
||||
flex: 1 1 25%;
|
||||
}
|
||||
|
||||
+ * {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
input, select, .o_datepicker, .o_datepicker_input {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Animation effects
|
||||
transition: margin-bottom .05s ease .05s;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
@include o-position-absolute($left: 0, $right: 0, $top: 100%);
|
||||
display: block;
|
||||
height: $o-domain-animation-bar-height;
|
||||
max-height: 0;
|
||||
background-image: linear-gradient(45deg, rgba($o-brand-primary, 0.4), rgba($o-brand-primary, 0.1));
|
||||
transition: max-height .05s ease .05s;
|
||||
}
|
||||
|
||||
&.o_hover_btns, &.o_hover_add_node {
|
||||
.o_domain_node_control_panel > button {
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
&.o_domain_delete_node_button {
|
||||
color: var(--danger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,40 +2,38 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="web._DomainSelector" owl="1">
|
||||
<t t-if="tree.isSupported">
|
||||
<t t-call="web._DomainSelector.root">
|
||||
<t t-set="node" t-value="tree.root" />
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div t-att-class="props.className">This domain is not supported.</div>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<t t-name="web._DomainSelector.root" owl="1">
|
||||
<div class="o_domain_node o_domain_tree o_domain_selector" aria-atomic="true" t-attf-class="{{ props.className }} {{ props.readonly ? 'o_read_mode' : 'o_edit_mode'}}" t-ref="root">
|
||||
<t t-if="node.children.length === 0">
|
||||
<span>Match <strong>all records</strong></span>
|
||||
<t t-if="!props.readonly">
|
||||
<button class="btn btn-sm btn-primary o_domain_add_first_node_button ms-1" t-on-click="() => this.insertRootLeaf(node)">
|
||||
<i class="fa fa-plus"/> Add filter
|
||||
</button>
|
||||
<div class="o_domain_node o_domain_tree o_domain_selector" aria-atomic="true" t-att-class="className" t-ref="root">
|
||||
<t t-set="node" t-value="tree.root" />
|
||||
<t t-if="tree.isSupported">
|
||||
<t t-if="node.children.length === 0">
|
||||
<span>Match <strong>all records</strong></span>
|
||||
<t t-if="!props.readonly">
|
||||
<button class="btn btn-sm btn-primary o_domain_add_first_node_button ms-1" t-on-click="() => this.insertRootLeaf(node)">
|
||||
<i class="fa fa-plus"/> Add filter
|
||||
</button>
|
||||
</t>
|
||||
</t>
|
||||
<t t-elif="node.children.length === 1">
|
||||
Match records with the following rule:
|
||||
<t t-call="web._DomainSelector.branch.children" />
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span>Match records with </span>
|
||||
<t t-call="web._DomainSelector.branch.operator" />
|
||||
<span> of the following rules:</span>
|
||||
<t t-call="web._DomainSelector.branch.children" />
|
||||
</t>
|
||||
</t>
|
||||
<t t-elif="node.children.length === 1">
|
||||
Match records with the following rule:
|
||||
<t t-call="web._DomainSelector.branch.children" />
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span>Match records with </span>
|
||||
<t t-call="web._DomainSelector.branch.operator" />
|
||||
<span> of the following rules:</span>
|
||||
<t t-call="web._DomainSelector.branch.children" />
|
||||
This domain is not supported.
|
||||
<t t-if="!props.readonly">
|
||||
<button class="btn btn-sm btn-primary o_domain_add_first_node_button ms-1" t-on-click="() => this.insertRootLeaf(node)">Reset domain</button>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="props.isDebugMode and !props.readonly">
|
||||
<t t-if="props.isDebugMode and (!tree.isSupported or !props.readonly)">
|
||||
<label class="o_domain_debug_container">
|
||||
<span class="small"># Code editor</span>
|
||||
<textarea type="text" class="o_domain_debug_input" spellcheck="false" t-att-value="props.debugValue or props.value" t-on-change="(ev) => this.onDebugValueChange(ev.target.value)" />
|
||||
<textarea type="text" class="o_domain_debug_input" t-att-readonly="props.readonly" spellcheck="false" t-att-value="props.debugValue or props.value" t-on-change="(ev) => this.onDebugValueChange(ev.target.value)" />
|
||||
</label>
|
||||
</t>
|
||||
</div>
|
||||
@@ -43,16 +41,6 @@
|
||||
|
||||
<t t-name="web._DomainSelector.controls" owl="1">
|
||||
<div class="o_domain_node_control_panel" role="toolbar" aria-label="Domain node">
|
||||
<button
|
||||
class="btn btn-link text-danger o_domain_delete_node_button"
|
||||
title="Delete node"
|
||||
aria-label="Delete node"
|
||||
t-on-click="() => this.delete(parent, node)"
|
||||
t-on-mouseenter="(ev) => this.highlightNode(ev.target, true, 'o_hover_btns')"
|
||||
t-on-mouseleave="(ev) => this.highlightNode(ev.target, false, 'o_hover_btns')"
|
||||
>
|
||||
<i class="fa fa-times"/>
|
||||
</button>
|
||||
<button
|
||||
class="btn o_domain_add_node_button"
|
||||
title="Add node"
|
||||
@@ -61,7 +49,7 @@
|
||||
t-on-mouseenter="(ev) => this.highlightNode(ev.target, true, 'o_hover_add_node')"
|
||||
t-on-mouseleave="(ev) => this.highlightNode(ev.target, false, 'o_hover_add_node')"
|
||||
>
|
||||
<i class="fa fa-plus-circle"/>
|
||||
<i class="fa fa-plus"/>
|
||||
</button>
|
||||
<button
|
||||
class="btn o_domain_add_node_button"
|
||||
@@ -71,7 +59,17 @@
|
||||
t-on-mouseenter="(ev) => this.highlightNode(ev.target, true, 'o_hover_add_node o_hover_add_inset_node')"
|
||||
t-on-mouseleave="(ev) => this.highlightNode(ev.target, false, 'o_hover_add_node o_hover_add_inset_node')"
|
||||
>
|
||||
<i class="fa fa-ellipsis-h"/>
|
||||
<i class="fa fa-sitemap"/>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-link text-danger o_domain_delete_node_button"
|
||||
title="Delete node"
|
||||
aria-label="Delete node"
|
||||
t-on-click="() => this.delete(parent, node)"
|
||||
t-on-mouseenter="(ev) => this.highlightNode(ev.target, true, 'o_hover_btns')"
|
||||
t-on-mouseleave="(ev) => this.highlightNode(ev.target, false, 'o_hover_btns')"
|
||||
>
|
||||
<i class="fa fa-trash"/>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
@@ -161,7 +159,7 @@
|
||||
<ModelFieldSelector fieldName="node.field.name" resModel="props.resModel" readonly="false" update="(field) => this.updateField(node, field)" isDebugMode="props.isDebugMode" />
|
||||
|
||||
<div>
|
||||
<select class="o_domain_leaf_operator_select o_input text-truncate pe-1" t-on-change="(ev) => this.updateLeafOperator(node, ev.target.value)">
|
||||
<select class="o_domain_leaf_operator_select o_input text-truncate pe-3" t-on-change="(ev) => this.updateLeafOperator(node, ev.target.value)">
|
||||
<t t-foreach="getOperatorsInfo(node)" t-as="operator" t-key="operator.key">
|
||||
<option t-att-value="operator.key" t-att-selected="node.operator.key === operator.key" t-esc="operator_value.label" />
|
||||
</t>
|
||||
|
||||
@@ -1,181 +1,184 @@
|
||||
|
||||
.o_domain_node {
|
||||
$o-domain-selector-indent: 32px;
|
||||
$o-domain-selector-panel-space: 60px;
|
||||
$o-domain-selector-row-height: 35px; // should be greater than a "normal" row so that "header" parts are the same size of rows
|
||||
$o-domain-animation-bar-height: 15px;
|
||||
.o_domain_selector_legacy {
|
||||
|
||||
position: relative;
|
||||
&.o_domain_node, .o_domain_node {
|
||||
$o-domain-selector-indent: 32px;
|
||||
$o-domain-selector-panel-space: 60px;
|
||||
$o-domain-selector-row-height: 35px; // should be greater than a "normal" row so that "header" parts are the same size of rows
|
||||
$o-domain-animation-bar-height: 15px;
|
||||
|
||||
// Rows in a domain node (selector) have a minimum height and holds the control panel
|
||||
.o_domain_selector_row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: $o-domain-selector-row-height;
|
||||
position: relative;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
// Rows in a domain node (selector) have a minimum height and holds the control panel
|
||||
.o_domain_selector_row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: $o-domain-selector-row-height;
|
||||
|
||||
> .o_domain_node_control_panel {
|
||||
@include o-position-absolute($top: 0, $bottom: 0, $right: -$o-domain-selector-panel-space);
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
> button {
|
||||
float: left;
|
||||
width: $o-domain-selector-panel-space/2;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
opacity: 0.2;
|
||||
cursor: pointer;
|
||||
> .o_domain_node_control_panel {
|
||||
@include o-position-absolute($top: 0, $bottom: 0, $right: -$o-domain-selector-panel-space);
|
||||
|
||||
&.o_domain_delete_node_button {
|
||||
font-size: 12px;
|
||||
padding-left: 5px;
|
||||
> button {
|
||||
float: left;
|
||||
width: $o-domain-selector-panel-space/2;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
font-size: 16px;
|
||||
opacity: 0.2;
|
||||
cursor: pointer;
|
||||
|
||||
&.o_domain_delete_node_button {
|
||||
font-size: 12px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rules specific to domain parts which contain sub-domain parts
|
||||
&.o_domain_tree {
|
||||
> .o_domain_tree_header {
|
||||
position: relative;
|
||||
}
|
||||
.o_domain_tree_operator_caret::after {
|
||||
@include o-caret-down;
|
||||
}
|
||||
|
||||
& .o_domain_node_children_container {
|
||||
padding-left: $o-domain-selector-indent;
|
||||
}
|
||||
|
||||
// Rules specific to domain root
|
||||
&.o_domain_selector {
|
||||
// Rules specific to domain parts which contain sub-domain parts
|
||||
&.o_domain_tree {
|
||||
> .o_domain_tree_header {
|
||||
min-height: 22px; // prevents "records button" to overflow 1-row domain
|
||||
position: relative;
|
||||
}
|
||||
.o_domain_tree_operator_caret::after {
|
||||
@include o-caret-down;
|
||||
}
|
||||
|
||||
> .o_domain_node_children_container {
|
||||
padding-left: 0;
|
||||
& .o_domain_node_children_container {
|
||||
padding-left: $o-domain-selector-indent;
|
||||
}
|
||||
|
||||
&.o_edit_mode > .o_domain_node_children_container {
|
||||
padding-right: $o-domain-selector-panel-space; // delete button is over the row
|
||||
}
|
||||
// Rules specific to domain root
|
||||
&.o_domain_selector {
|
||||
> .o_domain_tree_header {
|
||||
min-height: 22px; // prevents "records button" to overflow 1-row domain
|
||||
}
|
||||
|
||||
> .o_domain_debug_container {
|
||||
display: block;
|
||||
margin-top: 16px;
|
||||
padding: 8px 10px 12px;
|
||||
background: var(--DomainDebugContainer-background-colomap-get, #{$o-gray-900});
|
||||
color: rgba(white, 0.5);
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
> .o_domain_node_children_container {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
> textarea {
|
||||
border: none;
|
||||
padding-top: 8px;
|
||||
background: transparent;
|
||||
color: white;
|
||||
&.o_edit_mode > .o_domain_node_children_container {
|
||||
padding-right: $o-domain-selector-panel-space; // delete button is over the row
|
||||
}
|
||||
|
||||
> .o_domain_debug_container {
|
||||
display: block;
|
||||
margin-top: 16px;
|
||||
padding: 8px 10px 12px;
|
||||
background: var(--DomainDebugContainer-background-colomap-get, #{$o-gray-900});
|
||||
color: rgba(white, 0.5);
|
||||
font-family: monospace;
|
||||
font-weight: normal;
|
||||
|
||||
> textarea {
|
||||
border: none;
|
||||
padding-top: 8px;
|
||||
background: transparent;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rules specific to domain parts which do not contain sub-domain parts
|
||||
&.o_domain_leaf {
|
||||
&.o_read_mode {
|
||||
display: inline-flex;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
> .o_domain_leaf_info {
|
||||
background: $o-brand-lightsecondary;
|
||||
border: 1px solid darken($o-brand-lightsecondary, 10%);
|
||||
padding: 2px 4px;
|
||||
|
||||
> .o_field_selector {
|
||||
float: left;
|
||||
// Rules specific to domain parts which do not contain sub-domain parts
|
||||
&.o_domain_leaf {
|
||||
&.o_read_mode {
|
||||
display: inline-flex;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
> .o_field_selector_value > .o_field_selector_chain_part {
|
||||
border: none;
|
||||
> .o_domain_leaf_info {
|
||||
background: $o-brand-lightsecondary;
|
||||
border: 1px solid darken($o-brand-lightsecondary, 10%);
|
||||
padding: 2px 4px;
|
||||
|
||||
> .o_field_selector {
|
||||
float: left;
|
||||
margin-right: 4px;
|
||||
|
||||
> .o_field_selector_value > .o_field_selector_chain_part {
|
||||
border: none;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
.o_domain_leaf_operator {
|
||||
font-style: italic;
|
||||
}
|
||||
.o_domain_leaf_value {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
.o_domain_leaf_operator {
|
||||
font-style: italic;
|
||||
}
|
||||
.o_domain_leaf_value {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
> .o_domain_leaf_edition {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 4px ($o-domain-selector-panel-space/2) 4px 0;
|
||||
|
||||
> * {
|
||||
flex: 1 1 15%; // operator select
|
||||
width: auto;
|
||||
background-color: white;
|
||||
|
||||
&:first-child { // field selector
|
||||
flex: 0 1 55%;
|
||||
}
|
||||
|
||||
&:last-child { // field value
|
||||
flex: 1 1 25%;
|
||||
}
|
||||
|
||||
+ * {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
input, select, .o_datepicker, .o_datepicker_input {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.o_domain_leaf_value_tags {
|
||||
> .o_domain_leaf_edition {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 4px ($o-domain-selector-panel-space/2) 4px 0;
|
||||
|
||||
> * {
|
||||
flex: 0 0 auto;
|
||||
flex: 1 1 15%; // operator select
|
||||
width: auto;
|
||||
background-color: white;
|
||||
|
||||
&:first-child { // field selector
|
||||
flex: 0 1 55%;
|
||||
}
|
||||
|
||||
&:last-child { // field value
|
||||
flex: 1 1 25%;
|
||||
}
|
||||
|
||||
+ * {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
input, select, .o_datepicker, .o_datepicker_input {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
> input {
|
||||
flex: 1 1 auto;
|
||||
width: 0;
|
||||
min-width: 50px;
|
||||
}
|
||||
.o_domain_leaf_value_remove_tag_button {
|
||||
cursor: pointer;
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Animation effects
|
||||
transition: margin-bottom .05s ease .05s;
|
||||
// Animation effects
|
||||
transition: margin-bottom .05s ease .05s;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
@include o-position-absolute($left: 0, $right: 0, $top: 100%);
|
||||
display: block;
|
||||
height: $o-domain-animation-bar-height;
|
||||
max-height: 0;
|
||||
background-image: linear-gradient(45deg, rgba($o-brand-primary, 0.4), rgba($o-brand-primary, 0.1));
|
||||
transition: max-height .05s ease .05s;
|
||||
}
|
||||
&::after {
|
||||
content: "";
|
||||
@include o-position-absolute($left: 0, $right: 0, $top: 100%);
|
||||
display: block;
|
||||
height: $o-domain-animation-bar-height;
|
||||
max-height: 0;
|
||||
background-image: linear-gradient(45deg, rgba($o-brand-primary, 0.4), rgba($o-brand-primary, 0.1));
|
||||
transition: max-height .05s ease .05s;
|
||||
}
|
||||
|
||||
&.o_hover_btns, &.o_hover_add_node {
|
||||
.o_domain_node_control_panel > button {
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
&.o_hover_btns, &.o_hover_add_node {
|
||||
.o_domain_node_control_panel > button {
|
||||
opacity: 0.5;
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -673,7 +673,7 @@
|
||||
<t t-if="widget.operator === '!'">NONE</t>
|
||||
</strong>
|
||||
</t>
|
||||
<div aria-atomic="true" t-name="DomainSelector" t-attf-class="o_domain_node o_domain_tree o_domain_selector #{widget.readonly ? 'o_read_mode' : 'o_edit_mode'}">
|
||||
<div aria-atomic="true" t-name="DomainSelector" t-attf-class="o_domain_node o_domain_tree o_domain_selector o_domain_selector_legacy #{widget.readonly ? 'o_read_mode' : 'o_edit_mode'}">
|
||||
<t t-if="widget.children.length === 0">
|
||||
<span>Match <strong>all records</strong></span>
|
||||
<button t-if="!widget.readonly" class="btn btn-sm btn-primary o_domain_add_first_node_button"><i class="fa fa-plus"/> Add filter</button>
|
||||
|
||||
@@ -139,8 +139,8 @@ QUnit.module("Components", (hooks) => {
|
||||
|
||||
// There should be a "+" button to add a domain part; clicking on it
|
||||
// should add the default "['id', '=', 1]" domain
|
||||
assert.containsOnce(target, ".fa-plus-circle", "there should be a '+' button");
|
||||
await click(target, ".fa-plus-circle");
|
||||
assert.containsOnce(target, ".fa.fa-plus", "there should be a '+' button");
|
||||
await click(target, ".fa.fa-plus");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_domain_debug_input").value,
|
||||
`["&", ("bar", "=", True), ("id", "=", 1)]`,
|
||||
@@ -150,16 +150,16 @@ QUnit.module("Components", (hooks) => {
|
||||
// There should be two "..." buttons to add a domain group; clicking on
|
||||
// the first one, should add this group with defaults "['id', '=', 1]"
|
||||
// domains and the "|" operator
|
||||
assert.containsN(target, ".fa-ellipsis-h", 2, "there should be two '...' buttons");
|
||||
assert.containsN(target, ".fa.fa-sitemap", 2, "there should be two '...' buttons");
|
||||
|
||||
await click(target.querySelector(".fa-ellipsis-h"));
|
||||
await click(target.querySelector(".fa.fa-sitemap"));
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_domain_debug_input").value,
|
||||
`["&", ("bar", "=", True), "&", "|", ("id", "=", 1), ("id", "=", 1), ("id", "=", 1)]`,
|
||||
"the domain input should contain a domain with 'bar', 'id' and a subgroup"
|
||||
);
|
||||
|
||||
// There should be five "-" buttons to remove domain part; clicking on
|
||||
// There should be five buttons to remove domain part; clicking on
|
||||
// the two last ones, should leave a domain with only the "bar" and
|
||||
// "foo" fields, with the initial "&" operator
|
||||
assert.containsN(
|
||||
@@ -168,9 +168,9 @@ QUnit.module("Components", (hooks) => {
|
||||
5,
|
||||
"there should be five 'x' buttons"
|
||||
);
|
||||
let buttons = target.querySelectorAll(".o_domain_delete_node_button");
|
||||
let buttons = target.querySelectorAll(".o_domain_delete_node_button .fa.fa-trash");
|
||||
await click(buttons[buttons.length - 1]);
|
||||
buttons = target.querySelectorAll(".o_domain_delete_node_button");
|
||||
buttons = target.querySelectorAll(".o_domain_delete_node_button .fa.fa-trash");
|
||||
await click(buttons[buttons.length - 1]);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_domain_debug_input").value,
|
||||
@@ -286,21 +286,17 @@ QUnit.module("Components", (hooks) => {
|
||||
});
|
||||
|
||||
QUnit.test("editing a domain with `parent` key", async (assert) => {
|
||||
assert.expect(1);
|
||||
|
||||
// Create the domain selector and its mock environment
|
||||
await mountComponent(DomainSelector, {
|
||||
props: {
|
||||
resModel: "product",
|
||||
value: `[("name", "=", parent.foo)]`,
|
||||
readonly: false,
|
||||
isDebugMode: true,
|
||||
},
|
||||
});
|
||||
assert.strictEqual(
|
||||
target.lastElementChild.innerHTML,
|
||||
"This domain is not supported.",
|
||||
"an error message should be displayed because of the `parent` key"
|
||||
target.lastElementChild.innerText,
|
||||
`This domain is not supported. RESET DOMAIN`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -631,4 +627,58 @@ QUnit.module("Components", (hooks) => {
|
||||
await editInput(target, ".o_domain_leaf_value_input", `["b"]`);
|
||||
assert.strictEqual(comp.value, `[("state", "in", ["b"])]`);
|
||||
});
|
||||
|
||||
QUnit.test("domain not supported (mode readonly)", async (assert) => {
|
||||
await mountComponent(DomainSelector, {
|
||||
props: {
|
||||
resModel: "partner",
|
||||
value: `[`,
|
||||
readonly: true,
|
||||
isDebugMode: false,
|
||||
},
|
||||
});
|
||||
assert.containsNone(target, ".o_domain_add_first_node_button");
|
||||
assert.containsNone(target, ".o_domain_debug_input");
|
||||
});
|
||||
|
||||
QUnit.test("domain not supported (mode readonly + mode debug)", async (assert) => {
|
||||
await mountComponent(DomainSelector, {
|
||||
props: {
|
||||
resModel: "partner",
|
||||
value: `[`,
|
||||
readonly: true,
|
||||
isDebugMode: true,
|
||||
},
|
||||
});
|
||||
assert.containsNone(target, ".o_domain_add_first_node_button");
|
||||
assert.containsOnce(target, ".o_domain_debug_input");
|
||||
assert.ok(target.querySelector(".o_domain_debug_input").hasAttribute("readonly"));
|
||||
});
|
||||
|
||||
QUnit.test("domain not supported (mode edit)", async (assert) => {
|
||||
await mountComponent(DomainSelector, {
|
||||
props: {
|
||||
resModel: "partner",
|
||||
value: `[`,
|
||||
readonly: false,
|
||||
isDebugMode: false,
|
||||
},
|
||||
});
|
||||
assert.containsOnce(target, ".o_domain_add_first_node_button");
|
||||
assert.containsNone(target, ".o_domain_debug_input");
|
||||
});
|
||||
|
||||
QUnit.test("domain not supported (mode edit + mode debug)", async (assert) => {
|
||||
await mountComponent(DomainSelector, {
|
||||
props: {
|
||||
resModel: "partner",
|
||||
value: `[`,
|
||||
readonly: false,
|
||||
isDebugMode: true,
|
||||
},
|
||||
});
|
||||
assert.containsOnce(target, ".o_domain_add_first_node_button");
|
||||
assert.containsOnce(target, ".o_domain_debug_input");
|
||||
assert.notOk(target.querySelector(".o_domain_debug_input").hasAttribute("readonly"));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,7 +119,7 @@ QUnit.module("Fields", (hooks) => {
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_edit_mode").textContent,
|
||||
"This domain is not supported.",
|
||||
" This domain is not supported. Reset domain",
|
||||
"The widget should not crash the view, but gracefully admit its failure."
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user