Kaynağa Gözat

feat(docs): Expand ui extensions documentation

Michael Bromley 5 yıl önce
ebeveyn
işleme
8e31a3d69a

+ 14 - 305
docs/content/docs/plugins/extending-the-admin-ui/_index.md

@@ -1,145 +1,43 @@
 ---
 title: 'Extending the Admin UI'
-weight: 3
+weight: 5
 ---
 
 # Extending the Admin UI
 
-When creating a plugin, you may wish to extend the Admin UI in order to expose an interface to the plugin's functionality.
+When creating a plugin, you may wish to extend the Admin UI in order to expose a graphical interface to the plugin's functionality.
 
-This is possible by defining [AdminUiExtensions]({{< ref "admin-ui-extension" >}}). A UI extension is an [Angular module](https://angular.io/guide/ngmodules) which gets compiled into the Admin UI application bundle by the [`compileUiExtensions`]({{< relref "compile-ui-extensions" >}}) function exported by the `@vendure/ui-devkit` package.
-
-{{% alert warning %}}
-Note: a basic understanding of [Angular](https://angular.io/) is required to successfully work with UI extensions. Try [Angular's "Getting Started" guide](https://angular.io/start) to learn more.
-{{% /alert %}}
+This is possible by defining [AdminUiExtensions]({{< ref "admin-ui-extension" >}}). 
 
 {{% alert "primary" %}}
 For a complete working example of a Vendure plugin which extends the Admin UI, see the [real-world-vendure Reviews plugin](https://github.com/vendure-ecommerce/real-world-vendure/tree/master/src/plugins/reviews)
 {{% /alert %}}
 
-## Simple Example
-
-Here is a very simple example to illustrate how a UI extension works:
-
-### 1. Install `@vendure/ui-devkit`
-
-To create UI extensions, you'll need to install the `@vendure/ui-devkit` package. This package contains a compiler for building your
-customized version of the Admin UI, as well as the Angular dependencies you'll need to create your extensions.
-
-```bash
-yarn add @vendure/ui-devkit
-
-# or
-
-npm install @vendure/ui-devkit
-```
-
-### 2. Create a simple component
-
-Here's a very simple Angular component which displays a greeting:
-
-```TypeScript
-// project/ui-extensions/greeter.component.ts
-import { Component } from '@angular/core';
-
-@Component({
-  selector: 'greeter',
-  template: `<h1>{{ greeting }}</h1>`,
-})
-export class GreeterComponent {
-  greeting = 'Hello!';
-}
-```
-
-### 3. Create the Angular module
-
-Next we need to declare an Angular module to house the component:
-
-```TypeScript
-// project/ui-extensions/greeter.module.ts
-import { NgModule } from '@angular/core';
-import { RouterModule } from '@angular/router';
-import { SharedModule } from '@vendure/admin-ui/core';
-import { GreeterComponent } from './greeter.component';
-
-@NgModule({
-  imports: [
-    SharedModule,
-    RouterModule.forChild([{
-      path: '',
-      pathMatch: 'full',
-      component: GreeterComponent,
-      data: { breadcrumb: 'Greeter' },
-    }]),
-  ],
-  declarations: [GreeterComponent],
-})
-export class GreeterModule {}
-```
-
-{{% alert "primary" %}}
-**Note:** The `SharedModule` should, in general, always be imported by your extension modules. It provides the basic Angular
-directives and other common functionality that any extension would require.
-{{% /alert %}}
-
-### 4. Pass the extension to the `compileUiExtensions` function
-
-Now we need to tell the `compileUiExtensions` function where to find the extension, and which file contains the NgModule itself (since a non-trivial UI extension will likely contain multiple files).
-
-```TypeScript
-// project/vendure-config.ts
-import path from 'path';
-import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
-import { VendureConfig } from '@vendure/core';
-import { compileUiExtensions } from '@vendure/ui-devkit/compiler';
-
-export const config: VendureConfig = {
-  // ...
-  plugins: [
-    AdminUiPlugin.init({
-      port: 5001,
-      app: compileUiExtensions({
-        outputPath: path.join(__dirname, 'admin-ui'),
-        extensions: [{
-          extensionPath: path.join(__dirname, 'ui-extensions'),
-          ngModules: [{
-            type: 'lazy',
-            route: 'greet',
-            ngModuleFileName: 'greeter.module.ts',
-            ngModuleName: 'GreeterModule',
-          }],
-        }],
-      }),
-    }),
-  ],
-}
-```
-
-### 5. Start the server to compile
+## How It Works
 
-The `compileUiExtensions()` function returns a `compile()` function which will be invoked by the AdminUiPlugin upon server bootstrap. During this compilation process, a new directory will be generated at `/admin-ui` (as specified by the `outputPath` option) which will contains the uncompiled sources of your new Admin UI app.
+A UI extension is an [Angular module](https://angular.io/guide/ngmodules) which gets compiled into the Admin UI application bundle by the [`compileUiExtensions`]({{< relref "compile-ui-extensions" >}}) function exported by the `@vendure/ui-devkit` package. Internally, the ui-devkit package makes use of the Angular CLI to compile an optimized set of JavaScript bundles containing your extensions. 
 
-Next, these source files will be run through the Angular compiler, the output of which will be visible in the console.
+## Use Your Favourite Framework
 
-```console
-info 9/25/19, 09:54 - [AdminUiPlugin] Compiling Admin UI app in production mode...
+The Vendure Admin UI is build with Angular, and writing UI extensions in Angular is seamless and powerful. But if you are not familiar with Angular, that's no problem! You can write UI extensions using **React**, **Vue**, or **any other** web technology of choice!
 
-<output of Angular CLI compiler>
+* [UI extensions in Angular]({{< relref "using-angular" >}})
+* [UI extensions in other frameworks]({{< relref "using-other-frameworks" >}})
 
-info 9/25/19, 09:55 - [AdminUiPlugin] Admin UI successfully compiled
-```
+## Lazy vs Shared Modules
 
-Now go to the Admin UI app in your browser and log in. You should now be able to manually enter the URL `http://localhost:3000/admin/extensions/greet` and you should see the component with the "Hello!" header:
+Angular uses the concept of modules ([NgModules](https://angular.io/guide/ngmodules)) for organizing related code. These modules can be lazily-loaded, which means that the code is not loaded when the app starts, but only later once that code is required. This keeps the main bundle small and improves performance.
 
-{{< figure src="./ui-extensions-greeter.jpg" >}}
+When creating your UI extensions, you can set your module to be either `lazy` or `shared`. Shared modules are loaded _eagerly_, i.e. their code is bundled up with the main app and loaded as soon as the app loads. 
 
+As a rule, modules defining new routes should be lazily loaded (so that the code is only loaded once that route is activated), and modules defining [new navigations items]({{< relref "adding-navigation-items" >}}) and [CustomField controls]({{< relref "custom-field-controls" >}}) should be set to `shared`.
 
 ## Dev mode
 
 When you are developing your Admin UI extension, you can set the `devMode` option to `true` which will compile the Admin UI app in development mode, and recompile and auto-refresh the browser on any changes to your extension source files.
 
 ```TypeScript
-// project/vendure-config.ts
+// vendure-config.ts
 plugins: [
   AdminUiPlugin.init({
     port: 3002,
@@ -154,195 +52,6 @@ plugins: [
 ],
 ```
 
-
-## Lazy vs Shared Modules
-
-In the above example we set the `type` to `'lazy'`. A lazy module is not loaded when the Admin UI app is bootstrapped. It is only lazily-loaded when the `/extensions/greet` route is activated. This allows you to create large, complex extensions without impacting the initial load time of your Admin UI.
-
-The other kind of extension module is `'shared'`. These are not lazily-loaded and are used to customize the shared interface elements such as the navigation menu items. Let's explore this by adding a menu item for our Greeter module.
-
-## Custom navigation
-
-### Extending the NavMenu
-
-Once you have defined some custom routes in a lazy extension module, you need some way for the administrator to access them. For this you will use the `addNavMenuItem` and `addNavMenuSection` functions. Let's add a new section to the Admin UI main nav bar containing a link to the lazy module from the simple example above:
-
-```TypeScript
-// project/ui-extensions/greeter-shared.module.ts
-import { NgModule } from '@angular/core';
-import { SharedModule, addNavMenuSection } from '@vendure/admin-ui/core';
-
-@NgModule({
-  imports: [SharedModule],
-  providers: [
-    addNavMenuSection({
-      id: 'greeter',
-      label: 'My Extensions',
-      items: [{
-        id: 'greeter',
-        label: 'Greeter',
-        routerLink: ['/extensions/greet'],
-        // Icon can be any of https://clarity.design/icons
-        icon: 'cursor-hand-open',
-      }],
-    },
-    // Add this section before the "settings" section
-    'settings'),
-  ]
-})
-export class GreeterSharedModule {}
-```
-
-Now we must also register this new module with the compiler:
-
-```TypeScript
-// project/vendure-config.ts
-
-ngModules: [
-  {
-    type: 'lazy',
-    route: 'greet',
-    ngModuleFileName: 'greeter.module.ts',
-    ngModuleName: 'GreeterModule',
-  },
-  {
-    type: 'shared',
-    ngModuleFileName: 'greeter-shared.module.ts',
-    ngModuleName: 'GreeterSharedModule',
-  }
-],
-```
-
-Running the server will compile our new shared module into the app, and the result should look like this:
-
-{{< figure src="./ui-extensions-navbar.jpg" >}}
-
-### Adding new ActionBar buttons
-
-It may not always make sense to navigate to your extension view from the main nav menu. For example, a "product reviews" extension that shows reviews for a particular product. In this case, you can add new buttons to the "ActionBar", which is the horizontal section at the top of each screen containing the primary actions for that view.
-
-Here's an example of how this is done:
-
-```TypeScript
-import { NgModule } from '@angular/core';
-import { SharedModule, addActionBarItem } from '@vendure/admin-ui/core';
-
-@NgModule({
-  imports: [SharedModule],
-  providers: [
-    addActionBarItem({
-       id: 'product-reviews',
-       label: 'Product reviews',
-       locationId: 'product-detail',
-       buttonStyle: 'outline',
-       routerLink: ['./reviews'],
-       requiresPermission: 'SuperAdmin'
-    }),
-  ],
-})
-export class SharedExtensionModule {}
-```
-
-{{< figure src="./ui-extensions-actionbar.jpg" >}}
-
-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. Here is a complete list of available locations into which you can add new ActionBar buttons:
-
-```text
-asset-list
-collection-detail
-collection-list
-facet-detail
-facet-list
-product-detail
-product-list
-customer-detail
-customer-list
-promotion-detail
-promotion-list
-order-detail
-order-list
-administrator-detail
-administrator-list
-channel-detail
-channel-list
-country-detail
-country-list
-global-settings-detail
-payment-method-detail
-payment-method-list
-role-detail
-role-list
-shipping-method-detail
-shipping-method-list
-tax-category-detail
-tax-category-list
-tax-rate-detail
-tax-rate-list
-```
-
-## CustomField controls
-
-Another way to extend the Admin UI app is to define custom form control components for manipulating any [Custom Fields]({{< ref "/docs/typescript-api/custom-fields" >}}) you have defined on your entities.
-
-Let's say you define a custom "intensity" field on the Product entity:
-
-```TypeScript
-// project/vendure-config.ts
-
-customFields: {
-  Product: [
-    { name: 'intensity', type: 'int', min: 0, max: 100, defaultValue: 0 },
-  ],
-}
-```
-
-By default, the "intensity" field will be displayed as a number input:
-
-{{< figure src="./ui-extensions-custom-field-default.jpg" >}}
-
-But let's say we want to display a range slider instead. Here's how we can do this using our shared extension module combined with the `registerCustomFieldComponent()` function:
-
-```TypeScript
-import { NgModule, Component } from '@angular/core';
-import { FormControl } from '@angular/forms';
-import { SharedModule, CustomFieldControl, 
-  CustomFieldConfigType, registerCustomFieldComponent } from '@vendure/admin-ui/core';
-
-@Component({
-  template: `
-    <input
-        type="range"
-        [min]="customFieldConfig.intMin"
-        [max]="customFieldConfig.intMax"
-        [formControl]="formControl" />
-    {{ formControl.value }}
-  `,
-})
-export class SliderControl implements CustomFieldControl {
-  customFieldConfig: CustomFieldConfigType;
-  formControl: FormControl;
-}
-
-@NgModule({
-  imports: [SharedModule],
-  declarations: [SliderControl],
-  providers: [
-    registerCustomFieldComponent('Product', 'intensity', SliderControl),
-  ]
-})
-export class SharedExtensionModule { }
-```
-
-Re-compiling the Admin UI will result in our SliderControl now being used for the "intensity" custom field:
-
-{{< figure src="./ui-extensions-custom-field-slider.jpg" >}}
-
-To recap the steps involved:
-
-1. Create an Angular Component which implements the `CustomFieldControl` interface.
-2. Add this component to your shared extension module's `declarations` array.
-3. Use `registerCustomFieldComponent()` to register your component for the given entity & custom field name.
-
 ## Compiling as a deployment step
 
 Although the examples so far all use the `compileUiExtensions` function in conjunction with the AdminUiPlugin, it is also possible to use it on its own:

+ 126 - 0
docs/content/docs/plugins/extending-the-admin-ui/adding-navigation-items/_index.md

@@ -0,0 +1,126 @@
+---
+title: 'Adding Navigation Items'
+weight: 5
+---
+
+# Adding Navigation Items
+
+## Extending the NavMenu
+
+Once you have defined some custom routes in a lazy extension module, you need some way for the administrator to access them. For this you will use the `addNavMenuItem` and `addNavMenuSection` functions. 
+
+Let's add a new section to the Admin UI main nav bar containing a link to the "greeter" module from the [Using Angular]({{< relref "../using-angular" >}}) example:
+
+```TypeScript
+// project/ui-extensions/greeter-shared.module.ts
+import { NgModule } from '@angular/core';
+import { SharedModule, addNavMenuSection } from '@vendure/admin-ui/core';
+
+@NgModule({
+  imports: [SharedModule],
+  providers: [
+    addNavMenuSection({
+      id: 'greeter',
+      label: 'My Extensions',
+      items: [{
+        id: 'greeter',
+        label: 'Greeter',
+        routerLink: ['/extensions/greet'],
+        // Icon can be any of https://clarity.design/icons
+        icon: 'cursor-hand-open',
+      }],
+    },
+    // Add this section before the "settings" section
+    'settings'),
+  ]
+})
+export class GreeterSharedModule {}
+```
+
+Now we must also register this new module with the compiler:
+
+```TypeScript
+// project/vendure-config.ts
+
+ngModules: [
+  {
+    type: 'lazy',
+    route: 'greet',
+    ngModuleFileName: 'greeter.module.ts',
+    ngModuleName: 'GreeterModule',
+  },
+  {
+    type: 'shared',
+    ngModuleFileName: 'greeter-shared.module.ts',
+    ngModuleName: 'GreeterSharedModule',
+  }
+],
+```
+
+Running the server will compile our new shared module into the app, and the result should look like this:
+
+{{< figure src="./ui-extensions-navbar.jpg" >}}
+
+
+## Adding new ActionBar buttons
+
+It may not always make sense to navigate to your extension view from the main nav menu. For example, a "product reviews" extension that shows reviews for a particular product. In this case, you can add new buttons to the "ActionBar", which is the horizontal section at the top of each screen containing the primary actions for that view.
+
+Here's an example of how this is done:
+
+```TypeScript
+import { NgModule } from '@angular/core';
+import { SharedModule, addActionBarItem } from '@vendure/admin-ui/core';
+
+@NgModule({
+  imports: [SharedModule],
+  providers: [
+    addActionBarItem({
+       id: 'product-reviews',
+       label: 'Product reviews',
+       locationId: 'product-detail',
+       buttonStyle: 'outline',
+       routerLink: ['./reviews'],
+       requiresPermission: 'SuperAdmin'
+    }),
+  ],
+})
+export class SharedExtensionModule {}
+```
+
+{{< figure src="./ui-extensions-actionbar.jpg" >}}
+
+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. Here is a complete list of available locations into which you can add new ActionBar buttons:
+
+```text
+asset-list
+collection-detail
+collection-list
+facet-detail
+facet-list
+product-detail
+product-list
+customer-detail
+customer-list
+promotion-detail
+promotion-list
+order-detail
+order-list
+administrator-detail
+administrator-list
+channel-detail
+channel-list
+country-detail
+country-list
+global-settings-detail
+payment-method-detail
+payment-method-list
+role-detail
+role-list
+shipping-method-detail
+shipping-method-list
+tax-category-detail
+tax-category-list
+tax-rate-detail
+tax-rate-list
+```

+ 0 - 0
docs/content/docs/plugins/extending-the-admin-ui/ui-extensions-actionbar.jpg → docs/content/docs/plugins/extending-the-admin-ui/adding-navigation-items/ui-extensions-actionbar.jpg


+ 0 - 0
docs/content/docs/plugins/extending-the-admin-ui/ui-extensions-navbar.jpg → docs/content/docs/plugins/extending-the-admin-ui/adding-navigation-items/ui-extensions-navbar.jpg


+ 68 - 0
docs/content/docs/plugins/extending-the-admin-ui/custom-field-controls/_index.md

@@ -0,0 +1,68 @@
+---
+title: 'CustomField Controls'
+weight: 5
+---
+
+# CustomField Controls
+
+Another way to extend the Admin UI app is to define custom form control components for manipulating any [Custom Fields]({{< ref "/docs/typescript-api/custom-fields" >}}) you have defined on your entities.
+
+Let's say you define a custom "intensity" field on the Product entity:
+
+```TypeScript
+// project/vendure-config.ts
+
+customFields: {
+  Product: [
+    { name: 'intensity', type: 'int', min: 0, max: 100, defaultValue: 0 },
+  ],
+}
+```
+
+By default, the "intensity" field will be displayed as a number input:
+
+{{< figure src="./ui-extensions-custom-field-default.jpg" >}}
+
+But let's say we want to display a range slider instead. Here's how we can do this using our shared extension module combined with the `registerCustomFieldComponent()` function:
+
+```TypeScript
+import { NgModule, Component } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { SharedModule, CustomFieldControl, 
+  CustomFieldConfigType, registerCustomFieldComponent } from '@vendure/admin-ui/core';
+
+@Component({
+  template: `
+    <input
+        type="range"
+        [min]="customFieldConfig.intMin"
+        [max]="customFieldConfig.intMax"
+        [formControl]="formControl" />
+    {{ formControl.value }}
+  `,
+})
+export class SliderControl implements CustomFieldControl {
+  customFieldConfig: CustomFieldConfigType;
+  formControl: FormControl;
+}
+
+@NgModule({
+  imports: [SharedModule],
+  declarations: [SliderControl],
+  providers: [
+    registerCustomFieldComponent('Product', 'intensity', SliderControl),
+  ]
+})
+export class SharedExtensionModule { }
+```
+
+Re-compiling the Admin UI will result in our SliderControl now being used for the "intensity" custom field:
+
+{{< figure src="./ui-extensions-custom-field-slider.jpg" >}}
+
+To recap the steps involved:
+
+1. Create an Angular Component which implements the `CustomFieldControl` interface.
+2. Add this component to your shared extension module's `declarations` array.
+3. Use `registerCustomFieldComponent()` to register your component for the given entity & custom field name.
+

+ 0 - 0
docs/content/docs/plugins/extending-the-admin-ui/ui-extensions-custom-field-default.jpg → docs/content/docs/plugins/extending-the-admin-ui/custom-field-controls/ui-extensions-custom-field-default.jpg


+ 0 - 0
docs/content/docs/plugins/extending-the-admin-ui/ui-extensions-custom-field-slider.jpg → docs/content/docs/plugins/extending-the-admin-ui/custom-field-controls/ui-extensions-custom-field-slider.jpg


+ 123 - 0
docs/content/docs/plugins/extending-the-admin-ui/using-angular/_index.md

@@ -0,0 +1,123 @@
+---
+title: 'Using Angular'
+weight: 0
+---
+
+# UI Extensions with Angular
+
+Writing your UI extensions with Angular results in the best-optimized and most seamless UI extensions, since you can re-use shared components exported by the `@vendure/admin-ui/core` library, and the Angular framework itself is already present in the app.
+
+{{% alert warning %}}
+**Note:** an understanding of [Angular](https://angular.io/) is necessary for successfully working with Angular-based UI extensions. Try [Angular's "Getting Started" guide](https://angular.io/start) to learn more.
+{{% /alert %}}
+
+## 1. Install `@vendure/ui-devkit`
+
+To create UI extensions, you'll need to install the `@vendure/ui-devkit` package. This package contains a compiler for building your customized version of the Admin UI, as well as the Angular dependencies you'll need to create your extensions.
+
+```bash
+yarn add @vendure/ui-devkit
+
+# or
+
+npm install @vendure/ui-devkit
+```
+
+## 2. Create a simple component
+
+Here's a very simple Angular component which displays a greeting:
+
+```TypeScript
+// project/ui-extensions/greeter.component.ts
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'greeter',
+  template: `<h1>{{ greeting }}</h1>`,
+})
+export class GreeterComponent {
+  greeting = 'Hello!';
+}
+```
+
+## 3. Create the Angular module
+
+Next we need to declare an Angular module to house the component:
+
+```TypeScript
+// project/ui-extensions/greeter.module.ts
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { SharedModule } from '@vendure/admin-ui/core';
+import { GreeterComponent } from './greeter.component';
+
+@NgModule({
+  imports: [
+    SharedModule,
+    RouterModule.forChild([{
+      path: '',
+      pathMatch: 'full',
+      component: GreeterComponent,
+      data: { breadcrumb: 'Greeter' },
+    }]),
+  ],
+  declarations: [GreeterComponent],
+})
+export class GreeterModule {}
+```
+
+{{% alert "primary" %}}
+**Note:** The `SharedModule` should, in general, always be imported by your extension modules. It provides the basic Angular
+directives and other common functionality that any extension would require.
+{{% /alert %}}
+
+## 4. Pass the extension to the `compileUiExtensions` function
+
+Now we need to tell the `compileUiExtensions` function where to find the extension, and which file contains the NgModule itself (since a non-trivial UI extension will likely contain multiple files).
+
+```TypeScript
+// project/vendure-config.ts
+import path from 'path';
+import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
+import { VendureConfig } from '@vendure/core';
+import { compileUiExtensions } from '@vendure/ui-devkit/compiler';
+
+export const config: VendureConfig = {
+  // ...
+  plugins: [
+    AdminUiPlugin.init({
+      port: 5001,
+      app: compileUiExtensions({
+        outputPath: path.join(__dirname, '__admin-ui'),
+        extensions: [{
+          extensionPath: path.join(__dirname, 'ui-extensions'),
+          ngModules: [{
+            type: 'lazy',
+            route: 'greet',
+            ngModuleFileName: 'greeter.module.ts',
+            ngModuleName: 'GreeterModule',
+          }],
+        }],
+      }),
+    }),
+  ],
+}
+```
+
+## 5. Start the server to compile
+
+The `compileUiExtensions()` function returns a `compile()` function which will be invoked by the AdminUiPlugin upon server bootstrap. During this compilation process, a new directory will be generated at `/__admin-ui` (as specified by the `outputPath` option) which will contains the uncompiled sources of your new Admin UI app.
+
+Next, these source files will be run through the Angular compiler, the output of which will be visible in the console.
+
+{{% alert "warning" %}}
+**Note:** The first time the compiler is run, an additional step ([compatibility compiler](https://angular.io/guide/ivy#ivy-and-libraries)) is run to make sure all dependencies work with the latest version of Angular. This step can take up to a few minutes.
+{{% /alert %}}
+
+Now go to the Admin UI app in your browser and log in. You should now be able to manually enter the URL `http://localhost:3000/admin/extensions/greet` and you should see the component with the "Hello!" header:
+
+{{< figure src="./ui-extensions-greeter.jpg" >}}
+
+## Next Steps
+
+Now you have created your new route, you need a way for your admin to access it. See [Adding Navigation Items]({{< relref "../adding-navigation-items" >}})

+ 0 - 0
docs/content/docs/plugins/extending-the-admin-ui/ui-extensions-greeter.jpg → docs/content/docs/plugins/extending-the-admin-ui/using-angular/ui-extensions-greeter.jpg


+ 210 - 0
docs/content/docs/plugins/extending-the-admin-ui/using-other-frameworks/_index.md

@@ -0,0 +1,210 @@
+---
+title: 'Using Other Frameworks'
+weight: 1
+---
+
+# UI Extensions With Other Frameworks
+
+Although the Admin UI is an Angular app, it is possible to create UI extensions using any web technology - React, Vue, plain JavaScript, etc.
+
+There is still a small amount of Angular "glue code" needed to let the compiler know how to integrate your extension, so let's take a look at how this is done.
+
+## 1. Install `@vendure/ui-devkit`
+
+To create UI extensions, you'll need to install the `@vendure/ui-devkit` package. This package contains a compiler for building your customized version of the Admin UI, as well as the Angular dependencies you'll need to create your extensions.
+
+```bash
+yarn add @vendure/ui-devkit
+
+# or
+
+npm install @vendure/ui-devkit
+```
+
+## 2. Create the folder structure
+
+In this example, we will work with the following folder structure, and use Create React App our example.
+
+```text
+/src
+├─ui-extension/
+     ├─ modules/
+     |      ├─ react-extension.module.ts
+     ├─ react-app/
+            ├─ (create react app directory)
+```
+
+## 3. Create an extension module
+
+Here's the Angular code needed to tell the compiler where to find your extension:
+
+```TypeScript
+// ui-extension/modules/react-extension.module.ts
+
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+import { hostExternalFrame } from '@vendure/admin-ui/core';
+
+@NgModule({
+  imports: [
+    RouterModule.forChild([
+      hostExternalFrame({
+        path: '',
+        breadcrumbLabel: 'React App',
+        // This is the URL to the compiled React app index.
+        // The next step will explain the "assets/react-app" path.
+        extensionUrl: './assets/react-app/index.html',
+        openInNewTab: false,
+      }),
+    ]),
+  ],
+})
+export class ReactUiExtensionModule {}
+```
+
+## 4. Define the AdminUiExtension config
+
+Next we will define an [AdminUiExtension]({{< relref "admin-ui-extension" >}}) object which is passed to the `compileUiExtensions()` function in your Vendure config:
+
+```TypeScript
+// vendure-config.ts
+import path from 'path';
+import { VendureConfig } from '@vendure/core';
+import { AdminUiPlugin } from '@vendure/admin-ui-plugin';
+import { compileUiExtensions } from '@vendure/u-devkit/compiler';
+import { reactUiExtension } from './ui-extension/ui-extension';
+
+export const config: VendureConfig = {
+  // ...
+  plugins: [
+    AdminUiPlugin.init({
+      port: 3002,
+      app: compileUiExtensions({
+        outputPath: path.join(__dirname, '../__admin-ui'),
+        extensions: [{
+          // Points to the path containing our Angular "glue code" module
+          extensionPath: path.join(__dirname, 'ui-extension/modules'),
+          ngModules: [
+            {
+              // We want to lazy-load our extension...
+              type: 'lazy',
+              // ...when the `/admin/extensions/react-ui` 
+              // route is activated 
+              route: 'react-ui',
+              // The filename of the extension module 
+              // relative to the `extensionPath` above
+              ngModuleFileName: 'react-extension.module.ts',
+              // The name of the extension module class exported
+              // from the module file.
+              ngModuleName: 'ReactUiExtensionModule',
+            },
+          ],
+          staticAssets: [
+            // This is where we tell the compiler to copy the compiled React app
+            // artifacts over to the Admin UI's `/static` directory. In this case we
+            // also rename "build" to "react-app". This is why the `extensionUrl`
+            // in the module config points to './assets/react-app/index.html'.
+            { path: path.join(__dirname, 'react-app/build'), rename: 'react-app' },
+          ],
+        }],
+        devMode: true,
+      }),
+    }),
+  ]
+}
+```
+
+## 5. Build your extension
+
+To ensure things are working we can now build our React app by running `yarn build` in the `react-app` directory. This will build and output the app artifacts to the `react-app/build` directory - the one we pointed to in the `staticAssets` array above.
+
+Once build, we can start the Vendure server.
+
+The `compileUiExtensions()` function returns a `compile()` function which will be invoked by the AdminUiPlugin upon server bootstrap. During this compilation process, a new directory will be generated at `/__admin-ui` (as specified by the `outputPath` option) which will contains the uncompiled sources of your new Admin UI app.
+
+Next, these source files will be run through the Angular compiler, the output of which will be visible in the console.
+
+{{% alert "warning" %}}
+**Note:** The first time the compiler is run, an additional step ([compatibility compiler](https://angular.io/guide/ivy#ivy-and-libraries)) is run to make sure all dependencies work with the latest version of Angular. This step can take up to a few minutes.
+{{% /alert %}}
+
+Now go to the Admin UI app in your browser and log in. You should now be able to manually enter the URL `http://localhost:3000/admin/extensions/react-ui` and you should see the default Create React App demo page:
+
+{{< figure src="./ui-extensions-cra.jpg" >}}
+
+## Integrate with the Admin UI
+
+### Styling
+The `@vendure/admin-ui` package (which will be installed alongside the ui-devkit) provides a stylesheet to allow your extension to fit visually with the rest of the Admin UI.
+
+If you have a build step (as in our Create React App example), you can import it into your app like this:
+
+```TypeScript
+// src/ui-extension/react-app/src/App.tsx
+import '@vendure/admin-ui/static/theme.min.css';
+```
+
+If your extension does not have a build step, you can still include the theme stylesheet as a local resource:
+
+```HTML
+<!-- src/ui-extension/plain-js-app/index.html -->
+<head>
+  <link rel="stylesheet" href="../../theme.min.css" />
+</head>
+```
+
+### UiDevkitClient
+
+The `@vendure/ui-devkit` package provides a number of helper methods which allow your extension to seamlessly interact with the underlying Admin UI infrastructure, collectively known as the [UiDevkitClient]({{< relref "ui-devkit-client" >}}). The client allow your extension to:
+
+* Make GraphQL queries & mutations, without the need for your own HTTP or GraphQL client, with full integration with the Admin UI client-side GraphQL cache.
+* Display toast notifications.
+
+For apps with a build step, you can use these functions like this:
+
+```TypeScript
+import { graphQlMutation, notify } from '@vendure/ui-devkit';
+
+// somewhere in your component
+const disableProduct = (id: string) => {
+  graphQlMutation(`
+    mutation DisableProduct($id: ID!) {
+      updateProduct(input: { id: $id, enabled: false }) {
+        id
+        enabled
+      }
+    }`, { id }).then(result => {
+     notify({
+       message: 'Updated Product',
+     });
+  })
+}
+```
+
+If your extension does not have a build step, you can still include the UiDevkitClient as a local resource, which will expose a `VendureUiClient` global object:
+
+```HTML 
+<!-- src/ui-extension/plain-js-app/index.html -->
+<head>
+  <script src="../devkit/ui-devkit.js"></script>
+</head>
+<script>
+  const disableProduct = id => {
+    VendureUiClient.graphQlMutation(`
+      mutation DisableProduct($id: ID!) {
+        updateProduct(input: { id: $id, enabled: false }) {
+          id
+          enabled
+        }
+      }`, { id }).then(result => {
+       VendureUiClient.notify({
+         message: 'Updated Product',
+       });
+    })
+  } 
+</script>
+```
+
+## Next Steps
+
+Now you have created your extension, you need a way for your admin to access it. See [Adding Navigation Items]({{< relref "../adding-navigation-items" >}})

BIN
docs/content/docs/plugins/extending-the-admin-ui/using-other-frameworks/ui-extensions-cra.jpg


+ 4 - 0
packages/ui-devkit/src/client/devkit-client-api.ts

@@ -18,6 +18,7 @@ let targetOrigin = 'http://localhost:3000';
  * app. The `targetOrigin` is a security feature intended to provide control over where messages are sent.
  *
  * @docsCategory ui-devkit
+ * @docsPage UiDevkitClient
  */
 export function setTargetOrigin(value: string) {
     targetOrigin = value;
@@ -28,6 +29,7 @@ export function setTargetOrigin(value: string) {
  * Perform a GraphQL query and returns either an Observable or a Promise of the result.
  *
  * @docsCategory ui-devkit
+ * @docsPage UiDevkitClient
  */
 export function graphQlQuery<T, V extends { [key: string]: any }>(
     document: string,
@@ -53,6 +55,7 @@ export function graphQlQuery<T, V extends { [key: string]: any }>(
  * Perform a GraphQL mutation and returns either an Observable or a Promise of the result.
  *
  * @docsCategory ui-devkit
+ * @docsPage UiDevkitClient
  */
 export function graphQlMutation<T, V extends { [key: string]: any }>(
     document: string,
@@ -77,6 +80,7 @@ export function graphQlMutation<T, V extends { [key: string]: any }>(
  * Display a toast notification.
  *
  * @docsCategory ui-devkit
+ * @docsPage UiDevkitClient
  */
 export function notify(options: NotificationMessage['data']) {
     sendMessage('notification', options).toPromise();

+ 4 - 0
packages/ui-devkit/src/compiler/types.ts

@@ -8,6 +8,7 @@
  * detailed instructions.
  *
  * @docsCategory UiDevkit
+ * @docsPage AdminUiExtension
  */
 export interface AdminUiExtension {
     /**
@@ -44,6 +45,7 @@ export interface AdminUiExtension {
  * name, which will cause the compiler to copy and then rename the asset.
  *
  * @docsCategory UiDevkit
+ * @docsPage AdminUiExtension
  */
 export type StaticAssetDefinition = string | { path: string; rename: string };
 
@@ -52,6 +54,7 @@ export type StaticAssetDefinition = string | { path: string; rename: string };
  * Configuration defining a single NgModule with which to extend the Admin UI.
  *
  * @docsCategory UiDevkit
+ * @docsPage AdminUiExtension
  */
 export interface AdminUiExtensionSharedModule {
     /**
@@ -78,6 +81,7 @@ export interface AdminUiExtensionSharedModule {
  * Configuration defining a single NgModule with which to extend the Admin UI.
  *
  * @docsCategory UiDevkit
+ * @docsPage AdminUiExtension
  */
 export interface AdminUiExtensionLazyModule {
     /**