Browse Source

docs(admin-ui): Add docs on action bar dropdown menu items

Relates to #2678
Michael Bromley 1 year ago
parent
commit
c71e65cb02
26 changed files with 224 additions and 92 deletions
  1. 75 25
      docs/docs/guides/extending-the-admin-ui/add-actions-to-pages/index.md
  2. BIN
      docs/docs/guides/extending-the-admin-ui/add-actions-to-pages/ui-extensions-actionbar-dropdown.webp
  3. 41 6
      docs/docs/reference/admin-ui-api/action-bar/action-bar-context.md
  4. 10 5
      docs/docs/reference/admin-ui-api/action-bar/action-bar-item.md
  5. 3 2
      docs/docs/reference/admin-ui-api/action-bar/action-bar-location-id.md
  6. 1 1
      docs/docs/reference/admin-ui-api/action-bar/router-link-definition.md
  7. 1 1
      docs/docs/reference/admin-ui-api/custom-detail-components/custom-detail-component-location-id.md
  8. 3 3
      docs/docs/reference/admin-ui-api/directives/if-permissions-directive.md
  9. 3 3
      docs/docs/reference/admin-ui-api/list-detail-views/base-detail-component.md
  10. 1 1
      docs/docs/reference/admin-ui-api/list-detail-views/base-list-component.md
  11. 10 10
      docs/docs/reference/admin-ui-api/list-detail-views/detail-component-with-resolver.md
  12. 1 1
      docs/docs/reference/admin-ui-api/list-detail-views/typed-base-detail-component.md
  13. 13 7
      docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md
  14. 3 3
      docs/docs/reference/admin-ui-api/pipes/has-permission-pipe.md
  15. 1 1
      docs/docs/reference/typescript-api/common/currency-code.md
  16. 1 1
      docs/docs/reference/typescript-api/common/job-state.md
  17. 1 1
      docs/docs/reference/typescript-api/common/language-code.md
  18. 1 1
      docs/docs/reference/typescript-api/common/permission.md
  19. 1 1
      docs/docs/reference/typescript-api/custom-fields/custom-field-config.md
  20. 2 3
      docs/docs/reference/typescript-api/custom-fields/index.md
  21. 1 1
      docs/docs/reference/typescript-api/custom-fields/typed-custom-single-field-config.md
  22. 4 4
      docs/docs/reference/typescript-api/events/event-types.md
  23. 10 10
      docs/docs/reference/typescript-api/testing/simple-graph-qlclient.md
  24. 2 1
      packages/admin-ui/src/lib/core/src/common/component-registry-types.ts
  25. 2 0
      packages/admin-ui/src/lib/core/src/extension/add-action-bar-dropdown-menu-item.ts
  26. 33 0
      packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts

+ 75 - 25
docs/docs/guides/extending-the-admin-ui/add-actions-to-pages/index.md

@@ -6,11 +6,15 @@ weight: 5
 import Tabs from '@theme/Tabs';
 import TabItem from '@theme/TabItem';
 
-The `ActionBar` is the horizontal area at the top of each list or detail page, which contains the main buttons for that page. This guide explains how to add new buttons to the ActionBar.
+The `ActionBar` is the horizontal area at the top of each list or detail page, which contains the main buttons for that page.
+This guide explains how to add new buttons and dropdown menu items to the ActionBar.
 
-For example, consider an "order invoice" extension that allows you to print invoices for orders. In this case, you can add a "print invoice" button to the ActionBar. This is done using the [addActionBarItem function](/reference/admin-ui-api/action-bar/add-action-bar-item/).
+For example, consider an "order invoice" extension that allows you to print invoices for orders. You can add a "print invoice" 
+button to the ActionBar of the order detail page, either as a button or as a dropdown menu item.
 
