From bb450005af9a852de37d5b6ec22c476c7dda7203 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 7 Feb 2020 13:27:43 +0100 Subject: [PATCH] front/core: add plugin api hooks and output for widgets --- .../packages/core/src/api/plugin/Plugin.tsx | 11 +++++ .../packages/core/src/api/plugin/types.ts | 7 ++++ .../src/api/widgetView/WidgetViewBuilder.tsx | 42 +++++++++++++++---- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/frontend/packages/core/src/api/plugin/Plugin.tsx b/frontend/packages/core/src/api/plugin/Plugin.tsx index 772776e288..624a9042e6 100644 --- a/frontend/packages/core/src/api/plugin/Plugin.tsx +++ b/frontend/packages/core/src/api/plugin/Plugin.tsx @@ -1,6 +1,7 @@ import { ComponentType } from 'react'; import { PluginOutput, RoutePath, RouteOptions } from './types'; import { IconComponent } from '../types'; +import { Widget } from '../widgetView/types'; export type PluginConfig = { id: string; @@ -10,6 +11,7 @@ export type PluginConfig = { export type PluginHooks = { router: RouterHooks; entityPage: EntityPageHooks; + widgets: WidgetHooks; }; export type RouterHooks = { @@ -41,6 +43,10 @@ export type EntityPageHooks = { ): void; }; +export type WidgetHooks = { + add(widget: Widget): void; +}; + export const registerSymbol = Symbol('plugin-register'); export const outputSymbol = Symbol('plugin-output'); @@ -98,6 +104,11 @@ export default class Plugin { }); }, }, + widgets: { + add(widget: Widget) { + outputs.push({ type: 'widget', widget }); + }, + }, }); this.storedOutput = outputs; diff --git a/frontend/packages/core/src/api/plugin/types.ts b/frontend/packages/core/src/api/plugin/types.ts index f6efee230e..9b0eb73639 100644 --- a/frontend/packages/core/src/api/plugin/types.ts +++ b/frontend/packages/core/src/api/plugin/types.ts @@ -1,5 +1,6 @@ import { ComponentType } from 'react'; import { IconComponent } from '../types'; +import { Widget } from '../widgetView/types'; export type RouteOptions = { // Whether the route path must match exactly, defaults to true. @@ -22,6 +23,11 @@ export type RedirectRouteOutput = { options?: RouteOptions; }; +export type WidgetOutput = { + type: 'widget'; + widget: Widget; +}; + export type EntityPageViewRouteOutput = { type: 'entity-page-view-route'; path: RoutePath; @@ -39,5 +45,6 @@ export type EntityPageNavItemOutput = { export type PluginOutput = | RouteOutput | RedirectRouteOutput + | WidgetOutput | EntityPageViewRouteOutput | EntityPageNavItemOutput; diff --git a/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx b/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx index 164ceaf485..84e231651f 100644 --- a/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx +++ b/frontend/packages/core/src/api/widgetView/WidgetViewBuilder.tsx @@ -1,12 +1,18 @@ import React, { ComponentType } from 'react'; import { App, AppComponentBuilder } from '../app/types'; import { Widget } from './types'; +import BackstagePlugin from '../plugin/Plugin'; import DefaultWidgetView from '../../components/DefaultWidgetView'; -type WidgetViewRegistration = { - type: 'component'; - widget: Widget; -}; +type WidgetViewRegistration = + | { + type: 'component'; + widget: Widget; + } + | { + type: 'plugin'; + plugin: BackstagePlugin; + }; export default class WidgetViewBuilder extends AppComponentBuilder { private readonly registrations = new Array(); @@ -17,19 +23,41 @@ export default class WidgetViewBuilder extends AppComponentBuilder { return this; } + register(plugin: BackstagePlugin): WidgetViewBuilder { + this.registrations.push({ type: 'plugin', plugin }); + return this; + } + build(_app: App): ComponentType { if (this.output) { return this.output; } - const widgets = this.registrations.map(reg => { + const widgets = new Array(); + + for (const reg of this.registrations) { switch (reg.type) { case 'component': - return reg.widget; + widgets.push(reg.widget); + break; + case 'plugin': + let added = false; + for (const output of reg.plugin.output()) { + if (output.type === 'widget') { + widgets.push(output.widget); + added = true; + } + } + if (!added) { + throw new Error( + `Plugin ${reg.plugin} was registered as widget provider, but did not provide any widgets`, + ); + } + break; default: throw new Error(`Unknown WidgetViewBuilder registration`); } - }); + } this.output = () => ; return this.output;