diff --git a/addons/board/static/tests/add_to_dashboard_tests.js b/addons/board/static/tests/add_to_dashboard_tests.js index d0e77fa3046..df9ef9a4c06 100644 --- a/addons/board/static/tests/add_to_dashboard_tests.js +++ b/addons/board/static/tests/add_to_dashboard_tests.js @@ -23,6 +23,7 @@ import { browser } from "@web/core/browser/browser"; import { registry } from "@web/core/registry"; import testUtils from "@web/../tests/legacy/helpers/test_utils"; import { makeFakeUserService } from "@web/../tests/helpers/mock_services"; +import * as dsHelpers from "@web/../tests/core/domain_selector_tests"; const patchDate = testUtils.mock.patchDate; const favoriteMenuRegistry = registry.category("favoriteMenu"); @@ -495,7 +496,7 @@ QUnit.module("Board", (hooks) => { // Add a filter ['foo', '=', 'yop'] await toggleSearchBarMenu(target); await openAddCustomFilterDialog(target); - await editInput(target, ".o_domain_debug_input", `[("foo", "=", "yop")]`); + await editInput(target, dsHelpers.SELECTORS.debugArea, `[("foo", "=", "yop")]`); await click(target.querySelector(".modal footer button")); // Add to dashboard diff --git a/addons/mass_mailing/static/tests/mass_mailing_favourite_filter_tests.js b/addons/mass_mailing/static/tests/mass_mailing_favourite_filter_tests.js index 7d478898c6e..f268c6b02cd 100644 --- a/addons/mass_mailing/static/tests/mass_mailing_favourite_filter_tests.js +++ b/addons/mass_mailing/static/tests/mass_mailing_favourite_filter_tests.js @@ -3,6 +3,7 @@ import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import * as testUtils from "@web/../tests/helpers/utils"; import weTestUtils from "@web_editor/../tests/test_utils"; +import * as dsHelpers from "@web/../tests/core/domain_selector_tests"; let fixture; let serverData; @@ -377,7 +378,7 @@ QUnit.module('favorite filter widget', (hooks) => { "should show icon to save the filter because domain is set in the mailing"); // If domain is not set on mailing and no filter available, both drop-down and icon container are hidden - await testUtils.click(fixture.querySelector('.o_domain_delete_node_button')); + await dsHelpers.clickOnButtonDeleteNode(fixture); assert.isNotVisible(fixture.querySelector('.o_field_mailing_filter .o_input_dropdown'), "should not display drop-down because there is still no filter available to select from"); assert.isNotVisible(fixture.querySelector('.o_mass_mailing_filter_container'), @@ -400,7 +401,7 @@ QUnit.module('favorite filter widget', (hooks) => { assert.isNotVisible(fixture.querySelector('.o_mass_mailing_save_filter_container'), "should not have option to save filter if filter is selected"); - await testUtils.click(fixture.querySelector('.o_domain_add_node_button')); + await dsHelpers.clickOnButtonAddNewRule(fixture); await testUtils.nextTick(); assert.isVisible(fixture.querySelector('.o_mass_mailing_save_filter_container'), "should have option to save filter because mailing domain is changed"); diff --git a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet_extended.dark.scss b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet_extended.dark.scss index fa7682331ea..e8112f5c13f 100644 --- a/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet_extended.dark.scss +++ b/addons/spreadsheet/static/src/o_spreadsheet/o_spreadsheet_extended.dark.scss @@ -28,7 +28,7 @@ .o_input { color: #3c3c3c !important; } - .o_domain_leaf_info { + .o_read_mode .o_domain_selector_condition { background: #f6f7fa !important; border-color: #d5dae8 !important; } diff --git a/addons/web/static/src/core/domain_selector/domain_selector.js b/addons/web/static/src/core/domain_selector/domain_selector.js index f56d61e56a1..be25e47050f 100644 --- a/addons/web/static/src/core/domain_selector/domain_selector.js +++ b/addons/web/static/src/core/domain_selector/domain_selector.js @@ -15,15 +15,15 @@ import { Domain } from "@web/core/domain"; import { CheckBox } from "@web/core/checkbox/checkbox"; import { Dropdown } from "@web/core/dropdown/dropdown"; import { DropdownItem } from "@web/core/dropdown/dropdown_item"; -import { getOperatorInfo, toOperator } from "@web/core/domain_selector/domain_selector_operators"; +import { getPathEditorInfo } from "@web/core/domain_selector/domain_selector_path_editor"; import { - Editor, - PathEditor, getDefaultOperator, + getOperatorEditorInfo, +} from "@web/core/domain_selector/domain_selector_operator_editor"; +import { getDefaultValue, - getOperatorsInfo, - getEditorInfo, -} from "@web/core/domain_selector/domain_selector_fields"; + getValueEditorInfo, +} from "@web/core/domain_selector/domain_selector_value_editors"; import { ModelFieldSelector } from "@web/core/model_field_selector/model_field_selector"; import { useLoadFieldInfo } from "@web/core/model_field_selector/utils"; import { formatValue } from "@web/core/domain_tree"; @@ -88,13 +88,11 @@ function restoreVirtualOperators(tree, otherTree) { } export class DomainSelector extends Component { - static template = "web._DomainSelector"; + static template = "web.DomainSelector"; static components = { Dropdown, DropdownItem, ModelFieldSelector, - Editor, - PathEditor, CheckBox, }; static props = { @@ -230,7 +228,7 @@ export class DomainSelector extends Component { } notifyChanges() { - this.previousTree = cloneTree(this.tree); + this.previousTree = this.tree ? cloneTree(this.tree) : null; const archiveDomain = this.includeArchived ? `[("active", "in", [True, False])]` : `[]`; const domain = this.tree ? Domain.and([buildDomain(this.tree), archiveDomain]).toString() @@ -304,10 +302,28 @@ export class DomainSelector extends Component { } getDescription(node) { - const { path, value } = node; - const fieldDef = this.getFieldDef(path); - const operatorInfo = this.getOperatorInfo(node); - return leafToString(fieldDef, operatorInfo, value, this.displayNames[fieldDef?.relation]); + const fieldDef = this.getFieldDef(node.path); + return leafToString( + node, + fieldDef, + this.displayNames[fieldDef?.relation || fieldDef?.comodel] + ); + } + + getPathEditorInfo() { + const { resModel, isDebugMode } = this.props; + const defaultPath = this.defaultCondition.path; + return getPathEditorInfo({ defaultPath, isDebugMode, resModel }); + } + + getOperatorEditorInfo(node) { + const fieldDef = this.getFieldDef(node.path); + return getOperatorEditorInfo(fieldDef); + } + + getValueEditorInfo(node) { + const fieldDef = this.getFieldDef(node.path); + return getValueEditorInfo(fieldDef, node.operator); } resetDomain() { @@ -321,69 +337,27 @@ export class DomainSelector extends Component { this.notifyChanges(); } - updatePath(node, path, { fieldDef } = {}) { - if (!path) { - // don't like that - Object.assign(node, this.createNewLeaf()); - } else { - node.path = path; - node.operator = getDefaultOperator(fieldDef); - node.value = getDefaultValue(fieldDef, node.operator); - } + async updatePath(node, path) { + const { fieldDef } = await this.loadFieldInfo(this.props.resModel, path); + node.path = path; + node.negate = false; + node.operator = getDefaultOperator(fieldDef); + node.value = getDefaultValue(fieldDef, node.operator); this.notifyChanges(); } - updateLeafOperator(node, operatorKey) { - const previousOperatorInfo = getOperatorInfo(node.operator); - - const previousNode = node ? cloneTree(node) : null; - - const [operator, negate] = toOperator(operatorKey); + updateLeafOperator(node, operator, negate) { + const previousNode = cloneTree(node); + const fieldDef = this.getFieldDef(node.path); node.negate = negate; node.operator = operator; - - const editorInfo = this.getEditorInfo(node); - if (!editorInfo.isSupported(node.value) || editorInfo.shouldResetValue?.(node.value)) { - node.value = getDefaultValue(this.getFieldDef(node.path), node.operator); - } - - const operatorInfo = getOperatorInfo(operator); - if (previousOperatorInfo.valueCount !== operatorInfo.valueCount) { - switch (operatorInfo.valueCount) { - // binary operator with a variable sized array value - case "variable": { - node.value = []; - break; - } - // unary operator (set | not set) - case 0: { - node.value = false; - break; - } - // binary operator with a non array value - case 1: { - node.value = getDefaultValue(this.getFieldDef(node.path), node.operator); - break; - } - // binary operator with a fixed sized array value - default: { - const defaultValue = getDefaultValue( - this.getFieldDef(node.path), - node.operator - ); - node.value = Array(operatorInfo.valueCount).fill(defaultValue); - break; - } - } - } - - if (buildDomain(node) === buildDomain(previousNode)) { + node.value = getDefaultValue(fieldDef, operator, node.value); + if (buildDomain(previousNode) === buildDomain(node)) { // no interesting changes for parent (only possible domain formatting/rewriting) // this means that parent might not render the domain selector // but we need to udpate editors this.render(); } - this.notifyChanges(); } @@ -396,30 +370,8 @@ export class DomainSelector extends Component { return this.props.update(value, true); } - getEditorInfo(node) { - return getEditorInfo(this.getFieldDef(node.path), node.operator); - } - - getOperatorInfo(node) { - return getOperatorInfo(node.operator, node.negate); - } - - getOperatorsInfo(node) { - const fieldDef = this.getFieldDef(node.path); - const operatorsInfo = getOperatorsInfo(fieldDef); - if ( - !operatorsInfo.some((op) => op.operator === node.operator && op.negate === node.negate) - ) { - const operatorInfo = this.getOperatorInfo(node); - operatorsInfo.push(operatorInfo); - } - return operatorsInfo; - } - - highlightNode(target, toggle, classNames) { - const nodeEl = target.closest(".o_domain_node"); - for (const className of classNames.split(/\s+/i)) { - nodeEl.classList.toggle(className, toggle); - } + highlightNode(target, toggle) { + const nodeEl = target.closest(".o_domain_selector_node"); + nodeEl.classList.toggle("o_hovered_button", toggle); } } diff --git a/addons/web/static/src/core/domain_selector/domain_selector.scss b/addons/web/static/src/core/domain_selector/domain_selector.scss index db6f30642a3..f6352e78010 100644 --- a/addons/web/static/src/core/domain_selector/domain_selector.scss +++ b/addons/web/static/src/core/domain_selector/domain_selector.scss @@ -1,175 +1,15 @@ -.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; - } - } - } +.o_domain_selector { + .o_domain_selector_node { + .o_domain_selector_node_control_panel > button { + opacity: .2; } - // Rules specific to domain parts which contain sub-domain parts - &.o_domain_tree { - > .o_domain_tree_header { - position: relative; - } - .o_domain_tree_connector_caret::after { - @include o-caret-down; - } + &.o_hovered_button { + .o_domain_selector_node_control_panel > button { + opacity: .5; - & .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_model_field_selector { - float: left; - margin-right: 4px; - - > .o_model_field_selector_value > .o_model_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; - - &.o_model_field_selector { - flex: 0 1 40%; - width: 40%; - - > .o_model_field_selector_value > .o_model_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%; - } - } - } - } - - // 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); - } + opacity: 1; } } } diff --git a/addons/web/static/src/core/domain_selector/domain_selector.xml b/addons/web/static/src/core/domain_selector/domain_selector.xml index 6ba46c3b321..31f2da52791 100644 --- a/addons/web/static/src/core/domain_selector/domain_selector.xml +++ b/addons/web/static/src/core/domain_selector/domain_selector.xml @@ -1,100 +1,103 @@ - -
+ +
- -
-
- - Match all records - - - Match - - of the following rules: - +
+ +
+
+ + Match + + + + + + + + + of the following rules: + + + Match all records + +
+ + Include archived +
- - Include archived - + + +
+ New Rule +
+
- - - New Rule - - This domain is not supported. - - - +
+ This domain is not supported. + + + +
-