diff --git a/apps/docs/src/data/structures/components.js b/apps/docs/src/data/structures/components.js index f21e4e22e2..2a102dffdf 100644 --- a/apps/docs/src/data/structures/components.js +++ b/apps/docs/src/data/structures/components.js @@ -864,7 +864,7 @@ export const components = [ accessibility: 'Passed WCAG 2.2 AA', category: 'Controls', description: - 'Menu is a component that contains a list of actions. When a user clicks an item in the menu, the menu closes and the action is executed.', + 'A menu presents a set of related actions from which a user may choose.', seoDescription: 'Menu contains a list of actions. See some best UX practices .', sections: [ diff --git a/apps/docs/src/examples/components/menu/MenuAnatomy.js b/apps/docs/src/examples/components/menu/MenuAnatomy.js new file mode 100644 index 0000000000..2d5cd2a28c --- /dev/null +++ b/apps/docs/src/examples/components/menu/MenuAnatomy.js @@ -0,0 +1,187 @@ +import React, { useContext } from 'react'; +import { Box, Grid, Diagram, Stack, ThemeContext } from 'grommet'; +import { Add } from '@hpe-design/icons-grommet'; +import { Annotation } from '../../../layouts'; +import { + MenuMockButton, + MenuMockContainer, + MenuMockItem, + MenuMockGroup, +} from './MenuMock'; + +const color = 'border'; +const thickness = 'hair'; +const type = 'direct'; + +const connections = [ + { + anchor: 'horizontal', + type, + color, + thickness, + fromTarget: '1', + toTarget: 'menu-button', + }, + { + anchor: 'vertical', + type, + color, + thickness, + fromTarget: '1a', + toTarget: 'menu-label', + }, + { + anchor: 'vertical', + type, + color, + thickness, + fromTarget: '1b', + toTarget: 'menu-icon', + }, + { + anchor: 'horizontal', + type, + color, + thickness, + fromTarget: '2', + toTarget: 'drop-container', + }, + { + anchor: 'horizontal', + type, + color, + thickness, + fromTarget: '3', + toTarget: 'menu-item-target', + }, + { + anchor: 'vertical', + type, + color, + thickness, + fromTarget: '3a', + toTarget: 'item-label', + }, + { + anchor: 'vertical', + type, + color, + thickness, + fromTarget: '3b', + toTarget: 'item-icon', + }, + { + anchor: 'horizontal', + type, + color, + thickness, + fromTarget: '4', + toTarget: 'divider-target', + }, +]; + +const AnatomyGrid = ({ ...rest }) => ( + +); + +export const MenuAnatomy = () => { + const theme = useContext(ThemeContext); + const menuTheme = theme?.menu || {}; + const dropTheme = theme?.global.drop || {}; + + const annotations = [ + { id: '1', gridArea: 'annotation-1', target: '1' }, + { id: '1a', gridArea: 'annotation-1a', target: '1a' }, + { id: '1b', gridArea: 'annotation-1b', target: '1b' }, + { id: '2', gridArea: 'annotation-2', target: '2' }, + { id: '3', gridArea: 'annotation-3', target: '3' }, + { id: '3a', gridArea: 'annotation-3a', target: '3a' }, + { id: '3b', gridArea: 'annotation-3b', target: '3b' }, + { id: '4', gridArea: 'annotation-4', target: '4' }, + ]; + + return ( + + + {annotations.map(({ id, gridArea, target, ...rest }) => ( + + ))} + + {/* Menu anatomy mockup */} + + {/* Adding bounding box to add dashed outline for clarity */} + + Menu} + icon={{menuTheme.icons?.down.render()}} + /> + + + + + + + + + + } label="Action" /> + {/* Adding bounding box to add dashed outline for clarity */} + + Action} + icon={} + reverse + /> + + + + + + + + ); +}; diff --git a/apps/docs/src/examples/components/menu/MenuDangerousExample.js b/apps/docs/src/examples/components/menu/MenuDangerousExample.js index 0f2e060653..d87194268e 100644 --- a/apps/docs/src/examples/components/menu/MenuDangerousExample.js +++ b/apps/docs/src/examples/components/menu/MenuDangerousExample.js @@ -1,17 +1,17 @@ import PropTypes from 'prop-types'; -import { Menu, Notification } from 'grommet'; +import { Notification } from 'grommet'; import { useState } from 'react'; import { DestructiveConfirmation } from '../../templates'; +import { MenuMock } from './MenuMock'; export const MenuDangerousExample = ({ bestPractice = true }) => { const [showModal, setShowModal] = useState(false); const [toast, setToast] = useState(false); const items = [ - { label: 'View details', onClick: () => {} }, - { label: 'Edit profile', onClick: () => {} }, - { label: 'Apply blueprint', onClick: () => {} }, + { label: 'Edit' }, + { label: 'View servers' }, { label: 'Delete', onClick: () => { @@ -23,9 +23,8 @@ export const MenuDangerousExample = ({ bestPractice = true }) => { return ( <> - { /> {showModal && ( @@ -46,7 +45,7 @@ export const MenuDangerousExample = ({ bestPractice = true }) => { setToast(false)} /> )} diff --git a/apps/docs/src/examples/components/menu/MenuDefaultExample.js b/apps/docs/src/examples/components/menu/MenuDefaultExample.js new file mode 100644 index 0000000000..f055bb33a4 --- /dev/null +++ b/apps/docs/src/examples/components/menu/MenuDefaultExample.js @@ -0,0 +1,12 @@ +import React from 'react'; +import { Menu } from 'grommet'; + +export const MenuDefaultExample = () => { + const items = [ + { label: 'Edit' }, + { label: 'View servers' }, + { label: 'Add servers' }, + ]; + + return ; +}; diff --git a/apps/docs/src/examples/components/menu/MenuGroupingExample.js b/apps/docs/src/examples/components/menu/MenuGroupingExample.js index ff5489f097..3aced31861 100644 --- a/apps/docs/src/examples/components/menu/MenuGroupingExample.js +++ b/apps/docs/src/examples/components/menu/MenuGroupingExample.js @@ -1,21 +1,31 @@ import PropTypes from 'prop-types'; -import { Menu } from 'grommet'; +import { MenuMock } from './MenuMock'; export const MenuGroupingExample = ({ bestPractice = true }) => ( - {} }, - { label: 'Add to group', onClick: () => {} }, - { label: 'Update firmware', onClick: () => {} }, + [ + { label: 'Edit' }, + { + label: 'View servers', + }, + { label: 'Add servers' }, + { label: 'Remove servers' }, + ], + [{ label: 'Update firmware' }, { label: 'Update BIOS settings' }], + [{ label: 'Delete' }], ] : [ - [{ label: 'View', onClick: () => {} }], - [{ label: 'Add to group', onClick: () => {} }], - [{ label: 'Update firmware', onClick: () => {} }], + [{ label: 'Edit' }], + [{ label: 'View servers' }], + [{ label: 'Add servers' }], + [{ label: 'Remove servers' }], + [{ label: 'Update firmware' }], + [{ label: 'Update BIOS settings' }], + [{ label: 'Delete' }], ] } /> diff --git a/apps/docs/src/examples/components/menu/MenuIconExample.js b/apps/docs/src/examples/components/menu/MenuIconExample.js index 96801fe8eb..2d97275ea9 100644 --- a/apps/docs/src/examples/components/menu/MenuIconExample.js +++ b/apps/docs/src/examples/components/menu/MenuIconExample.js @@ -1,31 +1,26 @@ import React from 'react'; -import { Box, List, Menu } from 'grommet'; -import { More } from '@hpe-design/icons-grommet'; +import { Box, Menu } from 'grommet'; +import { More, Settings } from '@hpe-design/icons-grommet'; export const MenuIconExample = () => { - const data = ['User 1', 'User 2', 'User 3']; - return ( - - ( - - } - hoverIndicator - items={[{ label: 'Deactivate' }, { label: 'Suspend' }]} - /> - - )} - > - {(datum, index) => ( - - {datum} - - )} - + + } + items={[ + { label: 'Edit' }, + { label: 'View servers' }, + { label: 'Add servers' }, + ]} + /> + } + items={[ + { label: 'Edit preferences' }, + { label: 'View system details' }, + { label: 'Reset system settings' }, + ]} + /> ); }; diff --git a/apps/docs/src/examples/components/menu/MenuItemCountExample.js b/apps/docs/src/examples/components/menu/MenuItemCountExample.js new file mode 100644 index 0000000000..76109462cd --- /dev/null +++ b/apps/docs/src/examples/components/menu/MenuItemCountExample.js @@ -0,0 +1,35 @@ +import PropTypes from 'prop-types'; +import { MenuMock } from './MenuMock'; + +export const MenuItemCountExample = ({ bestPractice = true }) => ( + +); + +MenuItemCountExample.propTypes = { + bestPractice: PropTypes.bool, +}; diff --git a/apps/docs/src/examples/components/menu/MenuMock.js b/apps/docs/src/examples/components/menu/MenuMock.js new file mode 100644 index 0000000000..2e3d4f1cca --- /dev/null +++ b/apps/docs/src/examples/components/menu/MenuMock.js @@ -0,0 +1,127 @@ +/* eslint-disable react/prop-types */ +import React, { useContext } from 'react'; +import { Box, Button, ThemeContext } from 'grommet'; + +/** + * Shared MenuMock components that can be composed + * to replicate Menu component visual presentation + */ +export const MenuMockItem = ({ label, icon, reverse, ...rest }) => { + const theme = useContext(ThemeContext); + + return ( +