Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add <Menu.Item>content</Menu.item> syntax #9242

Merged
merged 7 commits into from
Sep 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,15 @@ You may have declared a resource without `list` prop. But with the default menu,
</Admin>
```

In order to have a specific resource without `list` prop listed on the menu, you have to [write your own custom menu](./Layout.md#menu).
In order to have a specific resource without `list` prop listed on the menu, you have to [write your own custom menu](./Menu.md).

```jsx
const MyMenu = ({ resources, onMenuClick }) => (
<div>
{resources.map(resource => (
<MenuItemLink to={`/${resource.name}`} primaryText={resource.name} onClick={onMenuClick} />
))}
<MenuItemLink to="/reference/create" primaryText="New Reference" onClick={onMenuClick} />
</div>
import { Menu } from 'react-admin';

export const MyMenu = () => (
<Menu>
<Menu.ResourceItems />
<Menu.Item to="/reference/create" primaryText="New Reference" />
</Menu>
);
```
41 changes: 32 additions & 9 deletions docs/Menu.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export const MyMenu = () => (
<Menu.ResourceItem name="posts" />
<Menu.ResourceItem name="comments" />
<Menu.ResourceItem name="users" />
<Menu.Item to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />}/>
<Menu.Item to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />} />
</Menu>
);
```
Expand Down Expand Up @@ -117,7 +117,7 @@ export const MyMenu = () => (
<Menu.ResourceItem name="posts" />
<Menu.ResourceItem name="comments" />
<Menu.ResourceItem name="users" />
<Menu.Item to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />}/>
<Menu.Item to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />} />
</Menu>
);
```
Expand Down Expand Up @@ -161,35 +161,54 @@ The `<Menu.Item>` component displays a menu item with a label and an icon - or o
```jsx
// in src/MyMenu.js
import { Menu } from 'react-admin';
import LabelIcon from '@mui/icons-material/Label';

export const MyMenu = () => (
<Menu>
...
<Menu.Item to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />}/>
<Menu.Item to="/custom-route" primaryText="Miscellaneous" />
</Menu>
);
```

The `primaryText` prop accepts a string or a React node. You can use it e.g. to display a badge on top of the menu item:
The `primaryText` prop accepts a string, that react-admin passes through the [translation utility](./Translation.md). Alternately, you can set the menu item content using the `children`, e.g. to display a badge on top of the menu item:

```jsx
import Badge from '@mui/material/Badge';
import { Menu } from 'react-admin';

export const MyMenu = () => (
<Menu>
...
<Menu.Item to="/custom-route" primaryText={
<Menu.Item to="/custom-route" primaryText="Notifications">
<Badge badgeContent={4} color="primary">
Notifications
</Badge>
} />
</Menu.Item>
</Menu>
);
```

Note that if you use the `children` prop, you'll have to translate the menu item content yourself using [`useTranslate`](./useTranslate.md). You'll also need to provide a `primaryText` either way, because it will be rendered in the tooltip when the side menu is collapsed.

The `letfIcon` prop allows setting the menu left icon.

```jsx
// in src/MyMenu.js
import { Menu } from 'react-admin';
import LabelIcon from '@mui/icons-material/Label';

export const MyMenu = () => (
<Menu>
...
<Menu.Item
to="/custom-route"
primaryText="Miscellaneous"
leftIcon={<LabelIcon />}
/>
</Menu>
);
```

Additional props are passed down to [the underling Material UI `<MenuItem>` component](https://mui.com/material-ui/api/menu-item/).

**Tip**: The `<Menu.Item>` component makes use of the React Router [NavLink](https://reactrouter.com/docs/en/v6/components/nav-link) component, hence allowing to customize the active menu style. For instance, here is how to use a custom theme to show a left border for the active menu:
Expand Down Expand Up @@ -237,6 +256,8 @@ export const MyMenu = () => (

Clicking on the dashboard menu item leads to the `/` route and renders the component defined in [the `<Admin dashboard>` prop](./Admin.md#dashboard).

You can customize this menu item label by using the `primaryText` or `children` prop (see [the `<Menu.Item>` section](#menuitem) for more information).

## `<Menu.ResourceItems>`

The `<Menu.ResourceItems>` component displays one menu item for each resource, using the resource label and icon defined in the `<Resource>` components, and in the order in which they were declared in `<Admin>`.
Expand Down Expand Up @@ -309,9 +330,11 @@ the following code:
translates to:

```jsx
<Menu.Item to="/posts" primaryText="Posts" leftIcon={<BookIcon />}/>
<Menu.Item to="/posts" primaryText="Posts" leftIcon={<BookIcon />} />
```

You can customize this menu item label by using the `primaryText` or `children` prop (see [the `<Menu.Item>` section](#menuitem) for more information).

## Creating Menu Items For Resources

If you want to reorder the default menu, create a new Menu and use `<Menu.ResourceItem>` components as children.
Expand Down Expand Up @@ -358,7 +381,7 @@ For instance, to include a menu to a list of published posts:
pathname: '/posts',
search: `filter=${JSON.stringify({ is_published: true })}`,
}}
primaryText="Posts"
primaryText="Published Posts"
leftIcon={<BookIcon />}
/>
```
Expand Down
163 changes: 160 additions & 3 deletions packages/ra-ui-materialui/src/layout/Menu.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,29 @@
import * as React from 'react';
import { Resource, CustomRoutes, testDataProvider } from 'ra-core';
import { defaultTheme, Admin, useSidebarState } from 'react-admin';
import {
Typography,
Skeleton,
ThemeOptions,
MenuItem,
ListItemText,
ListItemIcon,
Divider,
Collapse,
List,
Tooltip,
} from '@mui/material';
import {
Dashboard,
PieChartOutlined,
PeopleOutlined,
Inventory,
ExpandLess,
ExpandMore,
QrCode,
} from '@mui/icons-material';
import { MemoryRouter, Route } from 'react-router-dom';

