frontend-plugin-api: swtich extension factory to bind all outputs at once

Co-authored-by: Johan Haals <johan.haals@gmail.com>
Co-authored-by: Fredrik Adelöw <freben@gmail.com>
Co-authored-by: Philipp Hugenroth <philipph@spotify.com>
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-09-04 16:31:37 +02:00
parent e36fae2c2a
commit d1a1cad0e7
7 changed files with 81 additions and 50 deletions
@@ -63,9 +63,9 @@ export function createApiExtension<
},
factory({ bind, config, inputs }) {
if (typeof factory === 'function') {
bind.api(factory({ config, inputs }));
bind({ api: factory({ config, inputs }) });
} else {
bind.api(factory);
bind({ api: factory });
}
},
});
@@ -82,17 +82,18 @@ export function createPageExtension<
.component({ config, inputs })
.then(element => ({ default: () => element })),
);
bind.path(config.path);
bind.component(() => (
<ExtensionBoundary source={source}>
<React.Suspense fallback="...">
<LazyComponent />
</React.Suspense>
</ExtensionBoundary>
));
if (options.routeRef) {
bind.routeRef!(options.routeRef);
}
bind({
path: config.path,
component: () => (
<ExtensionBoundary source={source}>
<React.Suspense fallback="...">
<LazyComponent />
</React.Suspense>
</ExtensionBoundary>
),
routeRef: options.routeRef,
});
},
});
}
@@ -25,21 +25,26 @@ describe('createExtension', () => {
id: 'test',
at: 'root',
output: {
foo: coreExtensionData.title,
foo2: stringData,
foo: stringData,
},
factory({ bind }) {
// Make it work well with required and optional output
// HOH - High Order Helper
bind({
foo: 'bar',
});
bind({
// @ts-expect-error
foo: 3,
});
bind({
// @ts-expect-error
bar: 'bar',
});
// @ts-expect-error
bind.foo(3);
bind({});
// @ts-expect-error
bind.foo();
bind();
// @ts-expect-error
bind.bar('bar');
bind('bar');
},
});
expect(extension.id).toBe('test');
@@ -19,9 +19,9 @@ import { BackstagePlugin } from './createPlugin';
import { AnyExtensionDataMap, Extension } from './types';
/** @public */
export type ExtensionDataBind<TData extends AnyExtensionDataMap> = {
[K in keyof TData]: (value: TData[K]['T']) => void;
};
export type ExtensionDataBind<TOutput extends AnyExtensionDataMap> = (outputs: {
[K in keyof TOutput]: TOutput[K]['T'];
}) => void;
/** @public */
export type ExtensionDataValue<TData extends AnyExtensionDataMap> = {
@@ -30,23 +30,23 @@ export type ExtensionDataValue<TData extends AnyExtensionDataMap> = {
/** @public */
export interface CreateExtensionOptions<
TData extends AnyExtensionDataMap,
TPoint extends Record<string, { extensionData: AnyExtensionDataMap }>,
TOutput extends AnyExtensionDataMap,
TInputs extends Record<string, { extensionData: AnyExtensionDataMap }>,
TConfig,
> {
id: string;
at: string;
disabled?: boolean;
inputs?: TPoint;
output: TData;
inputs?: TInputs;
output: TOutput;
configSchema?: PortableSchema<TConfig>;
factory(options: {
source?: BackstagePlugin;
bind: ExtensionDataBind<TData>;
bind: ExtensionDataBind<TOutput>;
config: TConfig;
inputs: {
[pointName in keyof TPoint]: ExtensionDataValue<
TPoint[pointName]['extensionData']
[pointName in keyof TInputs]: ExtensionDataValue<
TInputs[pointName]['extensionData']
>[];
};
}): void;
@@ -54,14 +54,28 @@ export interface CreateExtensionOptions<
/** @public */
export function createExtension<
TData extends AnyExtensionDataMap,
TPoint extends Record<string, { extensionData: AnyExtensionDataMap }>,
TOutput extends AnyExtensionDataMap,
TInputs extends Record<string, { extensionData: AnyExtensionDataMap }>,
TConfig = never,
>(options: CreateExtensionOptions<TData, TPoint, TConfig>): Extension<TConfig> {
>(
options: CreateExtensionOptions<TOutput, TInputs, TConfig>,
): Extension<TConfig> {
return {
...options,
disabled: options.disabled ?? false,
$$type: 'extension',
inputs: options.inputs ?? {},
factory({ bind, config, inputs }) {
// TODO: Simplify this, but TS wouldn't infer the input type for some reason
return options.factory({
bind,
config,
inputs: inputs as {
[pointName in keyof TInputs]: ExtensionDataValue<
TInputs[pointName]['extensionData']
>[];
},
});
},
};
}
@@ -33,7 +33,7 @@ const TechRadarPage = createExtension({
name: nameExtensionDataRef,
},
factory({ bind }) {
bind.name('TechRadar');
bind({ name: 'TechRadar' });
},
});
@@ -47,7 +47,7 @@ const CatalogPage = createExtension({
z.object({ name: z.string().default('Catalog') }),
),
factory({ bind, config }) {
bind.name(config.name);
bind({ name: config.name });
},
});
@@ -61,7 +61,7 @@ const TechDocsAddon = createExtension({
z.object({ name: z.string().default('TechDocsAddon') }),
),
factory({ bind, config }) {
bind.name(config.name);
bind({ name: config.name });
},
});
@@ -79,7 +79,7 @@ const TechDocsPage = createExtension({
name: nameExtensionDataRef,
},
factory({ bind, inputs }) {
bind.name(`TechDocs-${inputs.addons.map(n => n.name).join('-')}`);
bind({ name: `TechDocs-${inputs.addons.map(n => n.name).join('-')}` });
},
});
@@ -97,11 +97,12 @@ const outputExtension = createExtension({
component: coreExtensionData.reactComponent,
},
factory({ bind, inputs }) {
bind.component(() =>
React.createElement('span', {}, [
`Names: ${inputs.names.map(n => n.name).join(', ')}`,
]),
);
bind({
component: () =>
React.createElement('span', {}, [
`Names: ${inputs.names.map(n => n.name).join(', ')}`,
]),
});
},
});