Skip to content

Commit

Permalink
Add MenuItemGroup component (#1231)
Browse files Browse the repository at this point in the history
This PR adds a GroupSection component which can be used in the Menu
component.

---------

Co-authored-by: Ricky James Smith <jamesricky@me.com>
Co-authored-by: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 2, 2024
1 parent 47eb81c commit f06f4be
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 75 deletions.
16 changes: 16 additions & 0 deletions .changeset/short-donkeys-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
"@comet/admin": minor
---

Add `MenuItemGroup` component to group menu items

**Example:**

```tsx
<MenuItemGroup title="Some item group title">
<MenuItemRouterLink primary="Menu item 1" icon={<Settings />} to="/menu-item-1" />
<MenuItemRouterLink primary="Menu item 2" icon={<Settings />} to="/menu-item-2" />
<MenuItemRouterLink primary="Menu item 3" icon={<Settings />} to="/menu-item-3" />
{ /* Some more menu items... */ }
</MenuItemGroup>
```
126 changes: 70 additions & 56 deletions demo/admin/src/common/MasterMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Menu, MenuCollapsibleItem, MenuContext, MenuItemRouterLink, useWindowSize } from "@comet/admin";
import { Menu, MenuCollapsibleItem, MenuContext, MenuItemGroup, MenuItemRouterLink, useWindowSize } from "@comet/admin";
import { Assets, Dashboard, Data, PageTree, Snips, Wrench } from "@comet/admin-icons";
import { useContentScope } from "@comet/cms-admin";
import * as React from "react";
import { useIntl } from "react-intl";
import { useRouteMatch } from "react-router";
Expand All @@ -11,7 +12,7 @@ const MasterMenu: React.FC = () => {
const windowSize = useWindowSize();
const intl = useIntl();
const match = useRouteMatch();

const { scope } = useContentScope();
const useTemporaryMenu: boolean = windowSize.width < permanentMenuMinWidth;

// Open menu when changing to permanent variant and close when changing to temporary variant.
Expand All @@ -23,71 +24,84 @@ const MasterMenu: React.FC = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [location]);

const sectionScopeTitle = `${scope?.domain.charAt(0).toUpperCase() + scope?.domain.slice(1)} - ${scope?.language?.toUpperCase()}`;

return (
<Menu variant={useTemporaryMenu ? "temporary" : "permanent"}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.dashboard", defaultMessage: "Dashboard" })}
icon={<Dashboard />}
to={`${match.url}/dashboard`}
/>
<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.pageTree", defaultMessage: "Page tree" })} icon={<PageTree />}>
<MenuItemGroup title={sectionScopeTitle}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.pageTree.mainNavigation", defaultMessage: "Main navigation" })}
to={`${match.url}/pages/pagetree/main-navigation`}
primary={intl.formatMessage({ id: "menu.dashboard", defaultMessage: "Dashboard" })}
icon={<Dashboard />}
to={`${match.url}/dashboard`}
/>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.pageTree.topMenu", defaultMessage: "Top menu" })}
to={`${match.url}/pages/pagetree/top-menu`}
/>
</MenuCollapsibleItem>
<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.pageTree", defaultMessage: "Page tree" })} icon={<PageTree />}>
<MenuItemRouterLink
primary={intl.formatMessage({
id: "menu.pageTree.mainNavigation",
defaultMessage: "Main navigation",
})}
to={`${match.url}/pages/pagetree/main-navigation`}
/>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.pageTree.topMenu", defaultMessage: "Top menu" })}
to={`${match.url}/pages/pagetree/top-menu`}
/>
</MenuCollapsibleItem>

<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.structuredContent", defaultMessage: "Structured Content" })} icon={<Data />}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.news", defaultMessage: "News" })}
to={`${match.url}/structured-content/news`}
/>
</MenuCollapsibleItem>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.dam", defaultMessage: "Assets" })}
icon={<Assets />}
to={`${match.url}/assets`}
/>
<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.projectSnips", defaultMessage: "Project snips" })} icon={<Snips />}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.mainMenu", defaultMessage: "Main menu" })}
to={`${match.url}/project-snips/main-menu`}
/>
</MenuCollapsibleItem>
<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.system", defaultMessage: "System" })} icon={<Wrench />}>
<MenuCollapsibleItem
primary={intl.formatMessage({ id: "menu.structuredContent", defaultMessage: "Structured Content" })}
icon={<Data />}
>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.news", defaultMessage: "News" })}
to={`${match.url}/structured-content/news`}
/>
</MenuCollapsibleItem>