import { Resource, testDataProvider } from 'ra-core';
import { defaultTheme, Admin } from 'react-admin';
import { Typography, ThemeOptions } from '@mui/material';
import { Layout, Menu, Title } from '.';

export default { title: 'ra-ui-materialui/layout/Menu' };
Expand Down Expand Up @@ -59,3 +80,139 @@ export const Dense = () => {
</Admin>
);
};

export const Custom = () => {
const CustomMenu = () => (
<Menu>
<Menu.Item
to="/"
leftIcon={<Dashboard />}
primaryText="Dashboard"
/>
<Menu.Item
to="/sales"
leftIcon={<PieChartOutlined />}
primaryText="Sales"
/>
<Menu.Item
to="/customers"
leftIcon={<PeopleOutlined />}
primaryText="Customers"
/>
<Menu.Item
to="/products"
leftIcon={<Inventory />}
primaryText="Catalog"
/>
</Menu>
);
const CustomLayout = props => <Layout {...props} menu={CustomMenu} />;

return (
<MemoryRouter initialEntries={['/']}>
<Admin dataProvider={testDataProvider()} layout={CustomLayout}>
<CustomRoutes>
<Route path="/" element={<Page title="Dashboard" />} />
<Route path="/sales" element={<Page title="Sales" />} />
<Route
path="/customers"
element={<Page title="Customers" />}
/>
<Route
path="/products"
element={<Page title="Catalog" />}
/>
</CustomRoutes>
</Admin>
</MemoryRouter>
);
};

const Page = ({ title }) => (
<>
<Typography variant="h5" mt={2}>
{title}
</Typography>
<Skeleton height={300} />
</>
);

export const MenuItemChild = () => {
const CustomMenu = () => {
const [open, setOpen] = React.useState(true);
const [sidebarOpen] = useSidebarState();

const handleClick = () => {
setOpen(!open);
};
return (
<Menu>
<Menu.Item to="/" primaryText="Dashboard">
<ListItemIcon>
<Dashboard />
</ListItemIcon>
<ListItemText>Dashboard</ListItemText>
<Typography variant="body2" color="text.secondary">
⌘D
</Typography>
</Menu.Item>
<Divider />
<Menu.Item to="/sales" primaryText="Sales">
<ListItemIcon>
<PieChartOutlined />
</ListItemIcon>
<ListItemText>Sales</ListItemText>
</Menu.Item>
<Menu.Item to="/customers" primaryText="Customers">
<ListItemIcon>
<PeopleOutlined />
</ListItemIcon>
<ListItemText>Customers</ListItemText>
</Menu.Item>
<Tooltip title="Catalog" placement="right">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately this Tooltip displays all the time, not only when the side menu is collapsed. Not sure it's worth it to bother fixing it though, it's really minor...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw it. This is indeed minor, and it's on an internal story, so I though we could live with it.

<MenuItem onClick={handleClick}>
<ListItemIcon>
<Inventory />
</ListItemIcon>
<ListItemText>Catalog</ListItemText>
{open ? <ExpandLess /> : <ExpandMore />}
</MenuItem>
</Tooltip>
<Collapse in={open}>
<List disablePadding>
<Menu.Item
to="/products"
sx={{ pl: sidebarOpen ? 4 : 2 }}
primaryText="Products"
>
<ListItemIcon>
<QrCode />
</ListItemIcon>
<ListItemText>Products</ListItemText>
</Menu.Item>
</List>
</Collapse>
</Menu>
);
};
const CustomLayout = props => <Layout {...props} menu={CustomMenu} />;

return (
<MemoryRouter initialEntries={['/']}>
<Admin dataProvider={testDataProvider()} layout={CustomLayout}>
<CustomRoutes>
<Route path="/" element={<Page title="Dashboard" />} />
<Route path="/sales" element={<Page title="Sales" />} />
<Route
path="/customers"
element={<Page title="Customers" />}
/>
<Route
path="/products"
element={<Page title="Products" />}
/>
</CustomRoutes>
</Admin>
</MemoryRouter>
);
};
5 changes: 4 additions & 1 deletion packages/ra-ui-materialui/src/layout/MenuItemLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export const MenuItemLink = forwardRef<any, MenuItemLinkProps>((props, ref) => {
onClick,
sidebarIsOpen,
tooltipProps,
children,
...rest
} = props;

Expand Down Expand Up @@ -113,7 +114,9 @@ export const MenuItemLink = forwardRef<any, MenuItemLinkProps>((props, ref) => {
{leftIcon}
</ListItemIcon>
)}
{typeof primaryText === 'string'
{children
? children
: typeof primaryText === 'string'
? translate(primaryText, { _: primaryText })
: primaryText}
</StyledMenuItem>
Expand Down
Loading