diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index e01844e233..2216dc9d01 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -45,8 +45,8 @@ import { NewRelicPage } from '@backstage/plugin-newrelic'; import { ScaffolderPage, scaffolderPlugin, - OwnerPicker, - RepoUrlPicker, + OwnerPickerFieldExtension, + RepoUrlPickerFieldExtension, } from '@backstage/plugin-scaffolder'; import { SearchPage, SearchPageNext } from '@backstage/plugin-search'; import { TechRadarPage } from '@backstage/plugin-tech-radar'; @@ -112,15 +112,10 @@ const routes = ( } /> } /> - - - - - } - /> + }> + + + } /> ; - // Register Custom Fields - registerCustomField(field: CustomField): void; - getCustomFields(): { - components: Map; - validators: Map void>; - }; - streamLogs({ taskId, after, @@ -102,7 +95,6 @@ export class ScaffolderClient implements ScaffolderApi { private readonly discoveryApi: DiscoveryApi; private readonly identityApi: IdentityApi; private readonly scmIntegrationsApi: ScmIntegrationRegistry; - private readonly customFields: Map = new Map(); constructor(options: { discoveryApi: DiscoveryApi; @@ -151,24 +143,6 @@ export class ScaffolderClient implements ScaffolderApi { return schema; } - registerCustomField(field: CustomField) { - this.customFields.set(field.name, field); - } - - getCustomFields() { - return [...this.customFields.entries()].reduce( - (previous, [name, field]) => { - previous.components.set(name, field.component); - previous.validators.set(name, field.validation); - return previous; - }, - { - components: new Map(), - validators: new Map(), - }, - ); - } - /** * Executes the scaffolding of a component, given a template and its * parameter values. diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index fab5eb50c8..8abdc7c09f 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -15,21 +15,38 @@ */ import React from 'react'; -import { Routes, Route } from 'react-router'; +import { Routes, Route, useOutlet } from 'react-router'; import { ScaffolderPage } from './ScaffolderPage'; import { TemplatePage } from './TemplatePage'; import { TaskPage } from './TaskPage'; import { ActionsPage } from './ActionsPage'; +import { getComponentData } from '@backstage/core'; +import { FieldExtensionOptions } from '../extensions'; export const Router = () => { + const children = useOutlet(); + const fieldExtensions = React.Children.map(children ?? [], child => { + if (!React.isValidElement(child)) { + return null; + } + + const data = getComponentData>( + child, + 'scaffolder.extensions.field.v1', + ); + + return data; + }).filter(Boolean); + return ( - <> - - } /> - } /> - } /> - } /> - - + + } /> + } + /> + } /> + } /> + ); }; diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 0aad67a1c0..00d4fbae16 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -34,6 +34,7 @@ import { scaffolderApiRef } from '../../api'; import { rootRouteRef } from '../../routes'; import { MultistepJsonForm } from '../MultistepJsonForm'; import { JsonObject, JsonValue } from '@backstage/config'; +import { FieldExtensionOptions } from '../../extensions'; const useTemplateParameterSchema = (templateName: string) => { const scaffolderApi = useApi(scaffolderApiRef); @@ -132,7 +133,11 @@ const storePathValidator = ( return errors; }; -export const TemplatePage = () => { +export const TemplatePage = ({ + customFieldExtensions = [], +}: { + customFieldExtensions: FieldExtensionOptions[]; +}) => { const errorApi = useApi(errorApiRef); const scaffolderApi = useApi(scaffolderApiRef); const { templateName } = useParams(); @@ -141,7 +146,6 @@ export const TemplatePage = () => { const { schema, loading, error } = useTemplateParameterSchema(templateName); const [formState, setFormState] = useState({}); const handleFormReset = () => setFormState({}); - const handleChange = useCallback( (e: IChangeEvent) => setFormState(e.formData), [setFormState], @@ -166,7 +170,14 @@ export const TemplatePage = () => { return ; } - const { components, validators } = scaffolderApi.getCustomFields(); + const customFieldComponents = Object.fromEntries( + customFieldExtensions.map(({ name, component }) => [name, component]), + ); + + const customFieldValidators = Object.fromEntries( + customFieldExtensions.map(({ name, validation }) => [name, validation]), + ); + return (
{ > { return { ...step, - validate: createValidator(step.schema, validators), + validate: createValidator(step.schema, customFieldValidators), }; })} /> diff --git a/plugins/scaffolder/src/extensions/index.tsx b/plugins/scaffolder/src/extensions/index.tsx index acc3de38a0..307bdd8aeb 100644 --- a/plugins/scaffolder/src/extensions/index.tsx +++ b/plugins/scaffolder/src/extensions/index.tsx @@ -24,7 +24,7 @@ export type FieldExtensionOptions = { export function createScaffolderFieldExtension( options: FieldExtensionOptions, -): Extension { +): Extension<() => null> { return { expose() { const FieldExtensionDataHolder: any = () => null; diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index 66eff47e5d..bda87eee5f 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -18,8 +18,8 @@ export { scaffolderPlugin, scaffolderPlugin as plugin, ScaffolderPage, - OwnerPicker, - RepoUrlPicker, + OwnerPickerFieldExtension, + RepoUrlPickerFieldExtension, } from './plugin'; export type { ScaffolderApi } from './api'; export { ScaffolderClient, scaffolderApiRef } from './api';