diff --git a/addons/web/static/src/webclient/settings_form_view/settings/setting.js b/addons/web/static/src/webclient/settings_form_view/settings/setting.js new file mode 100644 index 00000000000..0d3642e7b4b --- /dev/null +++ b/addons/web/static/src/webclient/settings_form_view/settings/setting.js @@ -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"; diff --git a/addons/web/static/src/webclient/settings_form_view/settings/setting.xml b/addons/web/static/src/webclient/settings_form_view/settings/setting.xml new file mode 100644 index 00000000000..43f85cf0666 --- /dev/null +++ b/addons/web/static/src/webclient/settings_form_view/settings/setting.xml @@ -0,0 +1,8 @@ + + + +
+ +
+
+
diff --git a/addons/web/static/src/webclient/settings_form_view/settings/settings_app.js b/addons/web/static/src/webclient/settings_form_view/settings/settings_app.js index b5690da131d..c9c693a5993 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings/settings_app.js +++ b/addons/web/static/src/webclient/settings_form_view/settings/settings_app.js @@ -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"; diff --git a/addons/web/static/src/webclient/settings_form_view/settings/settings_app.xml b/addons/web/static/src/webclient/settings_form_view/settings/settings_app.xml index 24e43cb8ce0..5724316db6e 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings/settings_app.xml +++ b/addons/web/static/src/webclient/settings_form_view/settings/settings_app.xml @@ -1,7 +1,7 @@ -
+
@@ -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) => {
`, }); - 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) => {
-
+
+
`, @@ -1351,7 +1433,7 @@ QUnit.module("SettingsFormView", (hooks) => { const expectedCompiled = `
- + diff --git a/addons/website/views/res_config_settings_views.xml b/addons/website/views/res_config_settings_views.xml index 0943d126543..5368f5a9acd 100644 --- a/addons/website/views/res_config_settings_views.xml +++ b/addons/website/views/res_config_settings_views.xml @@ -20,7 +20,7 @@
-