diff --git a/addons/project/static/tests/tours/project_burndown_chart_tour.js b/addons/project/static/tests/tours/project_burndown_chart_tour.js index ba38e91c718..65bfc208528 100644 --- a/addons/project/static/tests/tours/project_burndown_chart_tour.js +++ b/addons/project/static/tests/tours/project_burndown_chart_tour.js @@ -43,9 +43,6 @@ registry.category("web_tour.tours").add('burndown_chart_tour', { }, { content: 'Open the Date group by sub menu', trigger: '.o_group_by_menu button.o_menu_item:contains("Date")', - run: function () { - this.$anchor[0].dispatchEvent(new Event('mouseenter')); - }, }, { content: 'Click on the selected Date sub menu', trigger: '.o_group_by_menu button.o_menu_item:contains("Date") + * .dropdown-item.selected', @@ -61,9 +58,6 @@ registry.category("web_tour.tours").add('burndown_chart_tour', { }, { content: 'Open the Date filter sub menu', trigger: '.o_filter_menu button.o_menu_item:contains("Date")', - run: function () { - this.$anchor[0].dispatchEvent(new Event('mouseenter')); - }, }, { content: 'Click on the first Date filter sub menu', trigger: '.o_filter_menu .o_menu_item:contains("Date") + * .dropdown-item:first-child', diff --git a/addons/project/static/tests/tours/project_sharing_tour.js b/addons/project/static/tests/tours/project_sharing_tour.js index 7dbaaafa75b..f91acfaa846 100644 --- a/addons/project/static/tests/tours/project_sharing_tour.js +++ b/addons/project/static/tests/tours/project_sharing_tour.js @@ -71,8 +71,11 @@ const projectSharingSteps = [...stepUtils.goToAppSteps("project.menu_main_pm", ' trigger: 'iframe .o_group_by_menu .dropdown-item:first-child', content: 'click on the first item in the group by menu', }, { - trigger: 'iframe .o_favorite_menu .o_add_favorite > button', - content: 'click to "save current search" button in favorite menu', + trigger: 'iframe .o_favorite_menu .o_add_favorite', + content: 'open accordion "save current search" in favorite menu', +}, { + trigger: 'iframe .o_favorite_menu .o_accordion_values .o_save_favorite', + content: 'click to "save" button in favorite menu', }, { trigger: 'iframe .o_filter_menu .dropdown-item:first-child', content: 'click on the first item in the filter menu', @@ -80,8 +83,8 @@ const projectSharingSteps = [...stepUtils.goToAppSteps("project.menu_main_pm", ' trigger: 'iframe .o_group_by_menu .dropdown-item:first-child', content: 'click on the first item in the group by menu', }, { - trigger: 'iframe .o_favorite_menu .o_add_favorite > button', - content: 'click to "save current search" button in favorite menu', + trigger: 'iframe .o_favorite_menu .o_accordion_values .o_save_favorite', + content: 'click to "save" button in favorite menu', }, { trigger: 'iframe button.o_switch_view.o_list', content: 'Go to the list view', diff --git a/addons/web/static/src/core/dropdown/accordion_item.js b/addons/web/static/src/core/dropdown/accordion_item.js new file mode 100644 index 00000000000..7bb11cff806 --- /dev/null +++ b/addons/web/static/src/core/dropdown/accordion_item.js @@ -0,0 +1,40 @@ +/** @odoo-module **/ + +import { Component, onPatched, useState } from "@odoo/owl"; + +export const ACCORDION = Symbol("Accordion"); +export class AccordionItem extends Component { + static template = "web.AccordionItem"; + static components = {}; + static props = { + slots: { + type: Object, + shape: { + default: {}, + }, + }, + description: String, + selected: { + type: Boolean, + optional: true, + }, + class: { + type: String, + optional: true, + }, + }; + static defaultProps = { + class: "", + selected: false, + }; + + setup() { + this.state = useState({ + open: false, + }); + this.parentComponent = this.env[ACCORDION]; + onPatched(() => { + this.parentComponent?.accordionStateChanged?.(); + }); + } +} diff --git a/addons/web/static/src/core/dropdown/accordion_item.scss b/addons/web/static/src/core/dropdown/accordion_item.scss new file mode 100644 index 00000000000..7dc8ba479f5 --- /dev/null +++ b/addons/web/static/src/core/dropdown/accordion_item.scss @@ -0,0 +1,12 @@ +.o_accordion_toggle { + @include caret(); + + &.open { + @include caret('up'); + } + + &::after { + @include o-position-absolute($top: 0, $right: 0); + transform: translate(-0.6em, 0.8em) /*rtl:translate(0.6em, 0.8em) scaleX(-1)*/; + } +} diff --git a/addons/web/static/src/core/dropdown/accordion_item.xml b/addons/web/static/src/core/dropdown/accordion_item.xml new file mode 100644 index 00000000000..5f6418e6c51 --- /dev/null +++ b/addons/web/static/src/core/dropdown/accordion_item.xml @@ -0,0 +1,21 @@ + + + + +
+
+
+ +
diff --git a/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js b/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js index db4a3a8c26f..5f7e547aaba 100644 --- a/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js +++ b/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js @@ -5,7 +5,8 @@ import { browser } from "../browser/browser"; import { localization } from "@web/core/l10n/localization"; import { scrollTo } from "../utils/scrolling"; -import { useComponent, useEffect, useRef } from "@odoo/owl"; +import { useChildSubEnv, useComponent, useEffect, useRef } from "@odoo/owl"; +import { ACCORDION } from "@web/core/dropdown/accordion_item"; /** * @typedef {{ @@ -24,8 +25,12 @@ const ACTIVE_MENU_ELEMENT_CLASS = "focus"; const MENU_ELEMENTS_SELECTORS = [ ":scope > .dropdown-item", ":scope > .dropdown", + ":scope > .o_accordion > .dropdown-item", + ":scope > .o_accordion > .o_accordion_values > .dropdown-item", ":scope > .o_dropdown_container > .dropdown-item", ":scope > .o_dropdown_container > .dropdown", + ":scope > .o_dropdown_container > .o_accordion > .dropdown-item", + ":scope > .o_dropdown_container > .o_accordion > .o_accordion_values > .dropdown-item", ]; const NEXT_ACTIVE_INDEX_FNS = { FIRST: () => 0, @@ -70,7 +75,9 @@ export function useDropdownNavigation() { const menuRef = useRef("menuRef"); /** @type {MenuElement[]} */ let menuElements = []; - useEffect(() => { + + let cleanupMenuElements; + const refreshMenuElements = () => { if (!comp.state.open) { return; } @@ -157,7 +164,7 @@ export function useDropdownNavigation() { } addedListeners.push([navTarget, elementListeners]); } - return () => { + cleanupMenuElements = () => { menuElements = []; mouseSelectionActive = true; @@ -168,6 +175,21 @@ export function useDropdownNavigation() { } } }; + return () => cleanupMenuElements(); + }; + + useEffect(refreshMenuElements); + + // Set up nested accordion + // This is needed in order to keep the parent dropdown + // aware of the accordion menu elements when its state has changed. + useChildSubEnv({ + [ACCORDION]: { + accordionStateChanged: () => { + cleanupMenuElements?.(); + refreshMenuElements(); + }, + }, }); // Set up active menu element helpers -------------------------------------- diff --git a/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.js b/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.js index f8c0441e29a..a5ddeb1243b 100644 --- a/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.js +++ b/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { Dropdown } from "@web/core/dropdown/dropdown"; +import { AccordionItem } from "@web/core/dropdown/accordion_item"; import { CheckBox } from "@web/core/checkbox/checkbox"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; @@ -91,7 +91,7 @@ export class CustomFavoriteItem extends Component { } CustomFavoriteItem.template = "web.CustomFavoriteItem"; -CustomFavoriteItem.components = { CheckBox, Dropdown }; +CustomFavoriteItem.components = { CheckBox, AccordionItem }; CustomFavoriteItem.props = {}; favoriteMenuRegistry.add( "custom-favorite-item", diff --git a/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.xml b/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.xml index 04b53015f30..1471272e83f 100644 --- a/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.xml +++ b/addons/web/static/src/search/custom_favorite_item/custom_favorite_item.xml @@ -2,10 +2,7 @@ - - - Save current search - +
- Use by default + Default filter - Share with all users + Shared
-
-
+
diff --git a/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.js b/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.js index 5f30af28902..0e328ba4927 100644 --- a/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.js +++ b/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { Dropdown } from "@web/core/dropdown/dropdown"; +import { AccordionItem } from "@web/core/dropdown/accordion_item"; import { Component, useState } from "@odoo/owl"; @@ -14,7 +14,7 @@ export class CustomGroupByItem extends Component { } CustomGroupByItem.template = "web.CustomGroupByItem"; -CustomGroupByItem.components = { Dropdown }; +CustomGroupByItem.components = { AccordionItem }; CustomGroupByItem.props = { fields: Array, onAddCustomGroup: Function, diff --git a/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.xml b/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.xml index f2a202bf589..fb3b5770b61 100644 --- a/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.xml +++ b/addons/web/static/src/search/custom_group_by_item/custom_group_by_item.xml @@ -1,12 +1,9 @@ - - - Add Custom Group - +
-
-
+
diff --git a/addons/web/static/src/search/search_bar_menu/search_bar_menu.js b/addons/web/static/src/search/search_bar_menu/search_bar_menu.js index b540f4092ec..79f2363e12b 100644 --- a/addons/web/static/src/search/search_bar_menu/search_bar_menu.js +++ b/addons/web/static/src/search/search_bar_menu/search_bar_menu.js @@ -8,6 +8,7 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { registry } from "@web/core/registry"; import { sortBy } from "@web/core/utils/arrays"; import { useBus, useService } from "@web/core/utils/hooks"; +import { AccordionItem } from "@web/core/dropdown/accordion_item"; import { CustomGroupByItem } from "@web/search/custom_group_by_item/custom_group_by_item"; import { SearchDropdownItem } from "@web/search/search_dropdown_item/search_dropdown_item"; import { FACET_ICONS, GROUPABLE_TYPES } from "@web/search/utils/misc"; @@ -23,6 +24,7 @@ export class SearchBarMenu extends Component { DropdownItem, SearchDropdownItem, CustomGroupByItem, + AccordionItem, }; static props = { slots: { diff --git a/addons/web/static/src/search/search_bar_menu/search_bar_menu.scss b/addons/web/static/src/search/search_bar_menu/search_bar_menu.scss index a5d076f9ca5..a3c9165cd10 100644 --- a/addons/web/static/src/search/search_bar_menu/search_bar_menu.scss +++ b/addons/web/static/src/search/search_bar_menu/search_bar_menu.scss @@ -3,5 +3,12 @@ .o_search_bar_menu { .o_dropdown_container { border-color: $dropdown-divider-bg !important; + min-width: 220px; + } + + .o_accordion_values .o_input { + max-width: 17ch; + text-overflow: ellipsis; + white-space: nowrap; } } \ No newline at end of file diff --git a/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml b/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml index 4e7c39791db..fff083ad939 100644 --- a/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml +++ b/addons/web/static/src/search/search_bar_menu/search_bar_menu.xml @@ -22,10 +22,7 @@