docs/frontend-system: initial docs for creating extension blueprints
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -60,3 +60,83 @@ const myPageExtension = PageBlueprint.makeWithOverrides({
|
||||
When using `makeWithOverrides`, we no longer pass the blueprint parameters directly. Instead, we provide a `factory` function that receives the original blueprint factory as the first argument, and the extension factory context as the second. We can then call the original blueprint factory with the blueprint parameters and forward the result as the return value of out factory. Notice that when passing the blueprint parameters using this pattern we have access to a lot more information than when using the `make` method, at the cost of being more complex.
|
||||
|
||||
Apart from the addition of the blueprint parameters of the first argument to the original factory function, the `makeWithOverrides` method works the same way as [extension overrides](./25-extension-overrides.md). All the same options and rules apply, including the ability to define additional inputs, override outputs, and so on. We therefore defer to the [extension overrides](./25-extension-overrides.md) documentation for more information on how to use the `makeWithOverrides` method.
|
||||
|
||||
## Creating an extension blueprint
|
||||
|
||||
To create a new extension blueprint, you use the `createExtensionBlueprint` function. At the surface it is very similar to `createExtension`, but with a few key differences. Firstly you must provide a `kind` option, which will be the kind of all extensions created with the blueprint. See the [naming patterns section](./50-naming-patterns.md) for more information about how to select a good extension kind. Secondly, the `factory` function has a new signature where the first parameter is the blueprint parameters, and the second is the factory context. And finally, rather than returning an extension, `createExtensionBlueprint` returns a blueprint object with the `make` method and friends, which is used as is described above.
|
||||
|
||||
The following is an example of how one might create a new extension blueprint:
|
||||
|
||||
```tsx
|
||||
export interface MyWidgetBlueprintParams {
|
||||
defaultTitle: string;
|
||||
element: JSX.Element;
|
||||
}
|
||||
|
||||
export const MyWidgetBlueprint = createExtensionBlueprint({
|
||||
kind: 'my-widget',
|
||||
attachTo: { id: 'page:my-plugin', input: 'widgets' },
|
||||
config: {
|
||||
schema: {
|
||||
title: z.string().optional(),
|
||||
},
|
||||
},
|
||||
output: [coreExtensionData.reactElement],
|
||||
factory(params: MyWidgetBlueprintParams, { config }) {
|
||||
return [
|
||||
// Note that while this is a valid pattern, you might often want to
|
||||
// return separate pieces of data instead, more on that below.
|
||||
coreExtensionData.reactElement(
|
||||
<MyWidgetContainer title={config.title ?? params.defaultTitle}>
|
||||
{params.element}
|
||||
</MyWidgetContainer>,
|
||||
),
|
||||
];
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
This is of course a quite bare-bones example blueprint, but still a very real example. Blueprints can be very simple, there's already a lot of value in encapsulating the extension kind, attachment point, and output in a blueprint.
|
||||
|
||||
Most of the options provided to `createExtensionBlueprint` can be overridden when using `makeWithOverrides` to create an extension from the blueprint. These overrides work the same way as [extension overrides](./25-extension-overrides.md), and we defer to that documentation for more information on how overrides work.
|
||||
|
||||
### Blueprint-specific extension data references
|
||||
|
||||
In some cases you may want to define and provide [extension data reference](./20-extensions.md#extension-data-references) that are specific to your blueprint. In the above example we might want to forward the `title` as data for example, rather than encapsulating it into the `MyWidgetContainer` component. This gives the parent extension more flexibility in the rendering for our example widget extensions.
|
||||
|
||||
To do that, we create a new extension data reference for our widget title. This references is provided via the `dataRefs` options when we create the blueprint, which makes it available for use via `MyWidgetBlueprint.dataRefs.widgetTitle`.
|
||||
|
||||
```tsx
|
||||
export interface MyWidgetBlueprintParams {
|
||||
defaultTitle: string;
|
||||
element: JSX.Element;
|
||||
}
|
||||
|
||||
const widgetTitleRef = createExtensionDataRef<string>().with({
|
||||
id: 'my-plugin.widget.title',
|
||||
});
|
||||
|
||||
export const MyWidgetBlueprint = createExtensionBlueprint({
|
||||
kind: 'my-widget',
|
||||
attachTo: { id: 'page:my-plugin', input: 'widgets' },
|
||||
config: {
|
||||
schema: {
|
||||
title: z.string().optional(),
|
||||
},
|
||||
},
|
||||
output: [widgetTitleRef, coreExtensionData.reactElement],
|
||||
factory(params: MyWidgetBlueprintParams, { config }) {
|
||||
return [
|
||||
widgetTitleRef(config.title ?? params.defaultTitle),
|
||||
coreExtensionData.reactElement(params.element),
|
||||
];
|
||||
},
|
||||
dataRefs: {
|
||||
widgetTitle: widgetTitleRef,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### Extension Blueprints in libraries
|
||||
|
||||
If you are publishing a plugin, the extension creators should always be exported from frontend library packages (e.g. `*-react`) rather than plugin packages.
|
||||
|
||||
Reference in New Issue
Block a user