Skip to content

Commit

Permalink
Add support for custom components to CrudMoreActionsMenu (#3275)
Browse files Browse the repository at this point in the history
Co-authored-by: Ricky James Smith <[email protected]>
  • Loading branch information
johnnyomair and jamesricky authored Jan 29, 2025
1 parent 5c06e4b commit b918c81
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 28 deletions.
26 changes: 26 additions & 0 deletions .changeset/wise-insects-tan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
"@comet/admin": minor
---

Add support for custom components to `CrudMoreActionsMenu`

**Example**

```tsx
const CustomAction = () => (
<CrudMoreActionsMenuItem
onClick={() => {
// Perform action
}}
>
<ListItemIcon>
<Favorite />
</ListItemIcon>
Custom Action
</CrudMoreActionsMenuItem>
);

<CrudMoreActionsMenu overallActions={[<CustomAction key="custom-action" />]} />;
```

**Note:** Use the `CrudMoreActionsMenuItem` component or `CrudMoreActionsMenuContext` to close the menu after clicking an item.
68 changes: 41 additions & 27 deletions packages/admin/admin/src/dataGrid/CrudMoreActionsMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import {
ListItemText,
Menu,
MenuItem,
MenuItemProps,
MenuList,
MenuListProps,
Theme,
Typography,
} from "@mui/material";
import { Maybe } from "graphql/jsutils/Maybe";
import { ComponentProps, MouseEvent, PropsWithChildren, ReactNode, useState } from "react";
import { ComponentProps, createContext, isValidElement, MouseEvent, PropsWithChildren, ReactElement, ReactNode, useContext, useState } from "react";
import { FormattedMessage } from "react-intl";

import { Button } from "../common/buttons/Button";
Expand All @@ -39,8 +40,8 @@ export interface CrudMoreActionsMenuProps
chip: typeof Chip;
}> {
selectionSize?: number;
overallActions?: Maybe<ActionItem>[];
selectiveActions?: Maybe<ActionItem>[];
overallActions?: Maybe<ActionItem | ReactElement>[];
selectiveActions?: Maybe<ActionItem | ReactElement>[];
}