-## ActionBar Example
+## ActionBar button example
+
+Adding a button is done using the [`addActionBarItem`](/reference/admin-ui-api/action-bar/add-action-bar-item/) function.
 
 ```ts title="src/plugins/invoice/ui/providers.ts"
 import { addActionBarItem } from '@vendure/admin-ui/core';
@@ -18,8 +22,9 @@ import { addActionBarItem } from '@vendure/admin-ui/core';
 export default [
     addActionBarItem({
         id: 'print-invoice',
-        label: 'Print invoice',
         locationId: 'order-detail',
+        label: 'Print invoice',
+        icon: 'printer',
         routerLink: route => {
             const id = route.snapshot.params.id;
             return ['./extensions/order-invoices', id];
@@ -31,11 +36,44 @@ export default [
 
 ![./ui-extensions-actionbar.webp](./ui-extensions-actionbar.webp)
 
-In each list or detail view in the app, the ActionBar has a unique `locationId` which is how the app knows in which view to place your button. The complete list of available locations into which you can add new ActionBar can be found in the [PageLocationId docs](/reference/admin-ui-api/action-bar/page-location-id/). You can also press `ctrl + u` when in development mode to see the location of all UI extension points.
+In each list or detail view in the app, the ActionBar has a unique `locationId` which is how the app knows in which view to place your button.
+The complete list of available locations into which you can add new ActionBar can be found in the [PageLocationId docs](/reference/admin-ui-api/action-bar/page-location-id/).
+You can also press `ctrl + u` when in development mode to see the location of all UI extension points.
+
+## ActionBar dropdown menu example
+
+Vendure v2.2.0 introduced the ability to add dropdown menu items to the ActionBar. If you want to add an action which is
+less commonly used, or want to take up less space in the action bar, then a dropdown menu item is a good choice.
+This is done using the [`addActionBarDropdownMenuItem`](/reference/admin-ui-api/action-bar/add-action-bar-dropdown-menu-item/) function.
+
+Let's re-work the "print invoice" button example to display it instead as a dropdown menu item:
+
+```ts title="src/plugins/invoice/ui/providers.ts"
+import { addActionBarDropdownMenuItem } from '@vendure/admin-ui/core';
+
+export default [
+    addActionBarDropdownMenuItem({
+        id: 'print-invoice',
+        locationId: 'order-detail',
+        label: 'Print invoice',
+        icon: 'printer',
+        routerLink: route => {
+            const id = route.snapshot.params.id;
+            return ['./extensions/order-invoices', id];
+        },
+        requiresPermission: 'ReadOrder',
+        // When set to `true`, a horizontal divider will be
+        // displayed above this item in the dropdown menu.
+        hasDivider: true,
+    }),
+];
+```
+
+![./ui-extensions-actionbar-dropdown.webp](./ui-extensions-actionbar-dropdown.webp)
 
 ## Handling button clicks
 
-There are two ways to handle the click event of an ActionBar button:
+There are two ways to handle the click event of an ActionBar button or dropdown menu item:
 
 1. Use the `routerLink` property to navigate to a new route when the button is clicked.
 2. Use the `onClick` property to execute a function when the button is clicked.
@@ -44,7 +82,6 @@ There are two ways to handle the click event of an ActionBar button:
 
 The `routerLink` property allows you to specify a route to navigate to when the button is clicked. The route can be a constant value, or it can be a function which receives the current route as well as a [`context` object](/reference/admin-ui-api/action-bar/action-bar-context) as arguments.
 
-
 <Tabs>
 <TabItem value="routerLink constant" label="routerLink constant" default>
 
@@ -77,7 +114,7 @@ export default [
         locationId: 'order-detail',
         // highlight-start
         // The route can be a function
-        routerLink: (route) => {
+        routerLink: route => {
             const id = route.snapshot.params.id;
             return ['./extensions/order-invoices', id];
         },
@@ -105,7 +142,8 @@ import { addActionBarItem } from '@vendure/admin-ui/core';
 const mutation = gql`
     mutation MyMutation($orderId: ID!) {
         myMutation(orderId: $orderId)
-    }`;
+    }
+`;
 
 export default [
     addActionBarItem({
@@ -116,12 +154,9 @@ export default [
         onClick: async (event, context) => {
             try {
                 const orderId = context.route.snapshot.params.id;
-                await firstValueFrom(
-                    context.dataService.mutate(mutation, { orderId })
-                );
+                await firstValueFrom(context.dataService.mutate(mutation, { orderId }));
             } catch (error) {
-                context.notificationService
-                    .error('Error executing mutation: ' + error.message);
+                context.notificationService.error('Error executing mutation: ' + error.message);
             }
         },
         // highlight-end
@@ -139,7 +174,7 @@ Use the `buttonState` property (added in v2.1) to control the visibility and dis
 
 ```ts title="src/plugins/invoice/ui/providers.ts"
 import { map, switchMap } from 'rxjs/operators';
-import { addActionBarItem } from '@vendure/admin-ui/core'; 
+import { addActionBarItem } from '@vendure/admin-ui/core';
 
 export default [
     addActionBarItem({
@@ -147,22 +182,37 @@ export default [
         label: 'Print invoice',
         locationId: 'order-detail',
         buttonState: context => {
-            return context.route.data.pipe(
-                // For any of the detail pages, we can get an observable stream
-                // of the entity with the following "switchMap" function:
-                switchMap(data => data.detail.entity),
-                map((order: any) => {
-                    return {
-                        disabled: order.state === 'AddingItems',
-                        visible: true,
-                    };
-                }),
+            // For any of the detail pages, we can get an observable stream
+            // of the entity via `context.entity$`:
+            return context.entity$.pipe(
+                map(order => ({
+                    disabled: order?.state === 'AddingItems',
+                    visible: true,
+                })),
             );
         },
     }),
 ];
 ```
 
+:::note
+The `context.entity$` property was introduced in Vendure v2.2. If you are using v2.1, you can achieve equivalent functionality
+with this code:
+
+```ts
+buttonState: context => {
+    return context.route.data.pipe(
+        switchMap(data => data.detail.entity),
+        map((order: any) => ({
+            disabled: order.state === 'AddingItems',
+            visible: true,
+        })),
+    );
+}
+```
+
+:::
+
 ## Restricting access by permissions
 
 You can use the `requiresPermission` property to restrict access to the button by permission. This property accepts a single permission string or an array of permission strings. If the current user does not have the required permission, the button will not be visible.

BIN
docs/docs/guides/extending-the-admin-ui/add-actions-to-pages/ui-extensions-actionbar-dropdown.webp


+ 41 - 6
docs/docs/reference/admin-ui-api/action-bar/action-bar-context.md

@@ -11,9 +11,10 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ActionBarContext
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="89" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="90" packageName="@vendure/admin-ui" />
 
-Providers available to the onClick handler of an <a href='/reference/admin-ui-api/action-bar/action-bar-item#actionbaritem'>ActionBarItem</a> or <a href='/reference/admin-ui-api/nav-menu/nav-menu-item#navmenuitem'>NavMenuItem</a>.
+Providers & data available to the `onClick` & `buttonState` functions of an <a href='/reference/admin-ui-api/action-bar/action-bar-item#actionbaritem'>ActionBarItem</a>,
+<a href='/reference/admin-ui-api/action-bar/action-bar-dropdown-menu-item#actionbardropdownmenuitem'>ActionBarDropdownMenuItem</a> or <a href='/reference/admin-ui-api/nav-menu/nav-menu-item#navmenuitem'>NavMenuItem</a>.
 
 ```ts title="Signature"
 interface ActionBarContext {
@@ -21,6 +22,7 @@ interface ActionBarContext {
     injector: Injector;
     dataService: DataService;
     notificationService: NotificationService;
+    entity$: Observable<Record<string, any> | undefined>;
 }
 ```
 
@@ -30,22 +32,55 @@ interface ActionBarContext {
 
 <MemberInfo kind="property" type={`ActivatedRoute`}   />
 
-
+The router's [ActivatedRoute](https://angular.dev/guide/routing/router-reference#activated-route) object for
+the current route. This object contains information about the route, its parameters, and additional data
+associated with the route.
 ### injector
 
 <MemberInfo kind="property" type={`<a href='/reference/typescript-api/common/injector#injector'>Injector</a>`}   />
 
-
+The Angular [Injector](https://angular.dev/api/core/Injector) which can be used to get instances
+of services and other providers available in the application.
 ### dataService
 
 <MemberInfo kind="property" type={`<a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>`}   />
 
-
+The [DataService](/reference/admin-ui-api/services/data-service), which provides methods for querying the
+server-side data.
 ### notificationService
 
 <MemberInfo kind="property" type={`<a href='/reference/admin-ui-api/services/notification-service#notificationservice'>NotificationService</a>`}   />
 
-
+The [NotificationService](/reference/admin-ui-api/services/notification-service), which provides methods for
+displaying notifications to the user.
+### entity$
+
+<MemberInfo kind="property" type={`Observable&#60;Record&#60;string, any&#62; | undefined&#62;`}  since="2.2.0"  />
+
+An observable of the current entity in a detail view. In a list view the observable will not emit any values.
+
+*Example*
+
+```ts
+addActionBarDropdownMenuItem({
+    id: 'print-invoice',
+    locationId: 'order-detail',
+    label: 'Print Invoice',
+    icon: 'printer',
+    buttonState: context => {
+        // highlight-start
+        return context.entity$.pipe(
+            map((order) => {
+                return order?.state === 'PaymentSettled'
+                    ? { disabled: false, visible: true }
+                    : { disabled: true, visible: true };
+            }),
+        );
+        // highlight-end
+    },
+    requiresPermission: ['UpdateOrder'],
+}),
+```
 
 
 </div>

+ 10 - 5
docs/docs/reference/admin-ui-api/action-bar/action-bar-item.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ActionBarItem
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="107" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="158" packageName="@vendure/admin-ui" />
 
 A button in the ActionBar area at the top of one of the list or detail views.
 
@@ -37,17 +37,18 @@ interface ActionBarItem {
 
 <MemberInfo kind="property" type={`string`}   />
 
-
+A unique identifier for the item.
 ### label
 
 <MemberInfo kind="property" type={`string`}   />
 
-
+The label to display for the item. This can also be a translation token,
+e.g. `invoice-plugin.print-invoice`.
 ### locationId
 
 <MemberInfo kind="property" type={`<a href='/reference/admin-ui-api/action-bar/action-bar-location-id#actionbarlocationid'>ActionBarLocationId</a>`}   />
 
-
+The location in the UI where this button should be displayed.
 ### disabled
 
 <MemberInfo kind="property" type={`Observable&#60;boolean&#62;`}   />
@@ -83,7 +84,9 @@ dynamically enable/disable or show/hide the button.
 
 <MemberInfo kind="property" type={`string`}   />
 
-
+An optional icon to display in the button. The icon
+should be a valid shape name from the [Clarity Icons](https://core.clarity.design/foundation/icons/shapes/)
+set.
 ### requiresPermission
 
 <MemberInfo kind="property" type={`string | string[]`}   />
@@ -91,9 +94,11 @@ dynamically enable/disable or show/hide the button.
 Control the display of this item based on the user permissions. Note: if you attempt to pass a
 <a href='/reference/typescript-api/auth/permission-definition#permissiondefinition'>PermissionDefinition</a> object, you will get a compilation error. Instead, pass the plain
 string version. For example, if the permission is defined as:
+
 ```ts
 export const MyPermission = new PermissionDefinition('ProductReview');
 ```
+
 then the generated permission strings will be:
 
 - `CreateProductReview`

+ 3 - 2
docs/docs/reference/admin-ui-api/action-bar/action-bar-location-id.md

@@ -11,9 +11,10 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## ActionBarLocationId
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/component-registry-types.ts" sourceLine="105" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/component-registry-types.ts" sourceLine="106" packageName="@vendure/admin-ui" />
 
-The valid locationIds for registering action bar items.
+The valid locationIds for registering action bar items. For a list of
+values, see <a href='/reference/admin-ui-api/action-bar/page-location-id#pagelocationid'>PageLocationId</a>.
 
 ```ts title="Signature"
 type ActionBarLocationId = PageLocationId

+ 1 - 1
docs/docs/reference/admin-ui-api/action-bar/router-link-definition.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## RouterLinkDefinition
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="154" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts" sourceLine="289" packageName="@vendure/admin-ui" />
 
 A function which returns the router link for an <a href='/reference/admin-ui-api/action-bar/action-bar-item#actionbaritem'>ActionBarItem</a> or <a href='/reference/admin-ui-api/nav-menu/nav-menu-item#navmenuitem'>NavMenuItem</a>.
 

+ 1 - 1
docs/docs/reference/admin-ui-api/custom-detail-components/custom-detail-component-location-id.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CustomDetailComponentLocationId
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/component-registry-types.ts" sourceLine="113" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/component-registry-types.ts" sourceLine="114" packageName="@vendure/admin-ui" />
 
 The valid locations for embedding a <a href='/reference/admin-ui-api/custom-detail-components/custom-detail-component#customdetailcomponent'>CustomDetailComponent</a>.
 

+ 3 - 3
docs/docs/reference/admin-ui-api/directives/if-permissions-directive.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## IfPermissionsDirective
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/directives/if-permissions.directive.ts" sourceLine="33" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/directives/if-permissions.directive.ts" sourceLine="26" packageName="@vendure/admin-ui" />
 
 Conditionally shows/hides templates based on the current active user having the specified permission.
 Based on the ngIf source. Also support "else" templates:
@@ -28,7 +28,7 @@ must match (logical AND)
 
 ```ts title="Signature"
 class IfPermissionsDirective extends IfDirectiveBase<Array<Permission[] | null>> {
-    constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<any>, dataService: DataService, changeDetectorRef: ChangeDetectorRef)
+    constructor(_viewContainer: ViewContainerRef, templateRef: TemplateRef<any>, changeDetectorRef: ChangeDetectorRef, permissionsService: PermissionsService)
 }
 ```
 * Extends: <code>IfDirectiveBase&#60;Array&#60;<a href='/reference/typescript-api/common/permission#permission'>Permission</a>[] | null&#62;&#62;</code>
@@ -39,7 +39,7 @@ class IfPermissionsDirective extends IfDirectiveBase<Array<Permission[] | null>>
 
 ### constructor
 
-<MemberInfo kind="method" type={`(_viewContainer: ViewContainerRef, templateRef: TemplateRef&#60;any&#62;, dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>, changeDetectorRef: ChangeDetectorRef) => IfPermissionsDirective`}   />
+<MemberInfo kind="method" type={`(_viewContainer: ViewContainerRef, templateRef: TemplateRef&#60;any&#62;, changeDetectorRef: ChangeDetectorRef, permissionsService: PermissionsService) => IfPermissionsDirective`}   />
 
 
 

+ 3 - 3
docs/docs/reference/admin-ui-api/list-detail-views/base-detail-component.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## BaseDetailComponent
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="56" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="57" packageName="@vendure/admin-ui" />
 
 A base class for entity detail views. It should be used in conjunction with the
 <a href='/reference/admin-ui-api/list-detail-views/base-entity-resolver#baseentityresolver'>BaseEntityResolver</a>.
@@ -59,7 +59,7 @@ class BaseDetailComponent<Entity extends { id: string; updatedAt?: string }> imp
     id: string;
     abstract detailForm: UntypedFormGroup;
     protected destroy$ = new Subject<void>();
-    constructor(route: ActivatedRoute, router: Router, serverConfigService: ServerConfigService, dataService: DataService)
+    constructor(route: ActivatedRoute, router: Router, serverConfigService: ServerConfigService, dataService: DataService, permissionsService: PermissionsService)
     init() => ;
     setUpStreams() => ;
     destroy() => ;
@@ -119,7 +119,7 @@ class BaseDetailComponent<Entity extends { id: string; updatedAt?: string }> imp
 
 ### constructor
 
-<MemberInfo kind="method" type={`(route: ActivatedRoute, router: Router, serverConfigService: ServerConfigService, dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>) => BaseDetailComponent`}   />
+<MemberInfo kind="method" type={`(route: ActivatedRoute, router: Router, serverConfigService: ServerConfigService, dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>, permissionsService: PermissionsService) => BaseDetailComponent`}   />
 
 
 ### init

+ 1 - 1
docs/docs/reference/admin-ui-api/list-detail-views/base-list-component.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## BaseListComponent
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="39" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="40" packageName="@vendure/admin-ui" />
 
 This is a base class which implements the logic required to fetch and manipulate
 a list of data from a query which returns a PaginatedList type.

+ 10 - 10
docs/docs/reference/admin-ui-api/list-detail-views/detail-component-with-resolver.md

@@ -11,10 +11,10 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## detailComponentWithResolver
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="243" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="256" packageName="@vendure/admin-ui" />
 
-A helper function for creating tabs that point to a <a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>. This takes
-care of the route resolver parts so that the detail component automatically has access to the
+A helper function for creating tabs that point to a <a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>. This takes
+care of the route resolver parts so that the detail component automatically has access to the
 correct resolved detail data.
 
 *Example*
@@ -40,17 +40,17 @@ export class ProductSpecsUiExtensionModule {}
 ```
 
 ```ts title="Signature"
-function detailComponentWithResolver<T extends TypedDocumentNode<any, { id: string }>, Field extends keyof ResultOf<T>, R extends Field>(config: {
-    component: Type<TypedBaseDetailComponent<T, Field>>;
-    query: T;
-    entityKey: R;
-    getBreadcrumbs?: (entity: ResultOf<T>[R]) => BreadcrumbValue;
-    variables?: T extends TypedDocumentNode<any, infer V> ? Omit<V, 'id'> : never;
+function detailComponentWithResolver<T extends TypedDocumentNode<any, { id: string }>, Field extends keyof ResultOf<T>, R extends Field>(config: {
+    component: Type<TypedBaseDetailComponent<T, Field>>;
+    query: T;
+    entityKey: R;
+    getBreadcrumbs?: (entity: ResultOf<T>[R]) => BreadcrumbValue;
+    variables?: T extends TypedDocumentNode<any, infer V> ? Omit<V, 'id'> : never;
 }): void
 ```
 Parameters
 
 ### config
 
-<MemberInfo kind="parameter" type={`{
     component: Type&#60;<a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>&#60;T, Field&#62;&#62;;
     query: T;
     entityKey: R;
     getBreadcrumbs?: (entity: ResultOf&#60;T&#62;[R]) =&#62; BreadcrumbValue;
     variables?: T extends TypedDocumentNode&#60;any, infer V&#62; ? Omit&#60;V, 'id'&#62; : never;
 }`} />
+<MemberInfo kind="parameter" type={`{     component: Type&#60;<a href='/reference/admin-ui-api/list-detail-views/typed-base-detail-component#typedbasedetailcomponent'>TypedBaseDetailComponent</a>&#60;T, Field&#62;&#62;;     query: T;     entityKey: R;     getBreadcrumbs?: (entity: ResultOf&#60;T&#62;[R]) =&#62; BreadcrumbValue;     variables?: T extends TypedDocumentNode&#60;any, infer V&#62; ? Omit&#60;V, 'id'&#62; : never; }`} />
 

+ 1 - 1
docs/docs/reference/admin-ui-api/list-detail-views/typed-base-detail-component.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TypedBaseDetailComponent
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="179" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-detail.component.ts" sourceLine="186" packageName="@vendure/admin-ui" />
 
 A version of the <a href='/reference/admin-ui-api/list-detail-views/base-detail-component#basedetailcomponent'>BaseDetailComponent</a> which is designed to be used with a
 [TypedDocumentNode](https://the-guild.dev/graphql/codegen/plugins/typescript/typed-document-node).

+ 13 - 7
docs/docs/reference/admin-ui-api/list-detail-views/typed-base-list-component.md

@@ -13,7 +13,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 <GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/common/base-list.component.ts" sourceLine="199" packageName="@vendure/admin-ui" />
 
-A version of the <a href='/reference/admin-ui-api/list-detail-views/base-list-component#baselistcomponent'>BaseListComponent</a> which is designed to be used with a
+A version of the <a href='/reference/admin-ui-api/list-detail-views/base-list-component#baselistcomponent'>BaseListComponent</a> which is designed to be used with a
 [TypedDocumentNode](https://the-guild.dev/graphql/codegen/plugins/typescript/typed-document-node).
 
 ```ts title="Signature"
@@ -23,12 +23,13 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
     protected dataService = inject(DataService);
     protected router = inject(Router);
     protected serverConfigService = inject(ServerConfigService);
+    protected permissionsService = inject(PermissionsService);
     constructor()
-    configure(config: {
-        document: T;
-        getItems: (data: ResultOf<T>) => { items: Array<ItemOf<ResultOf<T>, Field>>; totalItems: number };
-        setVariables?: (skip: number, take: number) => VariablesOf<T>;
-        refreshListOnChanges?: Array<Observable<any>>;
+    configure(config: {
+        document: T;
+        getItems: (data: ResultOf<T>) => { items: Array<ItemOf<ResultOf<T>, Field>>; totalItems: number };
+        setVariables?: (skip: number, take: number) => VariablesOf<T>;
+        refreshListOnChanges?: Array<Observable<any>>;
     }) => ;
     ngOnInit() => ;
     createFilterCollection() => DataTableFilterCollection<NonNullable<NonNullable<Vars['options']>['filter']>>;
@@ -71,6 +72,11 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
 <MemberInfo kind="property" type={``}   />
 
 
+### permissionsService
+
+<MemberInfo kind="property" type={``}   />
+
+
 ### constructor
 
 <MemberInfo kind="method" type={`() => TypedBaseListComponent`}   />
@@ -78,7 +84,7 @@ class TypedBaseListComponent<T extends TypedDocumentNode<any, Vars>, Field exten
 
 ### configure
 
-<MemberInfo kind="method" type={`(config: {
         document: T;
         getItems: (data: ResultOf&#60;T&#62;) =&#62; { items: Array&#60;ItemOf&#60;ResultOf&#60;T&#62;, Field&#62;&#62;; totalItems: number };
         setVariables?: (skip: number, take: number) =&#62; VariablesOf&#60;T&#62;;
         refreshListOnChanges?: Array&#60;Observable&#60;any&#62;&#62;;
     }) => `}   />
+<MemberInfo kind="method" type={`(config: {         document: T;         getItems: (data: ResultOf&#60;T&#62;) =&#62; { items: Array&#60;ItemOf&#60;ResultOf&#60;T&#62;, Field&#62;&#62;; totalItems: number };         setVariables?: (skip: number, take: number) =&#62; VariablesOf&#60;T&#62;;         refreshListOnChanges?: Array&#60;Observable&#60;any&#62;&#62;;     }) => `}   />
 
 
 ### ngOnInit

+ 3 - 3
docs/docs/reference/admin-ui-api/pipes/has-permission-pipe.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## HasPermissionPipe
 
-<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/pipes/has-permission.pipe.ts" sourceLine="17" packageName="@vendure/admin-ui" />
+<GenerationInfo sourceFile="packages/admin-ui/src/lib/core/src/shared/pipes/has-permission.pipe.ts" sourceLine="16" packageName="@vendure/admin-ui" />
 
 A pipe which checks the provided permission against all the permissions of the current user.
 Returns `true` if the current user has that permission.
@@ -24,7 +24,7 @@ Returns `true` if the current user has that permission.
 
 ```ts title="Signature"
 class HasPermissionPipe implements PipeTransform, OnDestroy {
-    constructor(dataService: DataService, changeDetectorRef: ChangeDetectorRef)
+    constructor(permissionsService: PermissionsService, changeDetectorRef: ChangeDetectorRef)
     transform(input: string | string[]) => any;
     ngOnDestroy() => ;
 }
@@ -37,7 +37,7 @@ class HasPermissionPipe implements PipeTransform, OnDestroy {
 
 ### constructor
 
-<MemberInfo kind="method" type={`(dataService: <a href='/reference/admin-ui-api/services/data-service#dataservice'>DataService</a>, changeDetectorRef: ChangeDetectorRef) => HasPermissionPipe`}   />
+<MemberInfo kind="method" type={`(permissionsService: PermissionsService, changeDetectorRef: ChangeDetectorRef) => HasPermissionPipe`}   />
 
 
 ### transform

+ 1 - 1
docs/docs/reference/typescript-api/common/currency-code.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CurrencyCode
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="968" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="969" packageName="@vendure/common" />
 
 ISO 4217 currency code
 

+ 1 - 1
docs/docs/reference/typescript-api/common/job-state.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## JobState
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2125" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2131" packageName="@vendure/common" />
 
 The state of a Job in the JobQueue
 

+ 1 - 1
docs/docs/reference/typescript-api/common/language-code.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## LanguageCode
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2143" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="2149" packageName="@vendure/common" />
 
 Languages in the form of a ISO 639-1 language code with optional
 region or script modifier (e.g. de_AT). The selection available is based

+ 1 - 1
docs/docs/reference/typescript-api/common/permission.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## Permission
 
-<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4269" packageName="@vendure/common" />
+<GenerationInfo sourceFile="packages/common/src/generated-types.ts" sourceLine="4277" packageName="@vendure/common" />
 
 Permissions for administrators and customers. Used to control access to
 GraphQL resolvers via the <a href='/reference/typescript-api/request/allow-decorator#allow'>Allow</a> decorator.

+ 1 - 1
docs/docs/reference/typescript-api/custom-fields/custom-field-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CustomFieldConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="114" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="124" packageName="@vendure/core" />
 
 An object used to configure a custom field.
 

+ 2 - 3
docs/docs/reference/typescript-api/custom-fields/index.md

@@ -11,10 +11,9 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## CustomFields
 
-<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="149" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="159" packageName="@vendure/core" />
 
-Most entities can have additional fields added to them by defining an array of <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>
-objects on against the corresponding key.
+Most entities can have additional fields added to them by defining an array of <a href='/reference/typescript-api/custom-fields/custom-field-config#customfieldconfig'>CustomFieldConfig</a>objects on against the corresponding key.
 
 *Example*
 

+ 1 - 1
docs/docs/reference/typescript-api/custom-fields/typed-custom-single-field-config.md

@@ -11,7 +11,7 @@ import MemberDescription from '@site/src/components/MemberDescription';
 
 ## TypedCustomSingleFieldConfig
 
-<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="56" packageName="@vendure/core" />
+<GenerationInfo sourceFile="packages/core/src/config/custom-field/custom-field-types.ts" sourceLine="66" packageName="@vendure/core" />
 
 Configures a custom field on an entity in the <a href='/reference/typescript-api/custom-fields/#customfields'>CustomFields</a> config object.
 

+ 4 - 4
docs/docs/reference/typescript-api/events/event-types.md

@@ -1217,14 +1217,14 @@ class ProductVariantEvent extends VendureEntityEvent<ProductVariant[], ProductVa
 This event is fired whenever a <a href='/reference/typescript-api/entities/product-variant-price#productvariantprice'>ProductVariantPrice</a> is added, updated or deleted.
 
 ```ts title="Signature"
-class ProductVariantPriceEvent extends VendureEntityEvent<
-    ProductVariantPrice[],
-    ProductVariantInputTypes
+class ProductVariantPriceEvent extends VendureEntityEvent<
+    ProductVariantPrice[],
+    ProductVariantInputTypes
 > {
     constructor(ctx: RequestContext, entity: ProductVariantPrice[], type: 'created' | 'updated' | 'deleted', input?: ProductVariantInputTypes)
 }
 ```
-* Extends: <code><a href='/reference/typescript-api/events/vendure-entity-event#vendureentityevent'>VendureEntityEvent</a>&#60;     <a href='/reference/typescript-api/entities/product-variant-price#productvariantprice'>ProductVariantPrice</a>[],     ProductVariantInputTypes &#62;</code>
+* Extends: <code><a href='/reference/typescript-api/events/vendure-entity-event#vendureentityevent'>VendureEntityEvent</a>&#60;
     <a href='/reference/typescript-api/entities/product-variant-price#productvariantprice'>ProductVariantPrice</a>[],
     ProductVariantInputTypes
 &#62;</code>
 
 
 

+ 10 - 10
docs/docs/reference/typescript-api/testing/simple-graph-qlclient.md

@@ -27,10 +27,10 @@ class SimpleGraphQLClient {
     asUserWithCredentials(username: string, password: string) => ;
     asSuperAdmin() => ;
     asAnonymousUser() => ;
-    fileUploadMutation(options: {
-        mutation: DocumentNode;
-        filePaths: string[];
-        mapVariables: (filePaths: string[]) => any;
+    fileUploadMutation(options: {
+        mutation: DocumentNode;
+        filePaths: string[];
+        mapVariables: (filePaths: string[]) => any;
     }) => Promise<any>;
 }
 ```
@@ -66,8 +66,8 @@ Performs both query and mutation operations.
 
 <MemberInfo kind="method" type={`(url: string, options: RequestInit = {}) => Promise&#60;Response&#62;`}   />
 
-Performs a raw HTTP request to the given URL, but also includes the authToken & channelToken
-headers if they have been set. Useful for testing non-GraphQL endpoints, e.g. for plugins
+Performs a raw HTTP request to the given URL, but also includes the authToken & channelToken
+headers if they have been set. Useful for testing non-GraphQL endpoints, e.g. for plugins
 which make use of REST controllers.
 ### queryStatus
 
@@ -91,11 +91,11 @@ Logs in as the SuperAdmin user.
 Logs out so that the client is then treated as an anonymous user.
 ### fileUploadMutation
 
-<MemberInfo kind="method" type={`(options: {
         mutation: DocumentNode;
         filePaths: string[];
         mapVariables: (filePaths: string[]) =&#62; any;
     }) => Promise&#60;any&#62;`}   />
+<MemberInfo kind="method" type={`(options: {         mutation: DocumentNode;         filePaths: string[];         mapVariables: (filePaths: string[]) =&#62; any;     }) => Promise&#60;any&#62;`}   />
 
-Perform a file upload mutation.
-
-Upload spec: https://github.com/jaydenseric/graphql-multipart-request-spec
+Perform a file upload mutation.
+
+Upload spec: https://github.com/jaydenseric/graphql-multipart-request-spec
 Discussion of issue: https://github.com/jaydenseric/apollo-upload-client/issues/32
 
 

+ 2 - 1
packages/admin-ui/src/lib/core/src/common/component-registry-types.ts

@@ -98,7 +98,8 @@ export type PageLocationId =
 
 /**
  * @description
- * The valid locationIds for registering action bar items.
+ * The valid locationIds for registering action bar items. For a list of
+ * values, see {@link PageLocationId}.
  *
  * @docsCategory action-bar
  */

+ 2 - 0
packages/admin-ui/src/lib/core/src/extension/add-action-bar-dropdown-menu-item.ts

@@ -20,6 +20,8 @@ import { NavBuilderService } from '../providers/nav-builder/nav-builder.service'
  *     }),
  * ];
  * ```
+ *
+ * @since 2.2.0
  * @docsCategory action-bar
  */
 export function addActionBarDropdownMenuItem(config: ActionBarDropdownMenuItem): Provider {

+ 33 - 0
packages/admin-ui/src/lib/core/src/providers/nav-builder/nav-builder-types.ts

@@ -161,7 +161,16 @@ export interface ActionBarItem {
      * A unique identifier for the item.
      */
     id: string;
+    /**
+     * @description
+     * The label to display for the item. This can also be a translation token,
+     * e.g. `invoice-plugin.print-invoice`.
+     */
     label: string;
+    /**
+     * @description
+     * The location in the UI where this button should be displayed.
+     */
     locationId: ActionBarLocationId;
     /**
      * @description
@@ -181,15 +190,23 @@ export interface ActionBarItem {
     routerLink?: RouterLinkDefinition;
     buttonColor?: 'primary' | 'success' | 'warning';
     buttonStyle?: 'solid' | 'outline' | 'link';
+    /**
+     * @description
+     * An optional icon to display in the button. The icon
+     * should be a valid shape name from the [Clarity Icons](https://core.clarity.design/foundation/icons/shapes/)
+     * set.
+     */
     icon?: string;
     /**
      * @description
      * Control the display of this item based on the user permissions. Note: if you attempt to pass a
      * {@link PermissionDefinition} object, you will get a compilation error. Instead, pass the plain
      * string version. For example, if the permission is defined as:
+     *
      * ```ts
      * export const MyPermission = new PermissionDefinition('ProductReview');
      * ```
+     *
      * then the generated permission strings will be:
      *
      * - `CreateProductReview`
@@ -213,7 +230,16 @@ export interface ActionBarDropdownMenuItem {
      * A unique identifier for the item.
      */
     id: string;
+    /**
+     * @description
+     * The label to display for the item. This can also be a translation token,
+     * e.g. `invoice-plugin.print-invoice`.
+     */
     label: string;
+    /**
+     * @description
+     * The location in the UI where this menu item should be displayed.
+     */
     locationId: ActionBarLocationId;
     /**
      * @description
@@ -228,12 +254,19 @@ export interface ActionBarDropdownMenuItem {
     buttonState?: (context: ActionBarContext) => Observable<ActionBarButtonState | undefined>;
     onClick?: (event: MouseEvent, context: ActionBarContext) => void;
     routerLink?: RouterLinkDefinition;
+    /**
+     * @description
+     * An optional icon to display with the item. The icon
+     * should be a valid shape name from the [Clarity Icons](https://core.clarity.design/foundation/icons/shapes/)
+     * set.
+     */
     icon?: string;
     /**
      * @description
      * Control the display of this item based on the user permissions. Note: if you attempt to pass a
      * {@link PermissionDefinition} object, you will get a compilation error. Instead, pass the plain
      * string version. For example, if the permission is defined as:
+     *
      * ```ts
      * export const MyPermission = new PermissionDefinition('ProductReview');
      * ```