Просмотр исходного кода

fix(dashboard): Fix some small issues with extensions, add docs

Michael Bromley 8 месяцев назад
Родитель
Сommit
7dca28c94c

+ 10 - 0
packages/dashboard/src/lib/framework/extension-api/define-dashboard-extension.ts

@@ -22,6 +22,16 @@ export function executeDashboardExtensionCallbacks() {
     }
 }
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * The main entry point for extensions to the React-based dashboard.
+ *
+ *
+ * @docsCategry dashboard
+ * @since 3.3.0
+ */
 export function defineDashboardExtension(extension: DashboardExtension) {
     globalRegistry.get('registerDashboardExtensionCallbacks').add(() => {
         if (extension.routes) {

+ 74 - 7
packages/dashboard/src/lib/framework/extension-api/extension-api-types.ts

@@ -18,9 +18,30 @@ export interface ActionBarButtonState {
     visible: boolean;
 }
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * Allows you to define custom action bar items for any page in the dashboard.
+ *
+ * @docsCategry dashboard
+ * @since 3.3.0
+ */
 export interface DashboardActionBarItem {
-    locationId: string;
+    /**
+     * @description
+     * The ID of the page where the action bar item should be displayed.
+     */
+    pageId: string;
+    /**
+     * @description
+     * A React component that will be rendered in the action bar.
+     */
     component: React.FunctionComponent<{ context: PageContext }>;
+    /**
+     * @description
+     * Any permissions that are required to display this action bar item.
+     */
     requiresPermission?: string | string[];
 }
 
@@ -32,12 +53,33 @@ export interface DashboardActionBarDropdownMenuItem {
 
 export type PageBlockPosition = { blockId: string; order: 'before' | 'after' | 'replace' };
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * The location of a page block in the dashboard. The location can be found by turning on
+ * "developer mode" in the dashboard user menu (bottom left corner) and then
+ * clicking the `< />` icon when hovering over a page block.
+ *
+ * @docsCategry dashboard
+ * @since 3.3.0
+ */
 export type PageBlockLocation = {
     pageId: string;
     position: PageBlockPosition;
     column: 'main' | 'side';
 };
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * This allows you to insert a custom component into a specific location
+ * on any page in the dashboard.
+ *
+ * @docsCategry dashboard
+ * @since 3.3.0
+ */
 export interface DashboardPageBlockDefinition {
     id: string;
     title?: React.ReactNode;
@@ -48,13 +90,38 @@ export interface DashboardPageBlockDefinition {
 
 /**
  * @description
- * The main entry point for a dashboard extension.
+ * **Status: Developer Preview**
+ *
  * This is used to define the routes, widgets, etc. that will be displayed in the dashboard.
+ *
+ * @docsCategry dashboard
+ * @since 3.3.0
  */
 export interface DashboardExtension {
-    routes: DashboardRouteDefinition[];
-    widgets: DashboardWidgetDefinition[];
-    actionBarItems: DashboardActionBarItem[];
-    pageBlocks: DashboardPageBlockDefinition[];
-    alerts: DashboardAlertDefinition[];
+    /**
+     * @description
+     * Allows you to define custom routes such as list or detail views.
+     */
+    routes?: DashboardRouteDefinition[];
+    /**
+     * @description
+     * Allows you to define custom page blocks for any page in the dashboard.
+     */
+    pageBlocks?: DashboardPageBlockDefinition[];
+    /**
+     * @description
+     * Allows you to define custom action bar items for any page in the dashboard.
+     */
+    actionBarItems?: DashboardActionBarItem[];
+    /**
+     * @description
+     * Not yet implemented
+     */
+    alerts?: DashboardAlertDefinition[];
+    /**
+     * @description
+     * Allows you to define custom routes for the dashboard, which will render the
+     * given components and optionally also add a nav menu item.
+     */
+    widgets?: DashboardWidgetDefinition[];
 }

+ 3 - 3
packages/dashboard/src/lib/framework/layout-engine/layout-extensions.ts

@@ -9,13 +9,13 @@ globalRegistry.register('dashboardPageBlockRegistry', new Map<string, DashboardP
 
 export function registerDashboardActionBarItem(item: DashboardActionBarItem) {
     globalRegistry.set('dashboardActionBarItemRegistry', map => {
-        map.set(item.locationId, [...(map.get(item.locationId) ?? []), item]);
+        map.set(item.pageId, [...(map.get(item.pageId) ?? []), item]);
         return map;
     });
 }
 
-export function getDashboardActionBarItems(locationId: string) {
-    return globalRegistry.get('dashboardActionBarItemRegistry').get(locationId) ?? [];
+export function getDashboardActionBarItems(pageId: string) {
+    return globalRegistry.get('dashboardActionBarItemRegistry').get(pageId) ?? [];
 }
 
 export function registerDashboardPageBlock(block: DashboardPageBlockDefinition) {

+ 20 - 6
packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx

@@ -29,11 +29,11 @@ export function Page({ children, pageId, entity, form, submitHandler, ...props }
 
     const pageTitle = childArray.find(child => React.isValidElement(child) && child.type === PageTitle);
     const pageActionBar = childArray.find(
-        child => React.isValidElement(child) && child.type === PageActionBar,
+        child => isOfType(child, PageActionBar),
     );
 
     const pageContent = childArray.filter(
-        child => React.isValidElement(child) && child.type !== PageTitle && child.type !== PageActionBar,
+        child => !isOfType(child, PageTitle) && !isOfType(child, PageActionBar),
     );
 
     const pageHeader = (
@@ -112,7 +112,7 @@ export function PageLayout({ children, className }: PageLayoutProps) {
         if (childBlock) {
             const blockId =
                 childBlock.props.blockId ??
-                (childBlock.type === CustomFieldsPageBlock ? 'custom-fields' : undefined);
+                (isOfType(childBlock, CustomFieldsPageBlock) ? 'custom-fields' : undefined);
             const extensionBlock = extensionBlocks.find(block => block.location.position.blockId === blockId);
             if (extensionBlock) {
                 const ExtensionBlock = (
@@ -138,7 +138,7 @@ export function PageLayout({ children, className }: PageLayoutProps) {
     }
 
     const fullWidthBlocks = finalChildArray.filter(
-        child => isPageBlock(child) && child.type === FullWidthPageBlock,
+        child => isPageBlock(child) && isOfType(child, FullWidthPageBlock),
     );
     const mainBlocks = finalChildArray.filter(child => isPageBlock(child) && child.props.column === 'main');
     const sideBlocks = finalChildArray.filter(child => isPageBlock(child) && child.props.column === 'side');
@@ -178,10 +178,10 @@ export function PageActionBar({ children }: { children: React.ReactNode }) {
     let childArray = React.Children.toArray(children);
 
     const leftContent = childArray.filter(
-        child => React.isValidElement(child) && child.type === PageActionBarLeft,
+        child => isOfType(child, PageActionBarLeft),
     );
     const rightContent = childArray.filter(
-        child => React.isValidElement(child) && child.type === PageActionBarRight,
+        child => isOfType(child, PageActionBarRight),
     );
 
     return (
@@ -274,3 +274,17 @@ export function CustomFieldsPageBlock({
         </PageBlock>
     );
 }
+
+/**
+ * @description
+ * This compares the type of a React component to a given type.
+ * It is safer than a simple `el === Component` check, as it also works in the context of
+ * the Vite build where the component is not the same reference.
+ */
+export function isOfType(el: unknown, type: React.FunctionComponent<any>): boolean {
+    if (React.isValidElement(el)) {
+        const elTypeName = typeof el.type === 'string' ? el.type : (el.type as React.FunctionComponent).name;
+        return elTypeName === type.name;
+    }
+    return false;
+}

+ 1 - 1
packages/dashboard/src/lib/framework/page/detail-page.tsx

@@ -70,7 +70,7 @@ export function DetailPage<
         },
     });
 
-    const updateFields = getOperationVariablesFields(updateDocument);
+    const updateFields = getOperationVariablesFields(updateDocument, 'input');
 
     return (
         <Page pageId={pageId} form={form} submitHandler={submitHandler}>