<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.projectSnips", defaultMessage: "Project snips" })} icon={<Snips />}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.mainMenu", defaultMessage: "Main menu" })}
to={`${match.url}/project-snips/main-menu`}
/>
</MenuCollapsibleItem>
<MenuCollapsibleItem primary="Products" icon={<Snips />}>
<MenuItemRouterLink primary="Products" to={`${match.url}/products`} icon={<Snips />} />
<MenuItemRouterLink primary="Categories" to={`${match.url}/product-categories`} icon={<Snips />} />
<MenuItemRouterLink primary="Tags" to={`${match.url}/product-tags`} icon={<Snips />} />
<MenuItemRouterLink primary="Products Handmade" to={`${match.url}/products-handmade`} icon={<Snips />} />
</MenuCollapsibleItem>
</MenuItemGroup>
<MenuItemGroup title={intl.formatMessage({ id: "menu.section.furtherLayers", defaultMessage: "Further layers" })}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.publisher", defaultMessage: "Publisher" })}
to={`${match.url}/system/publisher`}
primary={intl.formatMessage({ id: "menu.dam", defaultMessage: "Assets" })}
icon={<Assets />}
to={`${match.url}/assets`}
/>
<MenuCollapsibleItem primary={intl.formatMessage({ id: "menu.system", defaultMessage: "System" })} icon={<Wrench />}>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.publisher", defaultMessage: "Publisher" })}
to={`${match.url}/system/publisher`}
/>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.cronJobs", defaultMessage: "Cron Jobs" })}
to={`${match.url}/system/cron-jobs`}
/>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.redirects", defaultMessage: "Redirects" })}
to={`${match.url}/system/redirects`}
/>
</MenuCollapsibleItem>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.cronJobs", defaultMessage: "Cron Jobs" })}
to={`${match.url}/system/cron-jobs`}
primary={intl.formatMessage({ id: "menu.componentDemo", defaultMessage: "Component demo" })}
to={`${match.url}/component-demo`}
icon={<Snips />}
/>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.redirects", defaultMessage: "Redirects" })}
to={`${match.url}/system/redirects`}
primary={intl.formatMessage({ id: "menu.userPermissions", defaultMessage: "User Permissions" })}
to={`${match.url}/user-permissions`}
icon={<Snips />}
/>
</MenuCollapsibleItem>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.componentDemo", defaultMessage: "Component demo" })}
to={`${match.url}/component-demo`}
icon={<Snips />}
/>
<MenuCollapsibleItem primary="Products" icon={<Snips />}>
<MenuItemRouterLink primary="Products" to={`${match.url}/products`} icon={<Snips />} />
<MenuItemRouterLink primary="Categories" to={`${match.url}/product-categories`} icon={<Snips />} />
<MenuItemRouterLink primary="Tags" to={`${match.url}/product-tags`} icon={<Snips />} />
<MenuItemRouterLink primary="Products Handmade" to={`${match.url}/products-handmade`} icon={<Snips />} />
</MenuCollapsibleItem>
<MenuItemRouterLink
primary={intl.formatMessage({ id: "menu.userPermissions", defaultMessage: "User Permissions" })}
to={`${match.url}/user-permissions`}
icon={<Snips />}
/>
</MenuItemGroup>
</Menu>
);
};
Expand Down
41 changes: 23 additions & 18 deletions packages/admin/admin-stories/src/admin/mui/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
Menu,
MenuCollapsibleItem,
MenuItemAnchorLink,
MenuItemGroup,
MenuItemRouterLink,
useWindowSize,
} from "@comet/admin";
Expand All @@ -26,24 +27,28 @@ const AppMenu: React.FC = () => {

return (
<Menu variant={windowSize.width < permanentMenuMinWidth ? "temporary" : "permanent"}>
<MenuItemRouterLink primary="Dashboard" icon={<Dashboard />} to="/dashboard" />
<MenuItemRouterLink primary="Settings" icon={<Settings />} to="/settings" />
<MenuCollapsibleItem primary="More Items" icon={<Sort />}>
<MenuItemRouterLink primary="Foo1" to="/foo1" />
<MenuItemRouterLink primary="Foo2" to="/foo2" />
</MenuCollapsibleItem>
<MenuCollapsibleItem primary="Even More Items" icon={<Sort />}>
<MenuItemRouterLink primary="Foo3" to="/foo3" />
<MenuItemRouterLink primary="Foo4" to="/foo4" />
</MenuCollapsibleItem>
<MenuItemAnchorLink
primary="Comet Admin"
secondary="View on GitHub"
target="_blank"
href="https://github.com/vivid-planet/comet"
icon={<CometColor />}
secondaryAction={<LinkExternal />}
/>
<MenuItemGroup title="Some Group">
<MenuItemRouterLink primary="Dashboard" icon={<Dashboard />} to="/dashboard" />
<MenuCollapsibleItem primary="More Items" icon={<Sort />}>
<MenuItemRouterLink primary="Foo1" to="/foo1" />
<MenuItemRouterLink primary="Foo2" to="/foo2" />
</MenuCollapsibleItem>
<MenuCollapsibleItem primary="Even More Items" icon={<Sort />}>
<MenuItemRouterLink primary="Foo3" to="/foo3" />
<MenuItemRouterLink primary="Foo4" to="/foo4" />
</MenuCollapsibleItem>
</MenuItemGroup>
<MenuItemGroup title="Further Layers">
<MenuItemRouterLink primary="Settings" icon={<Settings />} to="/settings" />
<MenuItemAnchorLink
primary="Comet Admin"
secondary="View on GitHub"
target="_blank"
href="https://github.com/vivid-planet/comet"
icon={<CometColor />}
secondaryAction={<LinkExternal />}
/>
</MenuItemGroup>
</Menu>
);
};
Expand Down
1 change: 1 addition & 0 deletions packages/admin/admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export { MenuCollapsibleItem, MenuCollapsibleItemClassKey, MenuCollapsibleItemPr
export { IMenuContext, IWithMenu, MenuContext, withMenu } from "./mui/menu/Context";
export { MenuItem, MenuItemClassKey, MenuItemProps } from "./mui/menu/Item";
export { MenuItemAnchorLink } from "./mui/menu/ItemAnchorLink";
export { MenuItemGroup, MenuItemGroupClassKey, MenuItemGroupProps } from "./mui/menu/ItemGroup";
export { MenuItemRouterLink, MenuItemRouterLinkProps } from "./mui/menu/ItemRouterLink";
export { Menu, MenuProps } from "./mui/menu/Menu";
export { MenuClassKey, styles } from "./mui/menu/Menu.styles";
Expand Down
51 changes: 51 additions & 0 deletions packages/admin/admin/src/mui/menu/ItemGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Box, ComponentsOverrides, Theme, Typography } from "@mui/material";
import { createStyles, WithStyles, withStyles } from "@mui/styles";
import * as React from "react";

