diff --git a/documentation-site/examples/menu/dividers.js b/documentation-site/examples/menu/dividers.js new file mode 100644 index 0000000000..bd03ab87a7 --- /dev/null +++ b/documentation-site/examples/menu/dividers.js @@ -0,0 +1,38 @@ +// @flow +import * as React from 'react'; +import {StatefulMenu} from 'baseui/menu'; + +const ITEMS = [ + {label: 'Menu option A'}, + {label: 'Menu option B'}, + {divider: true}, + {label: 'Menu option X'}, + {label: 'Menu option Y'}, + {label: 'Menu option Z'}, + {divider: true}, + {label: 'Menu option 1'}, + {label: 'Menu option 2'}, + {label: 'Menu option 3'}, +]; + +export default function Example() { + return ( + item.label, + }, + }, + }} + /> + ); +} diff --git a/documentation-site/examples/menu/dividers.tsx b/documentation-site/examples/menu/dividers.tsx new file mode 100644 index 0000000000..de19968608 --- /dev/null +++ b/documentation-site/examples/menu/dividers.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import {StatefulMenu} from 'baseui/menu'; + +const ITEMS = [ + {label: 'Menu option A'}, + {label: 'Menu option B'}, + {divider: true}, + {label: 'Menu option X'}, + {label: 'Menu option Y'}, + {label: 'Menu option Z'}, + {divider: true}, + {label: 'Menu option 1'}, + {label: 'Menu option 2'}, + {label: 'Menu option 3'}, +]; + +export default function Example() { + return ( + item.label, + }, + }, + }} + /> + ); +} diff --git a/documentation-site/pages/components/menu.mdx b/documentation-site/pages/components/menu.mdx index 81fd453852..626a2da959 100644 --- a/documentation-site/pages/components/menu.mdx +++ b/documentation-site/pages/components/menu.mdx @@ -18,6 +18,7 @@ import ChildRenderAll from 'examples/menu/child-render-all.js'; import Grouped from 'examples/menu/grouped.js'; import VirtualList from 'examples/menu/virtual-list.js'; import Href from 'examples/menu/href.js'; +import Dividers from 'examples/menu/dividers.js'; import Overrides from '../../components/overrides'; import { StatefulMenu, OptionProfile } from 'baseui/menu'; @@ -96,6 +97,10 @@ The provided id will be set as a value for the item container's `id` attribute t + + + + ## Overrides item.label, + }, + }, + }} + /> + ); +} diff --git a/src/menu/__tests__/menu.stories.tsx b/src/menu/__tests__/menu.stories.tsx index 8aa864cc44..268892caf2 100644 --- a/src/menu/__tests__/menu.stories.tsx +++ b/src/menu/__tests__/menu.stories.tsx @@ -15,6 +15,7 @@ import { Scenario as MenuStateful } from './menu-stateful.scenario'; import { Scenario as MenuVirtualized } from './menu-virtualized.scenario'; import { Scenario as MenuDefault } from './menu.scenario'; import { Scenario as MenuProfileMenu } from './menu-profile-menu.scenario'; +import { Scenario as MenuDividers } from './menu-dividers.scenario'; export const ChildInPopover = () => ; export const ChildRenderAll = () => ; @@ -26,3 +27,4 @@ export const Stateful = () => ; export const Virtualized = () => ; export const Menu = () => ; export const ProfileMenu = () => ; +export const Dividers = () => ; diff --git a/src/menu/index.js.flow b/src/menu/index.js.flow index 93fb3f2f4f..b1be038cbd 100644 --- a/src/menu/index.js.flow +++ b/src/menu/index.js.flow @@ -25,6 +25,7 @@ export { StyledProfileTitle, StyledProfileSubtitle, StyledProfileBody, + StyledMenuDivider, } from './styled-components.js'; // Flow export type * from './types.js'; diff --git a/src/menu/index.ts b/src/menu/index.ts index 6a28e47e35..bc67c124a4 100644 --- a/src/menu/index.ts +++ b/src/menu/index.ts @@ -40,6 +40,7 @@ export { StyledProfileTitle, StyledProfileSubtitle, StyledProfileBody, + StyledMenuDivider, } from './styled-components'; // Flow export * from './types'; diff --git a/src/menu/menu.js.flow b/src/menu/menu.js.flow index 9253e38ebb..265658681d 100644 --- a/src/menu/menu.js.flow +++ b/src/menu/menu.js.flow @@ -8,7 +8,12 @@ LICENSE file in the root directory of this source tree. import * as React from 'react'; import { LocaleContext } from '../locale/index.js'; // Components -import { StyledList, StyledEmptyState, StyledOptgroupHeader } from './styled-components.js'; +import { + StyledList, + StyledEmptyState, + StyledOptgroupHeader, + StyledMenuDivider, +} from './styled-components.js'; import OptionList from './option-list.js'; import { getOverrides } from '../helpers/overrides.js'; // Types @@ -49,6 +54,7 @@ export default function Menu(props: StatelessMenuPropsT) { overrides.OptgroupHeader, StyledOptgroupHeader ); + const [MenuDivider, menuDividerProps] = getOverrides(overrides.MenuDivider, StyledMenuDivider); const groupedItems = Array.isArray(props.items) ? { __ungrouped: props.items } : props.items; const optgroups = Object.keys(groupedItems); @@ -65,6 +71,10 @@ export default function Menu(props: StatelessMenuPropsT) { itemIndex = itemIndex + 1; const { getRequiredItemProps = (item, index) => ({}) } = props; + if (item.divider === true) { + return ; + } + const { disabled, isFocused, diff --git a/src/menu/menu.tsx b/src/menu/menu.tsx index 0c6d2c4f2a..e5cd8addb1 100644 --- a/src/menu/menu.tsx +++ b/src/menu/menu.tsx @@ -7,7 +7,12 @@ LICENSE file in the root directory of this source tree. import * as React from 'react'; import { LocaleContext } from '../locale'; // Components -import { StyledList, StyledEmptyState, StyledOptgroupHeader } from './styled-components'; +import { + StyledList, + StyledEmptyState, + StyledOptgroupHeader, + StyledMenuDivider, +} from './styled-components'; import OptionList from './option-list'; import { getOverrides } from '../helpers/overrides'; // Types @@ -52,6 +57,7 @@ export default function Menu(props: StatelessMenuProps) { overrides.OptgroupHeader, StyledOptgroupHeader ); + const [MenuDivider, menuDividerProps] = getOverrides(overrides.MenuDivider, StyledMenuDivider); const groupedItems = Array.isArray(props.items) ? { __ungrouped: props.items } : props.items; const optgroups = Object.keys(groupedItems); @@ -70,6 +76,10 @@ export default function Menu(props: StatelessMenuProps) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { getRequiredItemProps = (item, index) => ({} as RenderItemProps) } = props; + if (item.divider === true) { + return ; + } + const { disabled, isFocused, diff --git a/src/menu/styled-components.js.flow b/src/menu/styled-components.js.flow index 70cb69fd86..e55baaa50a 100644 --- a/src/menu/styled-components.js.flow +++ b/src/menu/styled-components.js.flow @@ -210,3 +210,16 @@ export const StyledProfileBody = styled('p', ({ $theme }) => ({ marginLeft: 0, marginRight: 0, })); + +export const StyledMenuDivider = styled('li', ({ $theme }) => ({ + color: $theme.colors.contentPrimary, + borderBottomWidth: $theme.borders.border300.borderWidth, + borderBottomStyle: $theme.borders.border300.borderStyle, + borderBottomColor: $theme.borders.border300.borderColor, + marginTop: $theme.sizing.scale100, + marginBottom: $theme.sizing.scale100, + marginLeft: $theme.sizing.scale600, + marginRight: $theme.sizing.scale600, + listStyle: 'none', + height: 0, +})); diff --git a/src/menu/styled-components.tsx b/src/menu/styled-components.tsx index ba96c1392a..d6b2914e69 100644 --- a/src/menu/styled-components.tsx +++ b/src/menu/styled-components.tsx @@ -232,3 +232,17 @@ export const StyledProfileBody = styled<'p', StyledProps>('p', ({ $theme }) => ( marginRight: 0, })); StyledProfileBody.displayName = 'StyledProfileBody'; + +export const StyledMenuDivider = styled<'li', StyledProps>('li', ({ $theme }) => ({ + color: $theme.colors.contentPrimary, + borderBottomWidth: $theme.borders.border300.borderWidth, + borderBottomStyle: $theme.borders.border300.borderStyle, + borderBottomColor: $theme.borders.border300.borderColor, + marginTop: $theme.sizing.scale100, + marginBottom: $theme.sizing.scale100, + marginLeft: $theme.sizing.scale500, + marginRight: $theme.sizing.scale500, + listStyle: 'none', + height: 0, +})); +StyledProfileBody.displayName = 'StyledMenuDivider'; diff --git a/src/menu/types.js.flow b/src/menu/types.js.flow index de57d5c008..fb30fd7c9f 100644 --- a/src/menu/types.js.flow +++ b/src/menu/types.js.flow @@ -144,6 +144,7 @@ export type MenuPropsT = { Option?: OverrideT, OptgroupHeader?: OverrideT, ListItem?: OverrideT, + MenuDivider?: OverrideT; }, /** Renders all menu content for SEO purposes regardless of menu state */ renderAll?: boolean, diff --git a/src/menu/types.ts b/src/menu/types.ts index d40b3d570f..685b26228a 100644 --- a/src/menu/types.ts +++ b/src/menu/types.ts @@ -153,6 +153,7 @@ export type MenuOverrides = { Option?: Override; OptgroupHeader?: Override; ListItem?: Override; + MenuDivider?: Override; }; export type MenuProps = { overrides?: MenuOverrides; diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-dark-chromium-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-dark-chromium-linux.png new file mode 100644 index 0000000000..7d9994e21d Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-dark-chromium-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-dark-firefox-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-dark-firefox-linux.png new file mode 100644 index 0000000000..39529af8db Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-dark-firefox-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-dark-webkit-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-dark-webkit-linux.png new file mode 100644 index 0000000000..193e2624aa Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-dark-webkit-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-chromium-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-chromium-linux.png new file mode 100644 index 0000000000..64ad27b8df Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-chromium-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-firefox-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-firefox-linux.png new file mode 100644 index 0000000000..0633e28f6c Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-firefox-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-webkit-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-webkit-linux.png new file mode 100644 index 0000000000..93c8a200ba Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-desktop-webkit-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-chromium-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-chromium-linux.png new file mode 100644 index 0000000000..b3d0a53c9f Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-chromium-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-firefox-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-firefox-linux.png new file mode 100644 index 0000000000..afa15b3ea4 Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-firefox-linux.png differ diff --git a/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-webkit-linux.png b/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-webkit-linux.png new file mode 100644 index 0000000000..b16c5918ce Binary files /dev/null and b/vrt/tests.vrt.js-snapshots/menu--dividers-mobile-webkit-linux.png differ