From 2460d637149fe25041c54267b11709a259b32e89 Mon Sep 17 00:00:00 2001 From: Andrea Rosci Date: Sun, 7 Jan 2024 13:46:58 +0100 Subject: [PATCH] Refactor Dropdown component to add menu support Change-type: minor --- src/components/ButtonWithTracking/index.tsx | 3 +- src/components/DropDownButton/index.tsx | 254 ++++++++++++-------- src/components/Tooltip/index.tsx | 2 +- 3 files changed, 155 insertions(+), 104 deletions(-) diff --git a/src/components/ButtonWithTracking/index.tsx b/src/components/ButtonWithTracking/index.tsx index f6ab5c5d..a5046e86 100644 --- a/src/components/ButtonWithTracking/index.tsx +++ b/src/components/ButtonWithTracking/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Button, ButtonProps, Tooltip } from '@mui/material'; +import { Button, ButtonProps } from '@mui/material'; import { useAnalyticsContext } from '../../contexts/AnalyticsContext'; +import { Tooltip } from '../Tooltip'; export interface ButtonWithTrackingProps extends ButtonProps { eventName: string; diff --git a/src/components/DropDownButton/index.tsx b/src/components/DropDownButton/index.tsx index ec8a4308..d0e9a191 100644 --- a/src/components/DropDownButton/index.tsx +++ b/src/components/DropDownButton/index.tsx @@ -1,28 +1,34 @@ -import * as React from 'react'; +import { useMemo, useState } from 'react'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; -import ClickAwayListener from '@mui/material/ClickAwayListener'; import { Button, ButtonGroup, ButtonGroupProps, - Tooltip, - Grow, - Paper, - Popper, MenuItem, - MenuList, + MenuItemProps, + Menu, + ButtonProps, } from '@mui/material'; -import { - ButtonWithTracking, - ButtonWithTrackingProps, -} from '../ButtonWithTracking'; +import { ButtonWithTracking } from '../ButtonWithTracking'; +import { useAnalyticsContext } from '../../contexts/AnalyticsContext'; +import groupBy from 'lodash/groupBy'; +import flatMap from 'lodash/flatMap'; +import { KeyboardArrowDown } from '@mui/icons-material'; +import { Tooltip } from '../Tooltip'; + +type MenuItemType = MenuItemWithTrackingProps & + T & { + tooltip?: string | undefined; + }; -export interface DropDownButtonProps extends Omit { - items: Array; +export interface DropDownButtonProps + extends Omit { + items: Array>; selectedItemIndex?: number; + groupByProp?: keyof T; onClick?: ( - event: React.MouseEvent, - button: ButtonWithTrackingProps, + event: React.MouseEvent, + item: MenuItemWithTrackingProps, ) => void; } @@ -30,111 +36,155 @@ export interface DropDownButtonProps extends Omit { * This component implements a Dropdown button using MUI (This can be removed as soon as MUI implements it. Check * progress: https://mui.com/material-ui/discover-more/roadmap/#new-components) */ -export const DropDownButton: React.FC = ({ +export const DropDownButton = ({ items, selectedItemIndex = 0, + groupByProp, onClick, - ...buttonGroupProps -}) => { - const [open, setOpen] = React.useState(false); - const anchorRef = React.useRef(null); - const [selectedIndex, setSelectedIndex] = React.useState(selectedItemIndex); + children, + ...buttonProps +}: DropDownButtonProps) => { + const [anchorEl, setAnchorEl] = useState(null); + const [selectedIndex, setSelectedIndex] = useState(selectedItemIndex); - const handleClick: React.MouseEventHandler = (event) => - items[selectedIndex].onClick?.(event) || - onClick?.(event, items[selectedIndex]); + // To use the groupBy pass another property on each item and define that property on groupByProp. + // const items = [{...menuItem, section: 'test1'}, {...menuItem, section: 'test2'}]; + // + const memoizedItems = useMemo(() => { + if (!groupByProp) { + return items; + } + const grouped = groupBy(items, (item) => item[groupByProp]); + const keys = Object.keys(grouped); + const lastKey = keys[keys.length - 1]; - const handleMenuItemClick = (index: number) => { - setSelectedIndex(index); - setOpen(false); - }; + return flatMap(grouped, (value, key) => [ + ...value.map((v, index) => + key !== lastKey && index === value.length - 1 + ? { ...v, divider: true } + : v, + ), + ]).filter((item) => item); + }, [items, groupByProp]); - const handleToggle = () => { - setOpen((prevOpen) => !prevOpen); + const handleClick = ( + event: React.MouseEvent, + ) => { + setAnchorEl(event.currentTarget); + return ( + items?.[selectedIndex]?.onClick?.(event) ?? + onClick?.(event, items[selectedIndex]) + ); }; - const handleClose = (event: Event) => { - if ( - anchorRef.current && - anchorRef.current.contains(event.target as HTMLElement) - ) { - return; + const handleMenuItemClick = ( + event: React.MouseEvent, + index: number, + ) => { + setSelectedIndex(index); + setAnchorEl(null); + if (children) { + return ( + items?.[index]?.onClick?.(event) ?? + onClick?.(event, items[selectedIndex]) + ); } + }; - setOpen(false); + const handleToggle = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); }; return ( - - - - {items[selectedIndex].children} - + <> + {children ? ( - - + + {items[selectedIndex].children} + + + + )} + { + setAnchorEl(null); }} - open={open} - anchorEl={anchorRef.current} - role={undefined} - transition - disablePortal > - {({ TransitionProps, placement }) => ( - ( + handleMenuItemClick(event, index)} > - - - - {items.map((option, index) => ( - - handleMenuItemClick(index)} - > - {option.children} - - - ))} - - - - - )} - - + {item.children} + + ))} + + + ); +}; + +export interface MenuItemWithTrackingProps + extends Omit { + eventName: string; + eventProperties?: { [key: string]: any }; + tooltip?: string; + onClick?: React.MouseEventHandler; +} + +/** + * This MenuItem will send analytics in case the analytics context is passed through the provider (AnalyticsProvider). + */ +export const MenuItemWithTracking: React.FC = ({ + eventName, + eventProperties, + children, + tooltip, + onClick, + ...menuItem +}) => { + const { state } = useAnalyticsContext(); + + const handleClick = (event: React.MouseEvent) => { + if (state.webTracker) { + state.webTracker.track(eventName, eventProperties); + } + onClick?.(event); + }; + + return ( + + + {children} + + ); }; diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index e10fa2a0..2fce0927 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -7,7 +7,7 @@ import { TooltipProps, Tooltip as MuiTooltip } from '@mui/material'; const Tooltip: React.FC = ({ children, ...tooltipProps }) => { return ( - {children} + {children.props.disabled ? {children} : children} ); };