interface CrudMoreActionsGroupProps {
Expand Down Expand Up @@ -104,6 +105,27 @@ const MoreActionsMenuItem = createComponentSlot(MenuItem)<CrudMoreActionsMenuCla
`,
);

type CrudMoreActionsMenuContext = {
closeMenu: () => void;
};

export const CrudMoreActionsMenuContext = createContext<CrudMoreActionsMenuContext>({
closeMenu: () => {
throw new Error("`CrudMoreActionsMenuContext` cannot be used outside of `CrudMoreActionsMenu`");
},
});

export function CrudMoreActionsMenuItem({ onClick, ...props }: MenuItemProps) {
const { closeMenu } = useContext(CrudMoreActionsMenuContext);

const handleClick: typeof onClick = (event) => {
onClick?.(event);
closeMenu();
};

return <MoreActionsMenuItem onClick={handleClick} {...props} />;
}

export function CrudMoreActionsMenu({ slotProps, overallActions, selectiveActions, selectionSize }: CrudMoreActionsMenuProps) {
const {
menu: menuProps,
Expand All @@ -122,7 +144,7 @@ export function CrudMoreActionsMenu({ slotProps, overallActions, selectiveAction
const hasSelectiveActions = !!selectiveActions?.length;

return (
<>
<CrudMoreActionsMenuContext.Provider value={{ closeMenu: handleClose }}>
<MoreActionsButton variant="textDark" endIcon={<MoreVertical />} {...buttonProps} onClick={handleClick}>
<FormattedMessage id="comet.crudMoreActions.title" defaultMessage="More" />
{!!selectionSize && <MoreActionsSelectedItemsChip size="small" color="primary" {...chipProps} label={selectionSize} />}
Expand Down Expand Up @@ -150,22 +172,18 @@ export function CrudMoreActionsMenu({ slotProps, overallActions, selectiveAction
{overallActions.map((item, index) => {
if (!item) return null;

const { divider, label, icon, onClick, ...rest } = item;
if (isValidElement(item)) {
return item;
}

const { divider, label, icon, ...rest } = item as ActionItem;

return (
<div key={index}>
<MoreActionsMenuItem
key={index}
disabled={!!selectionSize}
{...rest}
onClick={(e) => {
onClick?.(e);
handleClose();
}}
>
<CrudMoreActionsMenuItem key={index} disabled={!!selectionSize} {...rest}>
{!!icon && <ListItemIcon sx={{ minWidth: "unset !important" }}>{icon}</ListItemIcon>}
<ListItemText primary={label} />
</MoreActionsMenuItem>
</CrudMoreActionsMenuItem>
{!!divider && <CrudMoreActionsDivider {...dividerProps} />}
</div>
);
Expand All @@ -187,33 +205,29 @@ export function CrudMoreActionsMenu({ slotProps, overallActions, selectiveAction
{selectiveActions.map((item, index) => {
if (!item) return;

const { divider, label, icon, onClick, ...rest } = item;
if (isValidElement(item)) {
return item;
}

const { divider, label, icon, ...rest } = item as ActionItem;

return (
<div key={index}>
<MoreActionsMenuItem
key={index}
disabled={!selectionSize}
{...rest}
onClick={(e) => {
onClick?.(e);
handleClose();
}}
>
<CrudMoreActionsMenuItem key={index} disabled={!selectionSize} {...rest}>
{!!icon && <ListItemIcon sx={{ minWidth: "unset !important" }}>{icon}</ListItemIcon>}
<ListItemText primary={label} />
{!!selectionSize && (
<MoreActionsSelectedItemsChip size="small" color="primary" {...chipProps} label={selectionSize} />
)}
</MoreActionsMenuItem>
</CrudMoreActionsMenuItem>
{!!divider && <CrudMoreActionsDivider {...dividerProps} />}
</div>
);
})}
</CrudMoreActionsGroup>
)}
</Menu>
</>
</CrudMoreActionsMenuContext.Provider>
);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/admin/admin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export { Toolbar, ToolbarClassKey, ToolbarProps } from "./common/toolbar/Toolbar
export { Tooltip, TooltipClassKey, TooltipProps } from "./common/Tooltip";
export { ContentOverflow, ContentOverflowClassKey, ContentOverflowProps } from "./ContentOverflow";
export { CrudContextMenu, CrudContextMenuClassKey, CrudContextMenuProps } from "./dataGrid/CrudContextMenu";
export { CrudMoreActionsMenu, CrudMoreActionsMenuProps } from "./dataGrid/CrudMoreActionsMenu";
export { CrudMoreActionsMenu, CrudMoreActionsMenuContext, CrudMoreActionsMenuItem, CrudMoreActionsMenuProps } from "./dataGrid/CrudMoreActionsMenu";
export { CrudVisibility, CrudVisibilityProps } from "./dataGrid/CrudVisibility";
export { ExportApi, useDataGridExcelExport } from "./dataGrid/excelExport/useDataGridExcelExport";
export { GridCellContent, GridCellContentClassKey, GridCellContentProps } from "./dataGrid/GridCellContent";
Expand Down
73 changes: 73 additions & 0 deletions storybook/src/admin/data-grid/CrudMoreActionsMenu.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { CrudMoreActionsMenu, CrudMoreActionsMenuItem } from "@comet/admin";
import { Delete, Download, Excel, Favorite, Move } from "@comet/admin-icons";
import { ListItemIcon } from "@mui/material";

export default {
title: "@comet/admin/data-grid/CrudMoreActionsMenu",
};

export const Basic = () => {
return (
<CrudMoreActionsMenu
selectionSize={2}
overallActions={[
{
label: "Export to Excel",
onClick: () => {},
icon: <Excel />,
},
]}
selectiveActions={[
{
label: "Move",
onClick: () => {},
icon: <Move />,
},
{
label: "Delete",
onClick: () => {},
icon: <Delete />,
divider: true,
},
{
label: "Download",
onClick: () => {},
icon: <Download />,
},
]}
/>
);
};

export const CustomComponent = () => {
const CustomAction = () => {
return (
<CrudMoreActionsMenuItem
onClick={() => {
window.alert("Hello from custom action");
}}
>
<ListItemIcon
// TODO Remove once https://github.com/vivid-planet/comet/pull/2919 has been merged into main
sx={{ minWidth: "unset !important" }}
>
<Favorite />
</ListItemIcon>
Custom Action
</CrudMoreActionsMenuItem>
);
};

return (
<CrudMoreActionsMenu
overallActions={[
{
label: "Export to Excel",
onClick: () => {},
icon: <Excel />,
},
<CustomAction key="custom-action" />,
]}
/>
);
};

0 comments on commit b918c81

Please sign in to comment.