diff --git a/packages/lib/src/base-menu/GroupItem.tsx b/packages/lib/src/base-menu/GroupItem.tsx index 7817dfd214..177e1dab38 100644 --- a/packages/lib/src/base-menu/GroupItem.tsx +++ b/packages/lib/src/base-menu/GroupItem.tsx @@ -36,14 +36,18 @@ const GroupItem = ({ items, ...props }: GroupItemProps) => { aria-controls={isOpen ? groupMenuId : undefined} aria-expanded={isOpen ? true : undefined} collapseIcon={isOpen ? : } - onClick={() => toggleOpen()} + onClick={() => { + toggleOpen(); + }} selected={groupSelected && !isOpen} {...props} /> {portalContainer && ( - + { @@ -107,7 +111,9 @@ const GroupItem = ({ items, ...props }: GroupItemProps) => { aria-expanded={isOpen ? true : undefined} aria-pressed={groupSelected && !isOpen} collapseIcon={isOpen ? : } - onClick={() => toggleOpen()} + onClick={() => { + toggleOpen(); + }} selected={groupSelected && !isOpen} {...props} /> diff --git a/packages/lib/src/base-menu/SingleItem.tsx b/packages/lib/src/base-menu/SingleItem.tsx index 71606d85dd..11d57bd384 100644 --- a/packages/lib/src/base-menu/SingleItem.tsx +++ b/packages/lib/src/base-menu/SingleItem.tsx @@ -4,11 +4,14 @@ import { SingleItemProps } from "./types"; import BaseMenuContext from "./BaseMenuContext"; export default function SingleItem({ id, onSelect, selected = false, ...props }: SingleItemProps) { - const { selectedItemId, setSelectedItemId } = useContext(BaseMenuContext) ?? {}; + const { selectedItemId, setSelectedItemId, hasPopOver, closePopOver, onSelectItem } = + useContext(BaseMenuContext) ?? {}; const handleClick = () => { setSelectedItemId?.(id); onSelect?.(); + if (!hasPopOver) closePopOver?.(); + onSelectItem?.(); }; useEffect(() => { diff --git a/packages/lib/src/base-menu/types.ts b/packages/lib/src/base-menu/types.ts index adf2ea4fe8..20fdb1545a 100644 --- a/packages/lib/src/base-menu/types.ts +++ b/packages/lib/src/base-menu/types.ts @@ -45,6 +45,11 @@ type Props = { * If true the menu will be displayed horizontally. */ isHorizontal?: boolean; + /** + * Function that will be called when an option is clicked. + * This is used to notify that an item has been selected to close any visible menu that is not managed by the navigation tree. + */ + onSelectItem?: () => void; }; type ItemWithId = Item & { id: number }; @@ -94,6 +99,8 @@ type BaseMenuContextProps = { displayControlsAfter?: boolean; hasPopOver?: boolean; isHorizontal?: boolean; + onSelectItem?: () => void; + closePopOver?: () => void; }; export type { diff --git a/packages/lib/src/base-menu/useGroupItem.ts b/packages/lib/src/base-menu/useGroupItem.ts index c24123ee1d..96f11608bd 100644 --- a/packages/lib/src/base-menu/useGroupItem.ts +++ b/packages/lib/src/base-menu/useGroupItem.ts @@ -14,7 +14,9 @@ export const useGroupItem = (items: GroupItemProps["items"], context: BaseMenuCo const groupSelected = useMemo(() => isGroupSelected(items, selectedItemId), [items, selectedItemId]); const [isOpen, setIsOpen] = useState(hasPopOver ? false : (defaultOpen ?? (groupSelected && selectedItemId === -1))); - const toggleOpen = () => setIsOpen((prev) => !prev); + const toggleOpen = () => { + setIsOpen((prev) => !prev); + }; return { groupMenuId, @@ -23,5 +25,6 @@ export const useGroupItem = (items: GroupItemProps["items"], context: BaseMenuCo toggleOpen, hasPopOver: context.hasPopOver, isHorizontal: context.isHorizontal, + onSelectItem: context.onSelectItem, }; }; diff --git a/packages/lib/src/header/Header.test.tsx b/packages/lib/src/header/Header.test.tsx index 0b4367cbf6..760a591d6f 100644 --- a/packages/lib/src/header/Header.test.tsx +++ b/packages/lib/src/header/Header.test.tsx @@ -6,6 +6,30 @@ import { Item, GroupItem } from "../base-menu/types"; describe("Header component tests", () => { const mockMatchMedia = jest.fn(); + const items = [ + { + label: "Grouped Item 1", + icon: "favorite", + items: [ + { label: "Item 1", icon: "person", selected: true }, + { + label: "Grouped Item 2", + items: [ + { + label: "Item 2", + icon: "bookmark", + }, + { label: "Selected Item 3" }, + ], + }, + ], + }, + { label: "Item 4", icon: "key" }, + { label: "Item 5", icon: "person" }, + { label: "Grouped Item 6", items: [{ label: "Item 7", icon: "person" }, { label: "Item 8" }] }, + { label: "Item 9" }, + ]; + beforeAll(() => { Object.defineProperty(window, "matchMedia", { writable: true, @@ -95,4 +119,13 @@ describe("Header component tests", () => { const cancelButton = screen.queryByRole("button", { name: /cancel/i }); expect(cancelButton).not.toBeInTheDocument(); }); + + test("navigation group items closes whenever an item is clicked", () => { + render(); + const itemGroup6 = screen.getByText("Grouped Item 6"); + fireEvent.click(itemGroup6); + const item7 = screen.getByText("Item 7"); + fireEvent.click(item7); + expect(item7).not.toBeInTheDocument(); + }); }); diff --git a/packages/lib/src/header/Header.tsx b/packages/lib/src/header/Header.tsx index cdd4089c60..5bfbeb447c 100644 --- a/packages/lib/src/header/Header.tsx +++ b/packages/lib/src/header/Header.tsx @@ -272,6 +272,7 @@ const DxcHeader = ({ displayGroupLines={false} displayBorder={false} displayControlsAfter + onSelectItem={() => toggleMenu()} /> {responsiveBottomContent && ( <> diff --git a/packages/lib/src/navigation-tree/NavigationTree.tsx b/packages/lib/src/navigation-tree/NavigationTree.tsx index f47568d38a..9691dee40e 100644 --- a/packages/lib/src/navigation-tree/NavigationTree.tsx +++ b/packages/lib/src/navigation-tree/NavigationTree.tsx @@ -34,6 +34,7 @@ export default function DxcNavigationTree({ displayControlsAfter = false, hasPopOver = false, isHorizontal = false, + onSelectItem, }: NavigationTreePropsType) { const [firstUpdate, setFirstUpdate] = useState(true); const [selectedItemId, setSelectedItemId] = useState(-1); @@ -48,8 +49,9 @@ export default function DxcNavigationTree({ displayControlsAfter, hasPopOver, isHorizontal, + onSelectItem, }), - [selectedItemId, setSelectedItemId, displayGroupLines, displayControlsAfter, hasPopOver, isHorizontal] + [selectedItemId, setSelectedItemId, displayGroupLines, displayControlsAfter, hasPopOver, isHorizontal, onSelectItem] ); useLayoutEffect(() => {