diff --git a/.changeset/tender-tigers-joke.md b/.changeset/tender-tigers-joke.md new file mode 100644 index 0000000000..2c7d28c552 --- /dev/null +++ b/.changeset/tender-tigers-joke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': minor +--- + +Ability to modify the layout of the step form diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index dcc233a195..65a43b715b 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -64,6 +64,7 @@ import { ScaffolderPage, NextScaffolderPage, scaffolderPlugin, + ScaffolderLayouts, } from '@backstage/plugin-scaffolder'; import { SearchPage } from '@backstage/plugin-search'; import { TechRadarPage } from '@backstage/plugin-tech-radar'; @@ -104,6 +105,7 @@ import { ApacheAirflowPage } from '@backstage/plugin-apache-airflow'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common'; import { PlaylistIndexPage } from '@backstage/plugin-playlist'; +import { TwoColumnLayout } from './components/scaffolder/customScaffolderLayouts'; const app = createApp({ apis, @@ -218,6 +220,9 @@ const routes = ( + + + { + const mid = Math.ceil(properties.length / 2); + const left = properties.slice(0, mid); + const right = properties.slice(mid); + return ( + <> +

{title}

+

In two column layout!!

+ + {left.map(prop => ( + + {prop.content} + + ))} + {right.map(prop => ( + + {prop.content} + + ))} + + {description} + + ); +}; + +export const TwoColumnLayout = scaffolderPlugin.provide( + createScaffolderLayout({ + name: 'TwoColumn', + component: TwoColumn, + }), +); diff --git a/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts b/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts index 2e23b08e53..56f34779de 100644 --- a/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts +++ b/packages/app/src/components/scaffolder/defaultPreviewTemplate.ts @@ -17,6 +17,7 @@ export const defaultPreviewTemplate = `# Edit the template parameters below to see how they will render in the scaffolder form UI parameters: - title: Fill in some steps + ui:ObjectFieldTemplate: TwoColumn required: - name properties: diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 9541098934..539dd4f82c 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -16,6 +16,7 @@ import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FetchApi } from '@backstage/core-plugin-api'; import { FieldProps } from '@rjsf/core'; import { FieldValidation } from '@rjsf/core'; +import type { FormProps } from '@rjsf/core'; import { IdentityApi } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; @@ -37,6 +38,11 @@ export function createScaffolderFieldExtension< options: FieldExtensionOptions, ): Extension>; +// @public +export function createScaffolderLayout( + options: LayoutOptions, +): Extension>; + // @public export type CustomFieldValidator = ( data: TFieldReturnValue, @@ -110,6 +116,20 @@ export type FieldExtensionOptions< validation?: CustomFieldValidator; }; +// @public +export type LayoutComponent<_TInputProps> = () => null; + +// @public +export interface LayoutOptions

{ + // (undocumented) + component: LayoutTemplate

; + // (undocumented) + name: string; +} + +// @public +export type LayoutTemplate = FormProps['ObjectFieldTemplate']; + // @public export type ListActionsResponse = Array<{ id: string; @@ -349,6 +369,9 @@ export interface ScaffolderGetIntegrationsListResponse { }[]; } +// @public +export const ScaffolderLayouts: React.ComponentType; + // @public (undocumented) export type ScaffolderOutputLink = { title?: string; diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index 43e12ae85f..8c71df67b6 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -36,6 +36,7 @@ import { transformSchemaToProps } from './schema'; import { Content, StructuredMetadataTable } from '@backstage/core-components'; import cloneDeep from 'lodash/cloneDeep'; import * as fieldOverrides from './FieldOverrides'; +import { LayoutOptions } from '../../layouts'; const Form = withTheme(MuiTheme); type Step = { @@ -55,6 +56,7 @@ type Props = { widgets?: FormProps['widgets']; fields?: FormProps['fields']; finishButtonLabel?: string; + layouts: LayoutOptions[]; }; export function getUiSchemasFromSteps(steps: Step[]): UiSchema[] { @@ -119,6 +121,7 @@ export const MultistepJsonForm = (props: Props) => { fields, widgets, finishButtonLabel, + layouts, } = props; const [activeStep, setActiveStep] = useState(0); const [disableButtons, setDisableButtons] = useState(false); @@ -214,7 +217,7 @@ export const MultistepJsonForm = (props: Props) => { if (e.errors.length === 0) handleNext(); }} {...formProps} - {...transformSchemaToProps(schema)} + {...transformSchemaToProps(schema, layouts)} >