[FIX] web: SettingsFormView - avoid searching hidden settings
When searching a text of a hidden field (for example, "gate"), the setting itself will not be shown, but the group title, and the app Search Header will be shown. This issue arise because, we use an if condition with all the label of all the fields (hidden or not) in the group (or app) to decide if the group title or the app header will be showed. Now, we modify this to hide (d-none) the group title or the app header if there is not a settings below them. Part-of: odoo/odoo#99205
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { escapeRegExp } from "@web/core/utils/strings";
|
||||
|
||||
const { Component, useState, useChildSubEnv } = owl;
|
||||
|
||||
export class Setting extends Component {
|
||||
setup() {
|
||||
this.state = useState({
|
||||
search: this.env.searchState,
|
||||
showAllContainer: this.env.showAllContainer,
|
||||
});
|
||||
// Don't search on a header setting
|
||||
if (this.props.type === "header") {
|
||||
useChildSubEnv({ searchState: { value: "" } });
|
||||
}
|
||||
this.labels = this.props.labels || [];
|
||||
}
|
||||
visible() {
|
||||
if (!this.state.search.value) {
|
||||
return true;
|
||||
}
|
||||
// Always shown a header setting
|
||||
if (this.props.type === "header") {
|
||||
return true;
|
||||
}
|
||||
if (this.state.showAllContainer.showAllContainer) {
|
||||
return true;
|
||||
}
|
||||
const regexp = new RegExp(escapeRegExp(this.state.search.value), "i");
|
||||
if (regexp.test(this.labels.join())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
get classNames() {
|
||||
const { class: _class, type } = this.props;
|
||||
const classNames = {
|
||||
o_setting_box: true,
|
||||
o_searchable_setting: this.labels.length && type !== "header",
|
||||
[_class]: Boolean(_class),
|
||||
};
|
||||
|
||||
return classNames;
|
||||
}
|
||||
}
|
||||
Setting.template = "web.Setting";
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="web.Setting" owl="1">
|
||||
<div t-att-class="classNames" t-att-title="props.title" t-if="visible()">
|
||||
<t t-slot="default"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -1,12 +1,29 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
const { Component, useState } = owl;
|
||||
const { Component, useState, useEffect, useRef } = owl;
|
||||
|
||||
export class SettingsApp extends Component {
|
||||
setup() {
|
||||
this.state = useState({
|
||||
search: this.env.searchState,
|
||||
});
|
||||
this.settingsAppRef = useRef("settingsApp");
|
||||
useEffect(
|
||||
() => {
|
||||
if (this.settingsAppRef.el) {
|
||||
const force =
|
||||
this.state.search.value &&
|
||||
!this.settingsAppRef.el.querySelector(
|
||||
".o_settings_container:not(.d-none)"
|
||||
) &&
|
||||
!this.settingsAppRef.el.querySelector(
|
||||
".o_setting_box.o_searchable_setting"
|
||||
);
|
||||
this.settingsAppRef.el.classList.toggle("d-none", force);
|
||||
}
|
||||
},
|
||||
() => [this.state.search.value]
|
||||
);
|
||||
}
|
||||
}
|
||||
SettingsApp.template = "web.SettingsApp";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="web.SettingsApp" owl="1">
|
||||
<div class="app_settings_block" t-if="props.selectedTab === props.key or state.search.value.length !== 0" t-att-string="props.string" t-att-data-key="props.key">
|
||||
<div class="app_settings_block" t-if="props.selectedTab === props.key or state.search.value.length !== 0" t-att-string="props.string" t-att-data-key="props.key" t-ref="settingsApp">
|
||||
<div class="settingSearchHeader h4" t-if="state.search.value.length !== 0" role="search">
|
||||
<img class="icon" t-att-src="props.imgurl" alt="Search"></img>
|
||||
<span class="appName"><t t-esc="props.string"/></span>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { HighlightText } from "./../highlight_text/highlight_text";
|
||||
import { escapeRegExp } from "@web/core/utils/strings";
|
||||
|
||||
const { Component, useState, useRef, useEffect, onWillRender, useChildSubEnv } = owl;
|
||||
|
||||
export class SettingsContainer extends Component {
|
||||
setup() {
|
||||
this.state = useState({
|
||||
search: this.env.searchState,
|
||||
});
|
||||
this.showAllContainerState = useState({
|
||||
showAllContainer: false,
|
||||
});
|
||||
useChildSubEnv({
|
||||
showAllContainer: this.showAllContainerState,
|
||||
});
|
||||
this.settingsContainerRef = useRef("settingsContainer");
|
||||
this.settingsContainerTitleRef = useRef("settingsContainerTitle");
|
||||
this.settingsContainerTipRef = useRef("settingsContainerTip");
|
||||
useEffect(
|
||||
() => {
|
||||
const regexp = new RegExp(escapeRegExp(this.state.search.value), "i");
|
||||
const force =
|
||||
this.state.search.value &&
|
||||
!regexp.test([this.props.title, this.props.tip].join()) &&
|
||||
!this.settingsContainerRef.el.querySelector(
|
||||
".o_setting_box.o_searchable_setting"
|
||||
);
|
||||
this.toggleContainer(force);
|
||||
},
|
||||
() => [this.state.search.value]
|
||||
);
|
||||
onWillRender(() => {
|
||||
const regexp = new RegExp(escapeRegExp(this.state.search.value), "i");
|
||||
if (regexp.test([this.props.title, this.props.tip].join())) {
|
||||
this.showAllContainerState.showAllContainer = true;
|
||||
} else {
|
||||
this.showAllContainerState.showAllContainer = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
toggleContainer(force) {
|
||||
if (this.settingsContainerTitleRef.el) {
|
||||
this.settingsContainerTitleRef.el.classList.toggle("d-none", force);
|
||||
}
|
||||
if (this.settingsContainerTipRef.el) {
|
||||
this.settingsContainerTipRef.el.classList.toggle("d-none", force);
|
||||
}
|
||||
this.settingsContainerRef.el.classList.toggle("d-none", force);
|
||||
}
|
||||
}
|
||||
SettingsContainer.template = "web.SettingsContainer";
|
||||
SettingsContainer.components = {
|
||||
HighlightText,
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="web.SettingsContainer" owl="1">
|
||||
<h2 t-if="props.title" t-ref="settingsContainerTitle"><HighlightText originalText="props.title"/></h2>
|
||||
<h3 t-if="props.tip" class="o_setting_tip text-muted" t-ref="settingsContainerTip"><HighlightText originalText="props.tip"/></h3>
|
||||
<div t-att-class="props.class" t-ref="settingsContainer">
|
||||
<t t-slot="default"/>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -5,10 +5,10 @@ import { FormCompiler } from "@web/views/form/form_compiler";
|
||||
import { getModifier } from "@web/views/view_compiler";
|
||||
|
||||
function compileSettingsPage(el, params) {
|
||||
const settings = createElement("SettingsPage");
|
||||
settings.setAttribute("slots", "props.slots");
|
||||
settings.setAttribute("initialTab", "props.initialApp");
|
||||
settings.setAttribute("t-slot-scope", "settings");
|
||||
const settingsPage = createElement("SettingsPage");
|
||||
settingsPage.setAttribute("slots", "props.slots");
|
||||
settingsPage.setAttribute("initialTab", "props.initialApp");
|
||||
settingsPage.setAttribute("t-slot-scope", "settings");
|
||||
|
||||
//props
|
||||
const modules = [];
|
||||
@@ -21,16 +21,15 @@ function compileSettingsPage(el, params) {
|
||||
imgurl: getAppIconUrl(child.getAttribute("data-key")),
|
||||
isVisible: getModifier(child, "invisible"),
|
||||
};
|
||||
params.config = {};
|
||||
if (!child.classList.value.includes("o_not_app")) {
|
||||
modules.push(params.module);
|
||||
append(settings, this.compileNode(child, params));
|
||||
append(settingsPage, this.compileNode(child, params));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
settings.setAttribute("modules", JSON.stringify(modules));
|
||||
return settings;
|
||||
settingsPage.setAttribute("modules", JSON.stringify(modules));
|
||||
return settingsPage;
|
||||
}
|
||||
|
||||
function getAppIconUrl(module) {
|
||||
@@ -40,136 +39,81 @@ function getAppIconUrl(module) {
|
||||
}
|
||||
|
||||
function compileSettingsApp(el, params) {
|
||||
const settingsBlock = createElement("SettingsApp");
|
||||
settingsBlock.setAttribute("t-props", JSON.stringify(params.module));
|
||||
settingsBlock.setAttribute("selectedTab", "settings.selectedTab");
|
||||
|
||||
params.config.app = el.getAttribute("data-key");
|
||||
params.config.groupTitleId = undefined;
|
||||
params.config.groupTitle = "";
|
||||
params.config.groupTipId = undefined;
|
||||
params.config.groupTip = "";
|
||||
params.config.container = undefined;
|
||||
params.config.settingBox = undefined;
|
||||
const settingsApp = createElement("SettingsApp");
|
||||
settingsApp.setAttribute("t-props", JSON.stringify(params.module));
|
||||
settingsApp.setAttribute("selectedTab", "settings.selectedTab");
|
||||
|
||||
for (const child of el.children) {
|
||||
append(settingsBlock, this.compileNode(child, params));
|
||||
append(settingsApp, this.compileNode(child, params));
|
||||
}
|
||||
|
||||
settingsBlock.setAttribute(
|
||||
"t-if",
|
||||
`!searchState.value or search("app", "${el.getAttribute("data-key")}")`
|
||||
);
|
||||
|
||||
return settingsBlock;
|
||||
return settingsApp;
|
||||
}
|
||||
|
||||
function compileSettingsHeader(el, params) {
|
||||
const header = el.cloneNode();
|
||||
for (const child of el.children) {
|
||||
append(header, this.compileNode(child, { ...params, config: null }));
|
||||
append(header, this.compileNode(child, { ...params, settingType: "header" }));
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
let groupTitleId = 0;
|
||||
let settingsContainer = null;
|
||||
|
||||
function compileSettingsGroupTitle(el, params) {
|
||||
const res = this.compileGenericNode(el, params);
|
||||
const groupTitle = res.textContent;
|
||||
|
||||
//HighlightText
|
||||
const highlight = createElement("HighlightText");
|
||||
highlight.setAttribute("originalText", `\`${groupTitle}\``);
|
||||
append(res, highlight);
|
||||
res.firstChild.remove();
|
||||
|
||||
if (params.config) {
|
||||
params.config.groupTitleId = ++groupTitleId;
|
||||
params.config.groupTitle = groupTitle;
|
||||
params.config.groupTipId = undefined;
|
||||
params.config.groupTip = undefined;
|
||||
params.config.container = undefined;
|
||||
params.config.settingBox = undefined;
|
||||
params.labels.push({
|
||||
label: groupTitle.trim(),
|
||||
...params.config,
|
||||
});
|
||||
res.setAttribute("t-if", `!searchState.value or search("groupTitleId", ${groupTitleId})`);
|
||||
if (!settingsContainer) {
|
||||
settingsContainer = createElement("SettingsContainer");
|
||||
}
|
||||
|
||||
return res;
|
||||
settingsContainer.setAttribute("title", `\`${el.textContent}\``);
|
||||
}
|
||||
|
||||
let groupTipId = 0;
|
||||
|
||||
function compileSettingsGroupTip(el, params) {
|
||||
const res = this.compileGenericNode(el, params);
|
||||
const tip = res.textContent;
|
||||
|
||||
//HighlightText
|
||||
const highlight = createElement("HighlightText");
|
||||
highlight.setAttribute("originalText", `\`${tip}\``);
|
||||
append(res, highlight);
|
||||
res.firstChild.remove();
|
||||
|
||||
if (params.config) {
|
||||
params.config.groupTipId = ++groupTipId;
|
||||
params.config.groupTip = tip;
|
||||
params.config.container = undefined;
|
||||
params.config.settingBox = undefined;
|
||||
params.labels.push({
|
||||
label: tip.trim(),
|
||||
...params.config,
|
||||
});
|
||||
res.setAttribute("t-if", `!searchState.value or search("groupTipId", ${groupTipId})`);
|
||||
if (!settingsContainer) {
|
||||
settingsContainer = createElement("SettingsContainer");
|
||||
}
|
||||
|
||||
return res;
|
||||
settingsContainer.setAttribute("tip", `\`${el.textContent}\``);
|
||||
}
|
||||
|
||||
let containerId = 0;
|
||||
|
||||
function compileSettingsContainer(el, params) {
|
||||
if (params.config) {
|
||||
params.config.container = ++containerId;
|
||||
params.config.settingBox = undefined;
|
||||
params.containerLabels = [];
|
||||
if (!settingsContainer) {
|
||||
settingsContainer = createElement("SettingsContainer");
|
||||
}
|
||||
const res = this.compileGenericNode(el, params);
|
||||
if (params.config) {
|
||||
res.setAttribute("t-if", `!searchState.value or search("container", ${containerId})`);
|
||||
|
||||
for (const child of el.children) {
|
||||
append(settingsContainer, this.compileNode(child, params));
|
||||
}
|
||||
const res = settingsContainer;
|
||||
settingsContainer = null;
|
||||
return res;
|
||||
}
|
||||
|
||||
let settingBoxId = 0;
|
||||
|
||||
function compileSettingBox(el, params) {
|
||||
if (params.config) {
|
||||
settingBoxId++;
|
||||
params.config.settingBox = settingBoxId;
|
||||
const setting = createElement("Setting");
|
||||
params.labels = [];
|
||||
|
||||
if (params.settingType) {
|
||||
setting.setAttribute("type", `\`${params.settingType}\``);
|
||||
}
|
||||
const res = this.compileGenericNode(el, params);
|
||||
if (params.config) {
|
||||
res.setAttribute("t-if", `!searchState.value or search("settingBox", ${settingBoxId})`);
|
||||
if (el.getAttribute("title")) {
|
||||
setting.setAttribute("title", `\`${el.getAttribute("title")}\``);
|
||||
}
|
||||
return res;
|
||||
for (const child of el.children) {
|
||||
append(setting, this.compileNode(child, params));
|
||||
}
|
||||
setting.setAttribute("labels", JSON.stringify(params.labels));
|
||||
return setting;
|
||||
}
|
||||
|
||||
function compileField(el, params) {
|
||||
const res = this.compileField(el, params);
|
||||
if (params.config) {
|
||||
let widgetName;
|
||||
if (el.hasAttribute("widget")) {
|
||||
widgetName = el.getAttribute("widget");
|
||||
const label = params.getFieldExpr(el.getAttribute("name"), widgetName);
|
||||
if (label) {
|
||||
params.labels.push({
|
||||
label,
|
||||
...params.config,
|
||||
});
|
||||
}
|
||||
let widgetName;
|
||||
if (el.hasAttribute("widget")) {
|
||||
widgetName = el.getAttribute("widget");
|
||||
const label = params.getFieldExpr(el.getAttribute("name"), widgetName);
|
||||
if (label) {
|
||||
params.labels.push(label);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -181,11 +125,8 @@ function compileLabel(el, params) {
|
||||
// It the node is a FormLabel component node, the label is
|
||||
// localized *after* the field.
|
||||
// We don't know yet if the label refers to a field or not.
|
||||
if (res.textContent && res.tagName !== "FormLabel" && params.config) {
|
||||
params.labels.push({
|
||||
label: res.textContent.trim(),
|
||||
...params.config,
|
||||
});
|
||||
if (res.textContent && res.tagName !== "FormLabel") {
|
||||
params.labels.push(res.textContent.trim());
|
||||
//HighlightText
|
||||
const highlight = createElement("HighlightText");
|
||||
highlight.setAttribute("originalText", `\`${res.textContent}\``);
|
||||
@@ -198,11 +139,8 @@ function compileLabel(el, params) {
|
||||
|
||||
function compileGenericLabel(el, params) {
|
||||
const res = this.compileGenericNode(el, params);
|
||||
if (res.textContent && params.config) {
|
||||
params.labels.push({
|
||||
label: res.textContent.trim(),
|
||||
...params.config,
|
||||
});
|
||||
if (res.textContent) {
|
||||
params.labels.push(res.textContent.trim());
|
||||
//HighlightText
|
||||
const highlight = createElement("HighlightText");
|
||||
highlight.setAttribute("originalText", `\`${res.textContent}\``);
|
||||
@@ -254,10 +192,7 @@ export class SettingsFormCompiler extends FormCompiler {
|
||||
let labelText = label.textContent || fieldString;
|
||||
labelText = labelText ? labelText : params.record.fields[fieldName].string;
|
||||
|
||||
params.labels.push({
|
||||
label: labelText,
|
||||
...params.config,
|
||||
});
|
||||
params.labels.push(labelText);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,12 @@ export class SettingsFormController extends formView.Controller {
|
||||
useSubEnv({ searchState: this.searchState });
|
||||
useEffect(
|
||||
() => {
|
||||
if (this.rootRef.el.querySelector(".settings .o_setting_box")) {
|
||||
if (
|
||||
this.rootRef.el.querySelector(".o_settings_container:not(.d-none)") ||
|
||||
this.rootRef.el.querySelector(
|
||||
".settings .o_settings_container:not(.d-none) .o_setting_box.o_searchable_setting"
|
||||
)
|
||||
) {
|
||||
this.state.displayNoContent = false;
|
||||
} else {
|
||||
this.state.displayNoContent = true;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { escapeRegExp } from "@web/core/utils/strings";
|
||||
import { FormRenderer } from "@web/views/form/form_renderer";
|
||||
import { FormLabelHighlightText } from "./highlight_text/form_label_highlight_text";
|
||||
import { HighlightText } from "./highlight_text/highlight_text";
|
||||
import { Setting } from "./settings/setting";
|
||||
import { SettingsContainer } from "./settings/settings_container";
|
||||
import { SettingsApp } from "./settings/settings_app";
|
||||
import { SettingsPage } from "./settings/settings_page";
|
||||
|
||||
@@ -27,17 +28,7 @@ export class SettingsFormRenderer extends FormRenderer {
|
||||
super.setup();
|
||||
this.searchState = useState(this.env.searchState);
|
||||
}
|
||||
search(kind, value) {
|
||||
const regexp = new RegExp(escapeRegExp(this.searchState.value), "i");
|
||||
for (const x of labels[this.props.archInfo.arch]) {
|
||||
if (x[kind] === value) {
|
||||
if (regexp.test([x.label, x.groupTitle, x.groupTip].join())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getFieldExpr(fieldName, fieldWidget) {
|
||||
const name = `base_settings.${fieldWidget}`;
|
||||
let fieldClass;
|
||||
@@ -53,6 +44,8 @@ export class SettingsFormRenderer extends FormRenderer {
|
||||
}
|
||||
SettingsFormRenderer.components = {
|
||||
...FormRenderer.components,
|
||||
Setting,
|
||||
SettingsContainer,
|
||||
SettingsPage,
|
||||
SettingsApp,
|
||||
HighlightText,
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { SettingsContainer } from "../settings/settings_container";
|
||||
import { Setting } from "../settings/setting";
|
||||
|
||||
const { Component, onWillStart } = owl;
|
||||
|
||||
@@ -33,5 +35,9 @@ class ResConfigDevTool extends Component {
|
||||
}
|
||||
|
||||
ResConfigDevTool.template = "res_config_dev_tool";
|
||||
ResConfigDevTool.components = {
|
||||
SettingsContainer,
|
||||
Setting,
|
||||
};
|
||||
|
||||
registry.category("view_widgets").add("res_config_dev_tool", ResConfigDevTool);
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
<template>
|
||||
<div t-name='res_config_dev_tool' owl="1">
|
||||
<div id="developer_tool">
|
||||
<h2>Developer Tools</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box" id="devel_tool">
|
||||
<SettingsContainer title="'Developer Tools'" class="'row mt16 o_settings_container'">
|
||||
<Setting class="'col-12 col-lg-6 o_setting_box'" id="devel_tool">
|
||||
<div class="o_setting_right_pane">
|
||||
<a t-if="!isDebug" class="d-block" href="?debug=1">Activate the developer mode</a>
|
||||
<a t-if="!isAssets" class="d-block" href="?debug=assets">Activate the developer mode (with assets)</a>
|
||||
@@ -12,8 +11,8 @@
|
||||
<a t-if="isDebug" class="d-block" href="?debug=">Deactivate the developer mode</a>
|
||||
<a t-if="isDebug and !isDemoDataActive" t-on-click.prevent="onClickForceDemo" class="o_web_settings_force_demo" href="#">Load demo data</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Setting>
|
||||
</SettingsContainer>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -71,6 +71,18 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
<div class="o_setting_container">
|
||||
<div class="settings">
|
||||
<div class="app_settings_block" string="CRM" data-key="crm">
|
||||
<div class="app_settings_header pt-1 pb-1" style="background-color: #FEF0D0;">
|
||||
<div class="col-xs-12 col-md-6 ms-0 o_setting_box">
|
||||
<div class="o_setting_right_pane border-start-0 ms-0 ps-0">
|
||||
<div class="content-group">
|
||||
<div class="row flex-row flex-nowrap mt8 align-items-center">
|
||||
<label class="col text-nowrap ml8 flex-nowrap" string="Foo" for="foo_config_id"/>
|
||||
<field name="foo" title="Foo?."/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Title of group Bar</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
@@ -104,6 +116,18 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 attrs="{'invisible': [('bar','=',False)]}">Hide group Foo</h2>
|
||||
<div class="row mt16 o_settings_container" attrs="{'invisible': [('bar','=',False)]}">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane">
|
||||
<field name="foo"/>
|
||||
</div>
|
||||
<div class="o_setting_right_pane">
|
||||
<span class="o_form_label">Hide Foo</span>
|
||||
<div class="text-muted">this is hide foo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,7 +153,7 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
["this is bar", "this is big bar", "this is foo"]
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".settings h2")].map((x) => x.textContent),
|
||||
[...target.querySelectorAll(".settings h2:not(.d-none)")].map((x) => x.textContent),
|
||||
["Title of group Bar", "Title of group Foo"]
|
||||
);
|
||||
assert.doesNotHaveClass(target.querySelector(".o_form_editable"), "o_form_nosheet");
|
||||
@@ -138,6 +162,10 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
target.querySelector(".o_searchview input"),
|
||||
"searchview input should be focused"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "Hello there");
|
||||
await execTimeouts();
|
||||
@@ -146,6 +174,10 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
"Hello there",
|
||||
"input value should be updated"
|
||||
);
|
||||
assert.containsNone(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "b");
|
||||
await execTimeouts();
|
||||
@@ -161,10 +193,14 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
);
|
||||
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".settings h2")].map((x) => x.textContent),
|
||||
[...target.querySelectorAll(".settings h2:not(.d-none)")].map((x) => x.textContent),
|
||||
["Title of group Bar"],
|
||||
"The title of group Bar is also selected"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "Big");
|
||||
await execTimeouts();
|
||||
@@ -174,10 +210,14 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
"Only 'Big Bar' is shown"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".settings h2")].map((x) => x.textContent),
|
||||
[...target.querySelectorAll(".settings h2:not(.d-none)")].map((x) => x.textContent),
|
||||
["Title of group Bar"],
|
||||
"The title of group Bar is also selected"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "group Bar");
|
||||
await execTimeouts();
|
||||
@@ -186,6 +226,10 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
["Bar", "This is Big BAR"],
|
||||
"When searching a title, all group is shown"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "bx");
|
||||
await execTimeouts();
|
||||
@@ -194,18 +238,44 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
target.querySelector(".o_nocontent_help"),
|
||||
"record not found message shown"
|
||||
);
|
||||
assert.containsNone(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "Fo");
|
||||
await execTimeouts();
|
||||
assert.strictEqual(
|
||||
target.querySelector(".highlighter").textContent,
|
||||
"Fo",
|
||||
"F word highlighted"
|
||||
"Fo word highlighted"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_setting_box .o_form_label")].map((x) => x.textContent),
|
||||
["Foo"],
|
||||
"only Foo is shown"
|
||||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
|
||||
await editSearch(target, "Hide");
|
||||
await execTimeouts();
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".settings h2:not(.d-none)")].map((x) => x.textContent),
|
||||
[],
|
||||
"Hide settings should not be shown"
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_setting_box .o_form_label")].map((x) => x.textContent),
|
||||
[],
|
||||
"Hide settings should not be shown"
|
||||
);
|
||||
assert.containsNone(
|
||||
target,
|
||||
".app_settings_block:not(.d-none) .app_settings_header .o_setting_box"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("unhighlight section not matching anymore", async function (assert) {
|
||||
@@ -303,16 +373,20 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
</div>
|
||||
</form>`,
|
||||
});
|
||||
assert.containsOnce(target, ".o_setting_tip", "Tip should not be hidden initially");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_setting_tip:not(.d-none)",
|
||||
"Tip should not be hidden initially"
|
||||
);
|
||||
await editSearch(target, "below");
|
||||
await execTimeouts();
|
||||
assert.containsOnce(target, ".o_setting_tip", "Tip should not be hidden");
|
||||
assert.containsOnce(target, ".o_setting_tip:not(.d-none)", "Tip should not be hidden");
|
||||
await editSearch(target, "Foo");
|
||||
await execTimeouts();
|
||||
assert.containsNone(target, ".o_setting_tip", "Tip should not be displayed");
|
||||
assert.containsNone(target, ".o_setting_tip:not(.d-none)", "Tip should not be displayed");
|
||||
await editSearch(target, "");
|
||||
await execTimeouts();
|
||||
assert.containsOnce(target, ".o_setting_tip", "Tip should not be hidden");
|
||||
assert.containsOnce(target, ".o_setting_tip:not(.d-none)", "Tip should not be hidden");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
@@ -601,11 +675,11 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
await doAction(webClient, 1);
|
||||
assert.containsOnce(target, ".o_form_label");
|
||||
assert.equal(target.querySelector(".o_form_label").textContent, "");
|
||||
assert.containsNone(target, ".settingSearchHeader");
|
||||
assert.containsNone(target, ".app_settings_block:not(.d-none) .settingSearchHeader");
|
||||
await editSearch(target, "Fo");
|
||||
await execTimeouts();
|
||||
assert.containsNone(target, ".o_form_label");
|
||||
assert.containsNone(target, ".settingSearchHeader");
|
||||
assert.containsNone(target, ".app_settings_block:not(.d-none) .settingSearchHeader");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
@@ -1018,7 +1092,15 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
<form string="Settings" js_class="base_settings">
|
||||
<div class="settings">
|
||||
<div class="app_settings_block" string="CRM" data-key="crm">
|
||||
<button name="3" string="Execute action" type="action"/>
|
||||
<h2>Title of group</h2>
|
||||
<div class="row mt16 o_settings_container">
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<button name="3" string="Execute action" type="action"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>`,
|
||||
@@ -1351,7 +1433,7 @@ QUnit.module("SettingsFormView", (hooks) => {
|
||||
const expectedCompiled = `
|
||||
<div class="o_setting_container">
|
||||
<SettingsPage slots="props.slots" initialTab="props.initialApp" t-slot-scope="settings" modules="[{"key":"crm","string":"CRM","imgurl":"/crm/static/description/icon.png","isVisible":false}]" class="'settings'">
|
||||
<SettingsApp t-props="{"key":"crm","string":"CRM","imgurl":"/crm/static/description/icon.png","isVisible":false}" selectedTab="settings.selectedTab" t-if="!searchState.value or search("app", "crm")" class="'app_settings_block'">
|
||||
<SettingsApp t-props="{"key":"crm","string":"CRM","imgurl":"/crm/static/description/icon.png","isVisible":false}" selectedTab="settings.selectedTab" class="'app_settings_block'">
|
||||
<FormLabel t-props="{id:'display_name',fieldName:'display_name',record:props.record,fieldInfo:props.archInfo.fieldNodes['display_name'],className:"highhopes"}" string="\`My" little ' Label\`"/>
|
||||
<Field id="'display_name'" name="'display_name'" record="props.record" fieldInfo="props.archInfo.fieldNodes['display_name']"/>
|
||||
</SettingsApp>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('settings')]" position="inside">
|
||||
<div class="app_settings_block" data-string="Website" string="Website" data-key="website" groups="website.group_website_designer">
|
||||
<div class="row o_settings_container mb-0 mt-0 ms-0 mw-100"
|
||||
<div class="row app_settings_header mb-0 mt-0 ms-0 mw-100"
|
||||
style="background-color: #FEF0D0; margin-bottom: 0px !important; margin-top: 0px !important;">
|
||||
<div class="col-xs-12 col-md-6 ms-0 o_setting_box">
|
||||
<div class="o_setting_right_pane border-start-0 ms-0 ps-0">
|
||||
|
||||
Reference in New Issue
Block a user