export type MenuItemGroupClassKey = "root" | "title";

const styles = (theme: Theme) =>
createStyles<MenuItemGroupClassKey, MenuItemGroupProps>({
root: { marginTop: theme.spacing(8) },
title: {
fontWeight: theme.typography.fontWeightBold,
fontSize: 14,
lineHeight: "20px",
borderBottom: `1px solid ${theme.palette.grey[50]}`,
padding: theme.spacing(2, 4),
},
});

export interface MenuItemGroupProps {
title?: React.ReactNode;
}

const ItemGroup: React.FC<React.PropsWithChildren<WithStyles<typeof styles> & MenuItemGroupProps>> = ({ title, children, classes }) => {
return (
<Box className={classes.root}>
<Typography className={classes.title} variant="h3">
{title}
</Typography>
{children}
</Box>
);
};

export const MenuItemGroup = withStyles(styles, { name: "CometAdminMenuItemGroup" })(ItemGroup);

declare module "@mui/material/styles" {
interface ComponentsPropsList {
CometAdminMenuItemGroup: MenuItemGroupProps;
}

interface ComponentNameToClassKey {
CometAdminMenuItemGroup: MenuItemGroupClassKey;
}

interface Components {
CometAdminMenuItemGroup?: {
defaultProps?: ComponentsPropsList["CometAdminMenuItemGroup"];
styleOverrides?: ComponentsOverrides<Theme>["CometAdminMenuItemGroup"];
};
}
}
2 changes: 1 addition & 1 deletion packages/admin/admin/src/mui/menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ declare module "@mui/material/styles" {
}

interface Components {
CometAdminenu?: {
CometAdminMenu?: {
defaultProps?: ComponentsPropsList["CometAdminMenu"];
styleOverrides?: ComponentsOverrides<Theme>["CometAdminMenu"];
};
Expand Down

0 comments on commit f06f4be

Please sign in to comment.