diff --git a/.changeset/shaggy-carrots-talk.md b/.changeset/shaggy-carrots-talk.md deleted file mode 100644 index 7ea4888b69..0000000000 --- a/.changeset/shaggy-carrots-talk.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@leafygreen-ui/menu': minor ---- - -Adds `title` and `glyph` props to `MenuGroup`. Providing a title to `MenuGroup` will visually indent the child `MenuItem` components, appearing nested within the group. \ No newline at end of file diff --git a/packages/menu/package.json b/packages/menu/package.json index 44a435a801..d3ffcff680 100644 --- a/packages/menu/package.json +++ b/packages/menu/package.json @@ -27,13 +27,11 @@ "@leafygreen-ui/hooks": "^8.1.3", "@leafygreen-ui/icon": "^12.5.4", "@leafygreen-ui/icon-button": "^15.0.21", - "@leafygreen-ui/input-option": "^1.1.4", "@leafygreen-ui/lib": "^13.6.0", "@leafygreen-ui/palette": "^4.0.9", "@leafygreen-ui/popover": "^11.4.0", "@leafygreen-ui/polymorphic": "^2.0.0", "@leafygreen-ui/tokens": "^2.9.0", - "@leafygreen-ui/typography": "^19.2.0", "lodash": "^4.17.21", "polished": "^4.3.1", "react-transition-group": "^4.4.5" diff --git a/packages/menu/src/Menu.stories.tsx b/packages/menu/src/Menu.stories.tsx index bcc51c72e1..fdbd615952 100644 --- a/packages/menu/src/Menu.stories.tsx +++ b/packages/menu/src/Menu.stories.tsx @@ -20,14 +20,7 @@ import { TestUtils } from '@leafygreen-ui/popover'; const { getAlign, getJustify } = TestUtils; import { Size } from './types'; -import { - Menu, - MenuGroup, - MenuItem, - MenuProps, - MenuSeparator, - SubMenu, -} from '.'; +import { Menu, MenuItem, MenuProps, MenuSeparator, SubMenu } from '.'; const getDecoratorStyles = (args: Partial) => { return css` @@ -72,7 +65,7 @@ export default { align: 'bottom', usePortal: true, darkMode: false, - renderDarkMenu: false, + renderDarkMenu: true, }, argTypes: { open: { @@ -141,13 +134,14 @@ export const LiveExample = { Delete - - Lorem - Ipsum - Dolor - Sit - Amet - + Lorem + Ipsum + Adipiscing + Cursus + Ullamcorper + Vulputate + Inceptos + Risus ); }, diff --git a/packages/menu/src/MenuContext/GroupContext.tsx b/packages/menu/src/MenuContext/GroupContext.tsx deleted file mode 100644 index 10f0f7d013..0000000000 --- a/packages/menu/src/MenuContext/GroupContext.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { createContext, PropsWithChildren, useContext } from 'react'; - -export interface MenuGroupContextData { - depth: number; - hasIcon: boolean; -} - -export const MenuGroupContext = createContext({ - depth: 0, - hasIcon: false, -}); - -export const MenuGroupProvider = ({ - children, - depth, - hasIcon = false, -}: PropsWithChildren) => ( - - {children} - -); - -export const useMenuGroupContext = () => useContext(MenuGroupContext); diff --git a/packages/menu/src/MenuContext/MenuContext.tsx b/packages/menu/src/MenuContext/MenuContext.tsx index 59e0caf472..7a4a9a3b45 100644 --- a/packages/menu/src/MenuContext/MenuContext.tsx +++ b/packages/menu/src/MenuContext/MenuContext.tsx @@ -1,24 +1,8 @@ import { createContext, useContext } from 'react'; import { createDescendantsContext } from '@leafygreen-ui/descendants'; -import { Descendant } from '@leafygreen-ui/descendants'; -import { Theme } from '@leafygreen-ui/lib'; -import { HighlightReducerReturnType } from '../HighlightReducer/highlight.types'; - -export interface MenuContextData { - theme: Theme; - darkMode: boolean; - - /** The index of the currently highlighted (focused) item */ - highlight?: Descendant; - - /** Sets the current highlight by index or id */ - setHighlight?: HighlightReducerReturnType['setHighlight']; - - /** Whether to render a dark menu in light mode */ - renderDarkMenu?: boolean; -} +import { MenuContextData } from './MenuContext.types'; export const MenuDescendantsContext = createDescendantsContext( 'MenuDescendantsContext', @@ -31,3 +15,5 @@ export const MenuContext = createContext({ }); export const useMenuContext = () => useContext(MenuContext); + +export default MenuContext; diff --git a/packages/menu/src/MenuContext/MenuContext.types.ts b/packages/menu/src/MenuContext/MenuContext.types.ts new file mode 100644 index 0000000000..3a41943903 --- /dev/null +++ b/packages/menu/src/MenuContext/MenuContext.types.ts @@ -0,0 +1,18 @@ +import { Descendant } from '@leafygreen-ui/descendants'; +import { Theme } from '@leafygreen-ui/lib'; + +import { HighlightReducerReturnType } from '../HighlightReducer/highlight.types'; + +export interface MenuContextData { + theme: Theme; + darkMode: boolean; + + /** The index of the currently highlighted (focused) item */ + // highlightIndex?: number; + highlight?: Descendant; + + setHighlight?: HighlightReducerReturnType['setHighlight']; + + /** Whether to a dark menu in light mode */ + renderDarkMenu?: boolean; +} diff --git a/packages/menu/src/MenuContext/SubMenuContext.tsx b/packages/menu/src/MenuContext/SubMenuContext.tsx deleted file mode 100644 index 51e50bd6f2..0000000000 --- a/packages/menu/src/MenuContext/SubMenuContext.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React, { createContext, PropsWithChildren, useContext } from 'react'; - -export interface SubMenuContextData { - depth: number; - hasIcon: boolean; -} - -export const SubMenuContext = createContext({ - depth: 0, - hasIcon: false, -}); - -export const SubMenuProvider = ({ - children, - depth, - hasIcon = false, -}: PropsWithChildren) => ( - - {children} - -); - -export const useSubMenuContext = () => useContext(SubMenuContext); diff --git a/packages/menu/src/MenuContext/index.ts b/packages/menu/src/MenuContext/index.ts index 86c6880e74..86352e4de7 100644 --- a/packages/menu/src/MenuContext/index.ts +++ b/packages/menu/src/MenuContext/index.ts @@ -1,18 +1,5 @@ export { - type MenuGroupContext, - MenuGroupContextData, - MenuGroupProvider, - useMenuGroupContext, -} from './GroupContext'; -export { - MenuContext, - type MenuContextData, + default as MenuContext, MenuDescendantsContext, useMenuContext, } from './MenuContext'; -export { - SubMenuContext, - type SubMenuContextData, - SubMenuProvider, - useSubMenuContext, -} from './SubMenuContext'; diff --git a/packages/menu/src/MenuGroup/MenuGroup.stories.tsx b/packages/menu/src/MenuGroup/MenuGroup.stories.tsx deleted file mode 100644 index 9019f33d21..0000000000 --- a/packages/menu/src/MenuGroup/MenuGroup.stories.tsx +++ /dev/null @@ -1,90 +0,0 @@ -/* eslint-disable react/jsx-key */ -import React from 'react'; -import { StoryMetaType } from '@lg-tools/storybook-utils'; -import { StoryObj } from '@storybook/react'; - -import { css } from '@leafygreen-ui/emotion'; -import Icon, { glyphs } from '@leafygreen-ui/icon'; - -import { MenuItem } from '../MenuItem'; -import { SubMenu } from '../SubMenu'; -import { withMenuContext } from '../testUtils/withMenuContextDecorator.testutils'; - -import { MenuGroup } from './MenuGroup'; - -export default { - title: 'Components/Menu/MenuGroup', - component: MenuGroup, - parameters: { - default: null, - }, - args: { - title: 'Group', - glyph: 'AllProducts', - darkMode: false, - }, - argTypes: { - darkMode: { - control: 'boolean', - }, - glyph: { - control: 'select', - options: [undefined, ...Object.keys(glyphs)], - }, - }, - decorators: [withMenuContext()], -} satisfies StoryMetaType; - -export const LiveExample = { - render: ({ glyph, ...args }) => ( -
- - } - > - Apple - Banana - Carrot - - JalapeƱo - Habanero - Ghost - - - - Lasagna - Haggis - }> - Jellybeans - Chocolate - Cotton Candy - - -
- ), - parameters: { - chromatic: { - disableSnapshot: true, - }, - }, -} satisfies StoryObj; - -export const Generated = { - render: () => <>, - parameters: { - generate: { - combineArgs: { - darkMode: [false, true], - glyph: [undefined, ], - }, - decorator: withMenuContext(), - }, - }, -} satisfies StoryObj; diff --git a/packages/menu/src/MenuGroup/MenuGroup.styles.ts b/packages/menu/src/MenuGroup/MenuGroup.styles.ts deleted file mode 100644 index 43cb204e26..0000000000 --- a/packages/menu/src/MenuGroup/MenuGroup.styles.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { css } from '@leafygreen-ui/emotion'; -import { Theme } from '@leafygreen-ui/lib'; -import { color } from '@leafygreen-ui/tokens'; - -import { menuColor } from '../styles'; - -export const getMenuGroupItemStyles = (theme: Theme) => css` - cursor: unset; - background-color: ${menuColor[theme].background.default}; -`; - -export const getMenuGroupTitleStyles = (theme: Theme) => css` - color: ${color[theme].text.secondary.default}; -`; - -export const menuGroupULStyles = css` - margin: 0; - padding: 0; -`; diff --git a/packages/menu/src/MenuGroup/MenuGroup.tsx b/packages/menu/src/MenuGroup/MenuGroup.tsx index 74ca3bc097..ed9da3b34f 100644 --- a/packages/menu/src/MenuGroup/MenuGroup.tsx +++ b/packages/menu/src/MenuGroup/MenuGroup.tsx @@ -1,74 +1,23 @@ import * as React from 'react'; import PropTypes from 'prop-types'; -import { useIdAllocator } from '@leafygreen-ui/hooks'; -import { InputOption, InputOptionContent } from '@leafygreen-ui/input-option'; -import { Overline } from '@leafygreen-ui/typography'; - -import { - MenuGroupProvider, - useMenuContext, - useMenuGroupContext, -} from '../MenuContext'; - -import { - getMenuGroupItemStyles, - getMenuGroupTitleStyles, - menuGroupULStyles, -} from './MenuGroup.styles'; import { MenuGroupProps } from './MenuGroup.types'; /** * # MenuGroup * * ``` - - Item 1 + + Hello World! * ``` * @param props.children Content to appear inside of the MenuGroup. * */ -export function MenuGroup({ - children, - className, - title, - glyph, - ...rest -}: MenuGroupProps) { - const { theme, darkMode } = useMenuContext(); - const id = useIdAllocator({ prefix: 'lg-menu-group' }); - const { depth } = useMenuGroupContext(); - - const shouldRenderGroupHeader = !!title; - const hasIcon = shouldRenderGroupHeader && !!glyph; - // We only indent the child items if we render a title here, - // otherwise we just pass through - const nextGroupDepth = depth + (shouldRenderGroupHeader ? 1 : 0); - +export function MenuGroup({ children, className, ...rest }: MenuGroupProps) { return (
- {title && ( - - - - {title} - - - - )} - -
    - {children} -
-
+ {children}
); } diff --git a/packages/menu/src/MenuGroup/MenuGroup.types.ts b/packages/menu/src/MenuGroup/MenuGroup.types.ts index 4b70856400..9e72464469 100644 --- a/packages/menu/src/MenuGroup/MenuGroup.types.ts +++ b/packages/menu/src/MenuGroup/MenuGroup.types.ts @@ -1,15 +1,5 @@ import { HTMLElementProps } from '@leafygreen-ui/lib'; export interface MenuGroupProps extends HTMLElementProps<'div'> { - /** - * Main text rendered in `MenuGroup`. - */ - title?: string; - - /** - * Slot to pass in an Icon rendered to the left of the text. - */ - glyph?: React.ReactElement; - /** * Content that will appear inside of MenuGroup component. * @type `` | `` | `` | `` diff --git a/packages/menu/src/MenuItem/InternalMenuItemContent.tsx b/packages/menu/src/MenuItem/InternalMenuItemContent.tsx index 887f702560..2f236e69c7 100644 --- a/packages/menu/src/MenuItem/InternalMenuItemContent.tsx +++ b/packages/menu/src/MenuItem/InternalMenuItemContent.tsx @@ -7,18 +7,15 @@ import { PolymorphicAs, useInferredPolymorphic, } from '@leafygreen-ui/polymorphic'; -import { color } from '@leafygreen-ui/tokens'; +import { color, spacing } from '@leafygreen-ui/tokens'; -import { - useMenuContext, - useMenuGroupContext, - useSubMenuContext, -} from '../MenuContext'; +import { useMenuContext } from '../MenuContext'; +import { useSubMenuContext } from '../SubMenu'; import { getDarkInLightModeMenuItemStyles, getMenuItemStyles, - getNestedMenuItemStyles, + getSubMenuItemStyles, } from './MenuItem.styles'; import { MenuItemProps, Variant } from './MenuItem.types'; @@ -56,16 +53,9 @@ export const InternalMenuItemContent = React.forwardRef< const { as } = useInferredPolymorphic(asProp, rest, 'button'); const { theme, darkMode, highlight, renderDarkMenu } = useMenuContext(); - const { depth: submenuDepth, hasIcon: submenuHasIcon } = - useSubMenuContext(); - const { depth: groupDepth, hasIcon: groupHasIcon } = useMenuGroupContext(); - const isNested = !!(submenuDepth || groupDepth); - - // @ts-expect-error - // highlighted isn't a prop on this component, but could be passed in from MenuItem. - // Generally this will not be provided, but is permitted here to support isolated visual testing in Storybook - const forceHighlight = rest.highlighted; - const highlighted = id === highlight?.id || forceHighlight; + const { depth, hasIcon: parentHasIcon } = useSubMenuContext(); + const isSubMenuItem = depth > 0; + const highlighted = id === highlight?.id; const defaultAnchorProps = as === 'a' @@ -88,7 +78,7 @@ export const InternalMenuItemContent = React.forwardRef< darkMode={darkMode} showWedge highlighted={highlighted} - data-depth={submenuDepth} + data-depth={depth} className={cx( getMenuItemStyles({ active, @@ -99,13 +89,7 @@ export const InternalMenuItemContent = React.forwardRef< }), { - [getNestedMenuItemStyles({ - theme, - submenuDepth, - submenuHasIcon, - groupDepth, - groupHasIcon, - })]: isNested, + [getSubMenuItemStyles({ theme, parentHasIcon })]: isSubMenuItem, // TODO: Remove dark-in-light mode styles // after https://jira.mongodb.org/browse/LG-3974 @@ -120,7 +104,7 @@ export const InternalMenuItemContent = React.forwardRef< &:after { background-color: ${color.dark.border.secondary.default}; } - `]: theme === 'light' && renderDarkMenu && submenuDepth > 0, + `]: theme === 'light' && renderDarkMenu && depth > 0, }, className, )} @@ -132,6 +116,13 @@ export const InternalMenuItemContent = React.forwardRef< description={description} rightGlyph={rightGlyph} preserveIconSpace={false} + className={cx({ + [css` + position: relative; + padding-left: ${parentHasIcon ? spacing[900] : spacing[600]}px; + border-top: 1px solid transparent; + `]: depth > 0, + })} >
=> (Instance, ctx) => { + const { + args: { darkMode: darkModeProp, renderDarkMenu, highlighted, ...props }, + } = ctx ?? { + args: { + darkMode: false, + renderDarkMenu: false, + highlighted: false, + }, + }; + + const ref = useRef(null); + const [testDescendant, setTestDescendant] = useState(); + useEffect(() => { + setTestDescendant({ + ref, + element: ref.current, + id: ref?.current?.getAttribute('data-id'), + index: Number(ref?.current?.getAttribute('data-index')), + } as Descendant); + }, []); + const darkMode = (renderDarkMenu || darkModeProp) ?? false; + const theme = darkMode ? Theme.Dark : Theme.Light; + + return ( + +
    + +
+
+ ); + }; + export default { title: 'Components/Menu/MenuItem', component: MenuItem, @@ -32,7 +80,7 @@ export default { combineArgs: { darkMode: [false, true], }, - decorator: withMenuContext(), + decorator: _withMenuContext(), }, }, } satisfies StoryMetaType>; @@ -63,7 +111,7 @@ export const LiveExample = { {children} ), - decorators: [withMenuContext()], + decorators: [_withMenuContext()], parameters: { chromatic: { disableSnapshot: true, diff --git a/packages/menu/src/MenuItem/MenuItem.styles.ts b/packages/menu/src/MenuItem/MenuItem.styles.ts index 2ff3296837..809c5551a0 100644 --- a/packages/menu/src/MenuItem/MenuItem.styles.ts +++ b/packages/menu/src/MenuItem/MenuItem.styles.ts @@ -1,7 +1,6 @@ import { css, cx } from '@leafygreen-ui/emotion'; import { descriptionClassName, - inputOptionContentClassName, leftGlyphClassName, titleClassName, } from '@leafygreen-ui/input-option'; @@ -132,6 +131,24 @@ export const getMenuItemStyles = ({ }, ); +export const getSubMenuItemStyles = ({ + theme, + parentHasIcon, +}: { + theme: Theme; + parentHasIcon: boolean; +}) => css` + &:after { + content: ''; + position: absolute; + top: 0; + right: 0; + left: ${parentHasIcon ? spacing[900] : spacing[600]}px; + height: 1px; + background-color: ${menuColor[theme].border.default}; + } +`; + export const getMenuItemContentStyles = ({ hasGlyph, }: { @@ -143,52 +160,6 @@ export const getMenuItemContentStyles = ({ `} `; -interface NestedItemStyleArgs { - theme: Theme; - submenuDepth: number; - groupDepth: number; - submenuHasIcon: boolean; - groupHasIcon: boolean; -} - -/** Styling for nested items */ -export const getNestedMenuItemStyles = ({ - theme, - submenuDepth, - submenuHasIcon, - groupDepth, - groupHasIcon, -}: NestedItemStyleArgs) => { - const submenuInset = - submenuDepth * (submenuHasIcon ? spacing[1000] : spacing[300]); - const groupInset = groupDepth * (groupHasIcon ? spacing[600] : spacing[300]); - const totalInset = submenuInset + groupInset; - - return cx( - { - // The inset border for submenu items - [css` - &:after { - content: ''; - position: absolute; - top: 0; - right: 0; - left: ${totalInset}px; - height: 1px; - background-color: ${menuColor[theme].border.default}; - } - `]: submenuDepth > 0, - }, - css` - .${inputOptionContentClassName} { - position: relative; - padding-left: ${totalInset}px; - border-top: 1px solid transparent; - } - `, - ); -}; - // TODO: Remove dark-in-light mode styles // after https://jira.mongodb.org/browse/LG-3974 export const getDarkInLightModeMenuItemStyles = ({ diff --git a/packages/menu/src/SubMenu/SubMenu.tsx b/packages/menu/src/SubMenu/SubMenu.tsx index 6df913f4ac..5e6895eb3e 100644 --- a/packages/menu/src/SubMenu/SubMenu.tsx +++ b/packages/menu/src/SubMenu/SubMenu.tsx @@ -20,12 +20,7 @@ import { } from '@leafygreen-ui/polymorphic'; import { LGIDs } from '../constants'; -import { - MenuDescendantsContext, - SubMenuProvider, - useMenuContext, - useSubMenuContext, -} from '../MenuContext'; +import { MenuDescendantsContext, useMenuContext } from '../MenuContext'; import { InternalMenuItemContent } from '../MenuItem/InternalMenuItemContent'; import { @@ -36,6 +31,7 @@ import { submenuToggleStyles, } from './SubMenu.styles'; import { InternalSubMenuProps } from './SubMenu.types'; +import { SubMenuProvider, useSubMenuContext } from './SubMenuContext'; import { useChildrenHeight } from './useChildrenHeight'; import { useControlledState } from './useControlledState'; diff --git a/packages/menu/src/SubMenu/index.ts b/packages/menu/src/SubMenu/index.ts index 35c15083e7..3e99bbf493 100644 --- a/packages/menu/src/SubMenu/index.ts +++ b/packages/menu/src/SubMenu/index.ts @@ -1,2 +1,3 @@ export { SubMenu } from './SubMenu'; export { InternalSubMenuProps, SubMenuProps } from './SubMenu.types'; +export { useSubMenuContext } from './SubMenuContext'; diff --git a/packages/menu/src/testUtils/withMenuContextDecorator.testutils.tsx b/packages/menu/src/testUtils/withMenuContextDecorator.testutils.tsx deleted file mode 100644 index 710befdd6e..0000000000 --- a/packages/menu/src/testUtils/withMenuContextDecorator.testutils.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/* eslint-disable react/jsx-key, react/display-name, react-hooks/rules-of-hooks */ -import React from 'react'; -import { InstanceDecorator } from '@lg-tools/storybook-utils'; - -import { css } from '@leafygreen-ui/emotion'; -import { Theme } from '@leafygreen-ui/lib'; - -import { Menu } from '../Menu'; -import { MenuContext } from '../MenuContext'; -import { MenuItem } from '../MenuItem'; - -/** - * Implements a MenuContext wrapper around each `MenuItem`, `SubMenu` or `MenuGroup` - */ -export const withMenuContext = - (): InstanceDecorator => (Instance, ctx) => { - const { - args: { darkMode: darkModeProp, renderDarkMenu }, - } = ctx ?? { - args: { - darkMode: false, - renderDarkMenu: false, - }, - }; - - const darkMode = (renderDarkMenu || darkModeProp) ?? false; - const theme = darkMode ? Theme.Dark : Theme.Light; - - return ( -
- - - -
- ); - }; diff --git a/packages/menu/tsconfig.json b/packages/menu/tsconfig.json index ef8bcaf4c9..8441ac11ab 100644 --- a/packages/menu/tsconfig.json +++ b/packages/menu/tsconfig.json @@ -48,9 +48,6 @@ { "path": "../tokens" }, - { - "path": "../typography" - }, { "path": "../leafygreen-provider" }