Sfoglia il codice sorgente

docs(dashboard): Add docs to some public APIs

Michael Bromley 8 mesi fa
parent
commit
873f5cd501

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

@@ -29,7 +29,7 @@ export function executeDashboardExtensionCallbacks() {
  * The main entry point for extensions to the React-based dashboard.
  * The main entry point for extensions to the React-based dashboard.
  *
  *
  *
  *
- * @docsCategry dashboard
+ * @docsCategory extensions
  * @since 3.3.0
  * @since 3.3.0
  */
  */
 export function defineDashboardExtension(extension: DashboardExtension) {
 export function defineDashboardExtension(extension: DashboardExtension) {

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

@@ -3,7 +3,7 @@ import type React from 'react';
 
 
 import { DashboardAlertDefinition } from '../alert/types.js';
 import { DashboardAlertDefinition } from '../alert/types.js';
 import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
 import { DashboardWidgetDefinition } from '../dashboard-widget/types.js';
-import { PageContext } from '../layout-engine/page-layout.js';
+import { PageContextValue } from '../layout-engine/page-layout.js';
 import { NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
 import { NavMenuItem } from '../nav-menu/nav-menu-extensions.js';
 
 
 export interface DashboardRouteDefinition {
 export interface DashboardRouteDefinition {
@@ -24,7 +24,7 @@ export interface ActionBarButtonState {
  *
  *
  * Allows you to define custom action bar items for any page in the dashboard.
  * Allows you to define custom action bar items for any page in the dashboard.
  *
  *
- * @docsCategry dashboard
+ * @docsCategory extensions
  * @since 3.3.0
  * @since 3.3.0
  */
  */
 export interface DashboardActionBarItem {
 export interface DashboardActionBarItem {
@@ -37,7 +37,7 @@ export interface DashboardActionBarItem {
      * @description
      * @description
      * A React component that will be rendered in the action bar.
      * A React component that will be rendered in the action bar.
      */
      */
-    component: React.FunctionComponent<{ context: PageContext }>;
+    component: React.FunctionComponent<{ context: PageContextValue }>;
     /**
     /**
      * @description
      * @description
      * Any permissions that are required to display this action bar item.
      * Any permissions that are required to display this action bar item.
@@ -47,7 +47,7 @@ export interface DashboardActionBarItem {
 
 
 export interface DashboardActionBarDropdownMenuItem {
 export interface DashboardActionBarDropdownMenuItem {
     locationId: string;
     locationId: string;
-    component: React.FunctionComponent<{ context: PageContext }>;
+    component: React.FunctionComponent<{ context: PageContextValue }>;
     requiresPermission?: string | string[];
     requiresPermission?: string | string[];
 }
 }
 
 
@@ -61,7 +61,7 @@ export type PageBlockPosition = { blockId: string; order: 'before' | 'after' | '
  * "developer mode" in the dashboard user menu (bottom left corner) and then
  * "developer mode" in the dashboard user menu (bottom left corner) and then
  * clicking the `< />` icon when hovering over a page block.
  * clicking the `< />` icon when hovering over a page block.
  *
  *
- * @docsCategry dashboard
+ * @docsCategory extensions
  * @since 3.3.0
  * @since 3.3.0
  */
  */
 export type PageBlockLocation = {
 export type PageBlockLocation = {
@@ -77,14 +77,14 @@ export type PageBlockLocation = {
  * This allows you to insert a custom component into a specific location
  * This allows you to insert a custom component into a specific location
  * on any page in the dashboard.
  * on any page in the dashboard.
  *
  *
- * @docsCategry dashboard
+ * @docsCategory extensions
  * @since 3.3.0
  * @since 3.3.0
  */
  */
 export interface DashboardPageBlockDefinition {
 export interface DashboardPageBlockDefinition {
     id: string;
     id: string;
     title?: React.ReactNode;
     title?: React.ReactNode;
     location: PageBlockLocation;
     location: PageBlockLocation;
-    component: React.FunctionComponent<{ context: PageContext }>;
+    component: React.FunctionComponent<{ context: PageContextValue }>;
     requiresPermission?: string | string[];
     requiresPermission?: string | string[];
 }
 }
 
 
@@ -94,7 +94,7 @@ export interface DashboardPageBlockDefinition {
  *
  *
  * This is used to define the routes, widgets, etc. that will be displayed in the dashboard.
  * This is used to define the routes, widgets, etc. that will be displayed in the dashboard.
  *
  *
- * @docsCategry dashboard
+ * @docsCategory extensions
  * @since 3.3.0
  * @since 3.3.0
  */
  */
 export interface DashboardExtension {
 export interface DashboardExtension {

+ 53 - 22
packages/dashboard/src/lib/framework/layout-engine/page-layout.tsx

@@ -22,7 +22,7 @@ export interface PageProps extends ComponentProps<'div'> {
     submitHandler?: any;
     submitHandler?: any;
 }
 }
 
 
-export const PageProvider = createContext<PageContext | undefined>(undefined);
+export const PageContext = createContext<PageContextValue | undefined>(undefined);
 
 
 export function Page({ children, pageId, entity, form, submitHandler, ...props }: PageProps) {
 export function Page({ children, pageId, entity, form, submitHandler, ...props }: PageProps) {
     const childArray = React.Children.toArray(children);
     const childArray = React.Children.toArray(children);
@@ -43,7 +43,48 @@ export function Page({ children, pageId, entity, form, submitHandler, ...props }
         </div>
         </div>
     );
     );
 
 
-    const pageContentWithOptionalForm = form ? (
+    return (
+        <PageContext.Provider value={{ pageId, form, entity }}>
+            <PageContent 
+                pageHeader={pageHeader} 
+                pageContent={pageContent} 
+                form={form}
+                submitHandler={submitHandler}
+                className={props.className}
+                {...props}
+            />
+        </PageContext.Provider>
+    );
+}
+
+function PageContent({ pageHeader, pageContent, form, submitHandler, ...props }: {
+    pageHeader: React.ReactElement;
+    pageContent: React.ReactElement;
+    form?: UseFormReturn<any>;
+    submitHandler?: any;
+    className?: string;
+}) {
+    return (
+        <div className={cn('m-4', props.className)} {...props}>
+            <LocationWrapper>
+                <PageContentWithOptionalForm 
+                    pageHeader={pageHeader} 
+                    pageContent={pageContent} 
+                    form={form}
+                    submitHandler={submitHandler} 
+                />
+            </LocationWrapper>
+        </div>
+    );
+}
+
+export function PageContentWithOptionalForm({ form, pageHeader, pageContent, submitHandler }: {
+    form?: UseFormReturn<any>;
+    pageHeader: React.ReactElement
+    pageContent: React.ReactElement;
+    submitHandler?: any;
+}) {
+    return form ? (
         <Form {...form}>
         <Form {...form}>
             <NavigationConfirmation form={form} />
             <NavigationConfirmation form={form} />
             <form onSubmit={submitHandler} className="space-y-4">
             <form onSubmit={submitHandler} className="space-y-4">
@@ -57,16 +98,6 @@ export function Page({ children, pageId, entity, form, submitHandler, ...props }
             {pageContent}
             {pageContent}
         </div>
         </div>
     );
     );
-
-    return (
-        <PageProvider value={{ pageId, form, entity }}>
-            <LocationWrapper>
-                <div className={cn('m-4', props.className)} {...props}>
-                    {pageContentWithOptionalForm}
-                </div>
-            </LocationWrapper>
-        </PageProvider>
-    );
 }
 }
 
 
 export type PageLayoutProps = {
 export type PageLayoutProps = {
@@ -164,7 +195,7 @@ export function DetailFormGrid({ children }: { children: React.ReactNode }) {
     return <div className="md:grid md:grid-cols-2 gap-4 items-start mb-4">{children}</div>;
     return <div className="md:grid md:grid-cols-2 gap-4 items-start mb-4">{children}</div>;
 }
 }
 
 
-export interface PageContext {
+export interface PageContextValue {
     pageId?: string;
     pageId?: string;
     entity?: any;
     entity?: any;
     form?: UseFormReturn<any>;
     form?: UseFormReturn<any>;
@@ -209,7 +240,7 @@ export function PageActionBarRight({ children }: { children: React.ReactNode })
     );
     );
 }
 }
 
 
-function PageActionBarItem({ item, page }: { item: DashboardActionBarItem; page: PageContext }) {
+function PageActionBarItem({ item, page }: { item: DashboardActionBarItem; page: PageContextValue }) {
     return (
     return (
         <PermissionGuard requires={item.requiresPermission ?? []}>
         <PermissionGuard requires={item.requiresPermission ?? []}>
             <item.component context={page} />
             <item.component context={page} />
@@ -244,10 +275,10 @@ export function PageBlock({ children, title, description, className, blockId }:
 }
 }
 
 
 export function FullWidthPageBlock({
 export function FullWidthPageBlock({
-    children,
-    className,
-    blockId,
-}: Pick<PageBlockProps, 'children' | 'className' | 'blockId'>) {
+                                       children,
+                                       className,
+                                       blockId,
+                                   }: Pick<PageBlockProps, 'children' | 'className' | 'blockId'>) {
     return (
     return (
         <LocationWrapper blockId={blockId}>
         <LocationWrapper blockId={blockId}>
             <div className={cn('w-full', className)}>{children}</div>
             <div className={cn('w-full', className)}>{children}</div>
@@ -256,10 +287,10 @@ export function FullWidthPageBlock({
 }
 }
 
 
 export function CustomFieldsPageBlock({
 export function CustomFieldsPageBlock({
-    column,
-    entityType,
-    control,
-}: {
+                                          column,
+                                          entityType,
+                                          control,
+                                      }: {
     column: 'main' | 'side';
     column: 'main' | 'side';
     entityType: string;
     entityType: string;
     control: Control<any, any>;
     control: Control<any, any>;

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

@@ -23,21 +23,68 @@ import {
 } from '../layout-engine/page-layout.js';
 } from '../layout-engine/page-layout.js';
 import { DetailEntityPath } from './page-types.js';
 import { DetailEntityPath } from './page-types.js';
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * @docsCategory components
+ * @docsPage DetailPage
+ * @since 3.3.0
+ */
 export interface DetailPageProps<
 export interface DetailPageProps<
     T extends TypedDocumentNode<any, any>,
     T extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
     U extends TypedDocumentNode<any, any>,
     U extends TypedDocumentNode<any, any>,
     EntityField extends keyof ResultOf<T> = DetailEntityPath<T>,
     EntityField extends keyof ResultOf<T> = DetailEntityPath<T>,
 > {
 > {
+    /**
+     * @description
+     * A unique identifier for the page.
+     */
     pageId: string;
     pageId: string;
+    /**
+     * @description
+     * The Tanstack Router route used to navigate to this page.
+     */
     route: AnyRoute;
     route: AnyRoute;
+    /**
+     * @description
+     * The title of the page.
+     */
     title: (entity: ResultOf<T>[EntityField]) => string;
     title: (entity: ResultOf<T>[EntityField]) => string;
+    /**
+     * @description
+     * The query document used to fetch the entity.
+     */
     queryDocument: T;
     queryDocument: T;
+    /**
+     * @description
+     * The mutation document used to create the entity.
+     */
     createDocument?: C;
     createDocument?: C;
+    /**
+     * @description
+     * The mutation document used to update the entity.
+     */
     updateDocument: U;
     updateDocument: U;
+    /**
+     * @description
+     * A function that sets the values for the update input type based on the entity.
+     */
     setValuesForUpdate: (entity: ResultOf<T>[EntityField]) => VariablesOf<U>['input'];
     setValuesForUpdate: (entity: ResultOf<T>[EntityField]) => VariablesOf<U>['input'];
 }
 }
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * Auto-generates a detail page with a form based on the provided query and mutation documents.
+ *
+ * @docsCategory components
+ * @docsPage DetailPage
+ * @docsWeight 0
+ * @since 3.3.0
+ */
 export function DetailPage<
 export function DetailPage<
     T extends TypedDocumentNode<any, any>,
     T extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,

+ 19 - 0
packages/dashboard/src/lib/framework/page/list-page.tsx

@@ -31,6 +31,14 @@ type ListQueryFields<T extends TypedDocumentNode<any, any>> = {
         : never;
         : never;
 }[keyof ResultOf<T>];
 }[keyof ResultOf<T>];
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * @docsCategory components
+ * @docsPage ListPage
+ * @since 3.3.0
+ */
 export interface ListPageProps<
 export interface ListPageProps<
     T extends TypedDocumentNode<U, V>,
     T extends TypedDocumentNode<U, V>,
     U extends ListQueryShape,
     U extends ListQueryShape,
@@ -56,6 +64,17 @@ export interface ListPageProps<
     setTableOptions?: (table: TableOptions<any>) => TableOptions<any>;
     setTableOptions?: (table: TableOptions<any>) => TableOptions<any>;
 }
 }
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * Auto-generates a list page with columns generated based on the provided query document fields.
+ *
+ * @docsCategory components
+ * @docsPage ListPage
+ * @docsWeight 0
+ * @since 3.3.0
+ */
 export function ListPage<
 export function ListPage<
     T extends TypedDocumentNode<U, V>,
     T extends TypedDocumentNode<U, V>,
     U extends Record<string, any> = any,
     U extends Record<string, any> = any,

+ 80 - 0
packages/dashboard/src/lib/framework/page/use-detail-page.ts

@@ -26,6 +26,14 @@ type RemoveNullFields<T> = {
     [K in keyof T]: RemoveNull<T[K]>;
     [K in keyof T]: RemoveNull<T[K]>;
 };
 };
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * @docsCategory hooks
+ * @docsPage useDetailPage
+ * @since 3.3.0
+ */
 export interface DetailPageOptions<
 export interface DetailPageOptions<
     T extends TypedDocumentNode<any, any>,
     T extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
@@ -114,6 +122,14 @@ export type DetailPageEntity<
     translations: DetailPageTranslations<T, EntityField>;
     translations: DetailPageTranslations<T, EntityField>;
 };
 };
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * @docsCategory hooks
+ * @docsPage useDetailPage
+ * @since 3.3.0
+ */
 export interface UseDetailPageResult<
 export interface UseDetailPageResult<
     T extends TypedDocumentNode<any, any>,
     T extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
     C extends TypedDocumentNode<any, any>,
@@ -130,8 +146,72 @@ export interface UseDetailPageResult<
 
 
 /**
 /**
  * @description
  * @description
+ * **Status: Developer Preview**
+ *
  * This hook is used to create an entity detail page which can read
  * This hook is used to create an entity detail page which can read
  * and update an entity.
  * and update an entity.
+ *
+ * @example
+ * ```ts
+ * const { form, submitHandler, entity, isPending, resetForm } = useDetailPage({
+ *     queryDocument: paymentMethodDetailDocument,
+ *     createDocument: createPaymentMethodDocument,
+ *     updateDocument: updatePaymentMethodDocument,
+ *     setValuesForUpdate: entity => {
+ *         return {
+ *             id: entity.id,
+ *             enabled: entity.enabled,
+ *             name: entity.name,
+ *             code: entity.code,
+ *             description: entity.description,
+ *             checker: entity.checker?.code
+ *                 ? {
+ *                       code: entity.checker?.code,
+ *                       arguments: entity.checker?.args,
+ *                   }
+ *                 : null,
+ *             handler: entity.handler?.code
+ *                 ? {
+ *                       code: entity.handler?.code,
+ *                       arguments: entity.handler?.args,
+ *                   }
+ *                 : null,
+ *             translations: entity.translations.map(translation => ({
+ *                 id: translation.id,
+ *                 languageCode: translation.languageCode,
+ *                 name: translation.name,
+ *                 description: translation.description,
+ *             })),
+ *             customFields: entity.customFields,
+ *         };
+ *     },
+ *     transformCreateInput: input => {
+ *         return {
+ *             ...input,
+ *             checker: input.checker?.code ? input.checker : undefined,
+ *             handler: input.handler,
+ *         };
+ *     },
+ *     params: { id: params.id },
+ *     onSuccess: async data => {
+ *         toast.success(i18n.t('Successfully updated payment method'));
+ *         resetForm();
+ *         if (creatingNewEntity) {
+ *             await navigate({ to: `../$id`, params: { id: data.id } });
+ *         }
+ *     },
+ *     onError: err => {
+ *         toast.error(i18n.t('Failed to update payment method'), {
+ *             description: err instanceof Error ? err.message : 'Unknown error',
+ *         });
+ *     },
+ * });
+ * ```
+ *
+ * @docsCategory hooks
+ * @docsPage useDetailPage
+ * @docsWeight 0
+ * @since 3.3.0
  */
  */
 export function useDetailPage<
 export function useDetailPage<
     T extends TypedDocumentNode<any, any>,
     T extends TypedDocumentNode<any, any>,

+ 13 - 1
packages/dashboard/src/lib/hooks/use-auth.tsx

@@ -1,7 +1,19 @@
 import * as React from 'react';
 import * as React from 'react';
 import { AuthContext } from '../providers/auth.js';
 import { AuthContext } from '../providers/auth.js';
 
 
-
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * Provides access to the {@link ChannelContext} which contains information
+ * about the active channel.
+ *
+ *
+ * @docsCategory hooks
+ * @docsPage useAuth
+ * @docsWeight 0
+ * @since 3.3.0
+ */
 export function useAuth() {
 export function useAuth() {
     const context = React.useContext(AuthContext);
     const context = React.useContext(AuthContext);
     if (!context) {
     if (!context) {

+ 13 - 0
packages/dashboard/src/lib/hooks/use-channel.ts

@@ -3,6 +3,19 @@ import * as React from 'react';
 
 
 // Hook to use the channel context
 // Hook to use the channel context
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * Provides access to the {@link ChannelContext} which contains information
+ * about the active channel.
+ *
+ *
+ * @docsCategory hooks
+ * @docsPage useChannel
+ * @docsWeight 0
+ * @since 3.3.0
+ */
 export function useChannel() {
 export function useChannel() {
     const context = React.useContext(ChannelContext);
     const context = React.useContext(ChannelContext);
     if (context === undefined) {
     if (context === undefined) {

+ 4 - 0
packages/dashboard/src/lib/hooks/use-local-format.ts

@@ -18,6 +18,10 @@ import { useServerConfig } from './use-server-config.js';
  *          toMajorUnits,
  *          toMajorUnits,
  * } = useLocalFormat();
  * } = useLocalFormat();
  * ```
  * ```
+ *
+ * @docsCategory hooks
+ * @docsPage useLocalFormat
+ * @docsWeight 0
  */
  */
 export function useLocalFormat() {
 export function useLocalFormat() {
     const { moneyStrategyPrecision } = useServerConfig() ?? { moneyStrategyPrecision: 2 };
     const { moneyStrategyPrecision } = useServerConfig() ?? { moneyStrategyPrecision: 2 };

+ 2 - 3
packages/dashboard/src/lib/hooks/use-page.tsx

@@ -1,11 +1,10 @@
-import { PageProvider } from "@/framework/layout-engine/page-layout.js";
+import { PageContext } from "@/framework/layout-engine/page-layout.js";
 import { useContext } from "react";
 import { useContext } from "react";
 
 
 export function usePage() {
 export function usePage() {
-    const page = useContext(PageProvider);
+    const page = useContext(PageContext);
     if (!page) {
     if (!page) {
         throw new Error('PageProvider not found');
         throw new Error('PageProvider not found');
     }
     }
     return page;
     return page;
 }
 }
-    

+ 13 - 0
packages/dashboard/src/lib/hooks/use-permissions.ts

@@ -3,6 +3,19 @@ import { Permission } from '@vendure/common/lib/generated-types';
 
 
 import { useUserSettings } from './use-user-settings.js';
 import { useUserSettings } from './use-user-settings.js';
 
 
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * Returns a `hasPermissions` function that can be used to determine whether the active user
+ * has the given permissions on the active channel.
+ *
+ *
+ * @docsCategory hooks
+ * @docsPage usePermissions
+ * @docsWeight 0
+ * @since 3.3.0
+ */
 export function usePermissions() {
 export function usePermissions() {
     const { channels } = useAuth();
     const { channels } = useAuth();
     const { settings } = useUserSettings();
     const { settings } = useUserSettings();

+ 11 - 1
packages/dashboard/src/lib/providers/auth.tsx

@@ -3,7 +3,17 @@ import { ResultOf, graphql } from '@/graphql/graphql.js';
 import { useUserSettings } from '@/hooks/use-user-settings.js';
 import { useUserSettings } from '@/hooks/use-user-settings.js';
 import { useMutation, useQuery } from '@tanstack/react-query';
 import { useMutation, useQuery } from '@tanstack/react-query';
 import * as React from 'react';
 import * as React from 'react';
-
+import { useAuth } from '@/hooks/use-auth.js';
+
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * @docsCategory hooks
+ * @docsPage useAuth
+ * @docsWeight 0
+ * @since 3.3.0
+ */
 export interface AuthContext {
 export interface AuthContext {
     status: 'authenticated' | 'verifying' | 'unauthenticated';
     status: 'authenticated' | 'verifying' | 'unauthenticated';
     authenticationError?: string;
     authenticationError?: string;

+ 8 - 1
packages/dashboard/src/lib/providers/channel-provider.tsx

@@ -41,7 +41,14 @@ const ChannelsQuery = graphql(
 type ActiveChannel = ResultOf<typeof ChannelsQuery>['activeChannel'];
 type ActiveChannel = ResultOf<typeof ChannelsQuery>['activeChannel'];
 type Channel = ResultOf<typeof channelFragment>;
 type Channel = ResultOf<typeof channelFragment>;
 
 
-// Define the context interface
+/**
+ * @description
+ * **Status: Developer Preview**
+ *
+ * @docsCategory hooks
+ * @docsPage useChannel
+ * @since 3.3.0
+ */
 export interface ChannelContext {
 export interface ChannelContext {
     activeChannel: ActiveChannel | undefined;
     activeChannel: ActiveChannel | undefined;
     channels: Channel[];
     channels: Channel[];

+ 4 - 0
scripts/docs/generate-typescript-docs.ts

@@ -70,6 +70,10 @@ const sections: DocsSectionConfig[] = [
         exclude: [/generated-types/],
         exclude: [/generated-types/],
         outputPath: 'admin-ui-api',
         outputPath: 'admin-ui-api',
     },
     },
+    {
+        sourceDirs: ['packages/dashboard/src/'],
+        outputPath: 'dashboard',
+    },
 ];
 ];
 
 
 generateTypescriptDocs(sections);
 generateTypescriptDocs(sections);