From b7646dbff5329487b285a62ffef81092c2d410c3 Mon Sep 17 00:00:00 2001 From: Ricky James Smith Date: Thu, 22 Aug 2024 11:16:30 +0200 Subject: [PATCH] Admin Generator (Future): Allow adding custom grid actions (#1913) --- demo/admin/src/products/ProductsGrid.tsx | 2 +- .../products/ProductsGridPreviewAction.tsx | 12 ++-- .../products/future/ProductsGrid.cometGen.ts | 5 ++ .../future/generated/ProductsGrid.tsx | 3 + .../src/generator/future/generateGrid.ts | 66 ++++++++++++------- .../generateGrid/generateGqlFieldList.ts | 8 ++- .../src/generator/future/generator.ts | 7 +- 7 files changed, 67 insertions(+), 36 deletions(-) diff --git a/demo/admin/src/products/ProductsGrid.tsx b/demo/admin/src/products/ProductsGrid.tsx index fc50703e7d..a94ce49ad8 100644 --- a/demo/admin/src/products/ProductsGrid.tsx +++ b/demo/admin/src/products/ProductsGrid.tsx @@ -219,7 +219,7 @@ export function ProductsGrid() { renderCell: (params) => { return ( <> - + diff --git a/demo/admin/src/products/ProductsGridPreviewAction.tsx b/demo/admin/src/products/ProductsGridPreviewAction.tsx index 9b5afe273b..b19e5b3720 100644 --- a/demo/admin/src/products/ProductsGridPreviewAction.tsx +++ b/demo/admin/src/products/ProductsGridPreviewAction.tsx @@ -1,15 +1,15 @@ import { Tooltip } from "@comet/admin"; import { View } from "@comet/admin-icons"; import { Dialog, DialogContent, DialogTitle, IconButton, Typography } from "@mui/material"; +import { GridCellParams } from "@mui/x-data-grid-pro"; +import { GQLProductsGridFutureFragment } from "@src/products/future/generated/ProductsGrid.generated"; import { GQLProductsListManualFragment } from "@src/products/ProductsGrid.generated"; import React from "react"; import { FormattedMessage } from "react-intl"; -type Props = { - product: GQLProductsListManualFragment; -}; +type Props = GridCellParams; -export const ProductsGridPreviewAction = ({ product }: Props) => { +export const ProductsGridPreviewAction = ({ row }: Props) => { const [showDetails, setShowDetails] = React.useState(false); return ( <> @@ -24,9 +24,9 @@ export const ProductsGridPreviewAction = ({ product }: Props) => { - {product.title} + {row.title} - {product.description} + {row.description} diff --git a/demo/admin/src/products/future/ProductsGrid.cometGen.ts b/demo/admin/src/products/future/ProductsGrid.cometGen.ts index d5e39bcd0b..9fc1270b2d 100644 --- a/demo/admin/src/products/future/ProductsGrid.cometGen.ts +++ b/demo/admin/src/products/future/ProductsGrid.cometGen.ts @@ -16,5 +16,10 @@ export const ProductsGrid: GridConfig = { { type: "staticSelect", name: "type", maxWidth: 150, values: [{ value: "Cap", label: "great Cap" }, "Shirt", "Tie"] }, { type: "date", name: "availableSince", width: 140 }, { type: "dateTime", name: "createdAt", width: 170 }, + { + type: "actions", + width: 116, + component: { name: "ProductsGridPreviewAction", import: "../../ProductsGridPreviewAction" }, + }, ], }; diff --git a/demo/admin/src/products/future/generated/ProductsGrid.tsx b/demo/admin/src/products/future/generated/ProductsGrid.tsx index 65b4c2df7e..ab10cb1dfe 100644 --- a/demo/admin/src/products/future/generated/ProductsGrid.tsx +++ b/demo/admin/src/products/future/generated/ProductsGrid.tsx @@ -22,6 +22,7 @@ import { GQLProductFilter } from "@src/graphql.generated"; import * as React from "react"; import { useIntl } from "react-intl"; +import { ProductsGridPreviewAction } from "../../ProductsGridPreviewAction"; import { GQLCreateProductMutation, GQLCreateProductMutationVariables, @@ -149,9 +150,11 @@ export function ProductsGrid({ filter, toolbarAction, rowAction }: Props): React filterable: false, type: "actions", align: "right", + width: 116, renderCell: (params) => { return ( <> + {rowAction && rowAction(params)} { diff --git a/packages/admin/cms-admin/src/generator/future/generateGrid.ts b/packages/admin/cms-admin/src/generator/future/generateGrid.ts index e3ed671436..afe8b92f0f 100644 --- a/packages/admin/cms-admin/src/generator/future/generateGrid.ts +++ b/packages/admin/cms-admin/src/generator/future/generateGrid.ts @@ -12,7 +12,7 @@ import { findInputObjectType } from "./generateGrid/findInputObjectType"; import { generateGqlFieldList } from "./generateGrid/generateGqlFieldList"; import { getForwardedGqlArgs } from "./generateGrid/getForwardedGqlArgs"; import { getPropsForFilterProp } from "./generateGrid/getPropsForFilterProp"; -import { GeneratorReturn, GridConfig } from "./generator"; +import { ActionsGridColumnConfig, GeneratorReturn, GridColumnConfig, GridConfig } from "./generator"; import { camelCaseToHumanReadable } from "./utils/camelCaseToHumanReadable"; import { findMutationType } from "./utils/findMutationType"; import { findRootBlocks } from "./utils/findRootBlocks"; @@ -95,7 +95,13 @@ export function generateGrid( const imports: Imports = []; const props: Prop[] = []; - const fieldList = generateGqlFieldList({ columns: config.columns.filter((column) => column.name !== "id") }); // exclude id because it's always required + const fieldList = generateGqlFieldList({ + columns: config.columns.filter((column) => { + return ( + column.type !== "actions" && column.name !== "id" // exclude id because it's always required + ); + }), + }); // all root blocks including those we don't have columns for (required for copy/paste) // this is not configured in the grid config, it's just an heuristics @@ -223,7 +229,15 @@ export function generateGrid( return true; }); - const gridColumnFields = config.columns.map((column) => { + const actionsColumnConfig = config.columns.find((column) => column.type === "actions") as ActionsGridColumnConfig; + const { + component: actionsColumnComponent, + type: actionsColumnType, + headerName: actionsColumnHeaderName, + ...restActionsColumnConfig + } = actionsColumnConfig ?? {}; + + const gridColumnFields = (config.columns.filter((column) => column.type !== "actions") as GridColumnConfig[]).map((column) => { const type = column.type; const name = String(column.name); @@ -366,6 +380,7 @@ export function generateGrid( ${Object.entries(rootBlocks) .map(([rootBlockKey, rootBlock]) => `import { ${rootBlock.name} } from "${rootBlock.import}";`) .join("\n")} + ${actionsColumnComponent ? `import { ${actionsColumnComponent.name} } from "${actionsColumnComponent.import}";` : ""} const ${instanceGqlTypePlural}Fragment = gql\` fragment ${fragmentName} on ${gqlType} { @@ -518,28 +533,31 @@ export function generateGrid( .join(",\n")}, ${ showActionsColumn - ? `{ - field: "actions", - headerName: "", - sortable: false, - filterable: false, - type: "actions", - align: "right", - renderCell: (params) => { + ? tsCodeRecordToString({ + field: '"actions"', + headerName: actionsColumnHeaderName + ? `intl.formatMessage({ id: "${instanceGqlType}.actions", defaultMessage: "${actionsColumnHeaderName}" })` + : `""`, + sortable: "false", + filterable: "false", + type: '"actions"', + align: '"right"', + ...restActionsColumnConfig, + renderCell: `(params) => { return ( <> - ${ - allowEditing - ? forwardRowAction - ? `{rowAction && rowAction(params)}` - : ` + ${actionsColumnComponent?.name ? `<${actionsColumnComponent.name} {...params} />` : ""}${ + allowEditing + ? forwardRowAction + ? `{rowAction && rowAction(params)}` + : ` ` - : "" - }${ - allowCopyPaste || allowDeleting - ? ` + : "" + }${ + allowCopyPaste || allowDeleting + ? ` ` - : "" - } + : "" + } ); - }, - },` + }`, + }) : "" } ]; diff --git a/packages/admin/cms-admin/src/generator/future/generateGrid/generateGqlFieldList.ts b/packages/admin/cms-admin/src/generator/future/generateGrid/generateGqlFieldList.ts index 5418619d9d..3d6ed42cc8 100644 --- a/packages/admin/cms-admin/src/generator/future/generateGrid/generateGqlFieldList.ts +++ b/packages/admin/cms-admin/src/generator/future/generateGrid/generateGqlFieldList.ts @@ -1,6 +1,6 @@ import objectPath from "object-path"; -import { GridColumnConfig } from "../generator"; +import { ActionsGridColumnConfig, GridColumnConfig } from "../generator"; type FieldsObjectType = { [key: string]: FieldsObjectType | boolean | string }; const recursiveStringify = (obj: FieldsObjectType): string => { @@ -21,9 +21,11 @@ const recursiveStringify = (obj: FieldsObjectType): string => { }; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export function generateGqlFieldList({ columns }: { columns: GridColumnConfig[] }) { +export function generateGqlFieldList({ columns }: { columns: Array | ActionsGridColumnConfig> }) { const fieldsObject: FieldsObjectType = columns.reduce((acc, field) => { - objectPath.set(acc, field.name, true); + if (field.type !== "actions") { + objectPath.set(acc, field.name, true); + } return acc; }, {}); return recursiveStringify(fieldsObject); diff --git a/packages/admin/cms-admin/src/generator/future/generator.ts b/packages/admin/cms-admin/src/generator/future/generator.ts index 3af2518a9e..f09217a399 100644 --- a/packages/admin/cms-admin/src/generator/future/generator.ts +++ b/packages/admin/cms-admin/src/generator/future/generator.ts @@ -68,7 +68,7 @@ export type FormConfig = { export type TabsConfig = { type: "tabs"; tabs: { name: string; content: GeneratorConfig }[] }; -type DataGridSettings = Pick; +export type DataGridSettings = Pick; export type GridColumnConfig = ( | { type: "text" } @@ -79,12 +79,15 @@ export type GridColumnConfig = ( | { type: "staticSelect"; values?: Array<{ value: string; label: string } | string> } | { type: "block"; block: ImportReference } ) & { name: UsableFields } & DataGridSettings; + +export type ActionsGridColumnConfig = { type: "actions"; component?: ImportReference } & DataGridSettings; + export type GridConfig = { type: "grid"; gqlType: T["__typename"]; fragmentName?: string; query?: string; - columns: GridColumnConfig[]; + columns: Array | ActionsGridColumnConfig>; add?: boolean; edit?: boolean; delete?: boolean;