From cd63a3d39fdee990db895dc1595e7c2ce93a4f1b Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Mon, 19 Dec 2022 10:01:55 +0000 Subject: [PATCH 01/16] add layouts to NextScaffolderPage Signed-off-by: Paul Cowan --- packages/app/src/App.tsx | 3 +++ plugins/scaffolder/src/next/Router/Router.tsx | 18 ++++++++++++++++-- .../TemplateWizardPage/TemplateWizardPage.tsx | 2 +- plugins/scaffolder/src/next/types.ts | 5 ++++- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index c5410c4f80..8984a69259 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -244,6 +244,9 @@ const routes = ( + + + } /> ) => { ), ] as NextFieldExtensionOptions[]; + const customLayouts = useElementFilter(outlet, elements => + elements + .selectByComponentData({ + key: LAYOUTS_WRAPPER_KEY, + }) + .findComponentData({ + key: LAYOUTS_KEY, + }), + ); + return ( ) => { } diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx index 24c529b094..99f2577459 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx @@ -34,7 +34,7 @@ import { scaffolderTaskRouteRef, selectedTemplateRouteRef } from '../../routes'; import { Header, Page } from '@backstage/core-components'; import { Workflow } from '@backstage/plugin-scaffolder-react'; -type TemplateWizardPageProps = { +export type TemplateWizardPageProps = { customFieldExtensions: NextFieldExtensionOptions[]; FormProps?: FormProps; }; diff --git a/plugins/scaffolder/src/next/types.ts b/plugins/scaffolder/src/next/types.ts index e9c77aa00e..2700137724 100644 --- a/plugins/scaffolder/src/next/types.ts +++ b/plugins/scaffolder/src/next/types.ts @@ -21,6 +21,7 @@ */ import type { FormProps as SchemaFormProps } from '@rjsf/core-v5'; +import { LayoutOptions } from '../layouts/types'; /** * Any `@rjsf/core` form properties that are publicly exposed to the `NextScaffolderpage` @@ -31,4 +32,6 @@ import type { FormProps as SchemaFormProps } from '@rjsf/core-v5'; export type FormProps = Pick< SchemaFormProps, 'transformErrors' | 'noHtml5Validate' ->; +> & { + layouts?: LayoutOptions[]; +}; From d2ddde21081da2b0396ad0ab9d093c860392e7c8 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Fri, 23 Dec 2022 16:36:52 +0000 Subject: [PATCH 02/16] add ScaffolderLayouts to NextScaffolderPage Signed-off-by: Paul Cowan --- .changeset/silly-turkeys-hang.md | 5 +++ .../next/components/Stepper/Stepper.test.tsx | 43 +++++++++++++++++++ .../src/next/components/Stepper/Stepper.tsx | 14 +++--- .../src/next/hooks/useTemplateSchema.test.tsx | 38 ++++++++++++++++ plugins/scaffolder-react/src/next/types.ts | 19 ++++++++ .../src/next/Router/Router.test.tsx | 1 + plugins/scaffolder/src/next/Router/Router.tsx | 8 ++-- plugins/scaffolder/src/next/types.ts | 22 +++++++++- 8 files changed, 138 insertions(+), 12 deletions(-) create mode 100644 .changeset/silly-turkeys-hang.md diff --git a/.changeset/silly-turkeys-hang.md b/.changeset/silly-turkeys-hang.md new file mode 100644 index 0000000000..e2345fb35d --- /dev/null +++ b/.changeset/silly-turkeys-hang.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Add `ScaffolderLayouts` to `NextScaffolderPage` diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx index 5dbce3243f..57ff949c62 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx @@ -392,4 +392,47 @@ describe('Stepper', () => { await fireEvent.click(getByRole('button', { name: 'Make' })); }); }); + + describe('Scaffolder Layouts', () => { + it('should render the step in the scaffolder layout', async () => { + const ScaffolderLayout: LayoutTemplate = ({ properties }) => ( + <> +

A Scaffolder Layout

+ {properties.map((prop, i) => ( +
{prop.content}
+ ))} + + ); + + const manifest: TemplateParameterSchema = { + steps: [ + { + title: 'Step 1', + schema: { + type: 'object', + 'ui:ObjectFieldTemplate': 'Layout', + properties: { + field1: { + type: 'string', + }, + }, + }, + }, + ], + title: 'scaffolder layouts', + }; + + const { getByText, getByRole } = await renderInTestApp( + , + ); + + expect(getByText('A Scaffolder Layout')).toBeInTheDocument(); + expect(getByRole('textbox', { name: 'field1' })).toBeInTheDocument(); + }); + }); }); diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 950c9720de..c2c4f06ffd 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -28,11 +28,14 @@ import React, { useCallback, useMemo, useState, type ReactNode } from 'react'; import { NextFieldExtensionOptions } from '../../extensions'; import { TemplateParameterSchema } from '../../../types'; import { createAsyncValidators } from './createAsyncValidators'; -import type { FormProps } from '../../types'; +import type { FormProps, LayoutOptions } from '../../types'; import { ReviewState, type ReviewStateProps } from '../ReviewState'; import { useTemplateSchema } from '../../hooks/useTemplateSchema'; import { useFormDataFromQuery } from '../../hooks/useFormDataFromQuery'; import validator from '@rjsf/validator-ajv6'; +import { useFormDataFromQuery } from '../../hooks'; +import type { FormProps } from '../../types'; +import { selectedTemplateRouteRef } from '../../../routes'; const useStyles = makeStyles(theme => ({ backButton: { @@ -65,6 +68,7 @@ export type StepperProps = { createButtonText?: ReactNode; reviewButtonText?: ReactNode; }; + layouts?: LayoutOptions[]; }; // TODO(blam): We require here, as the types in this package depend on @rjsf/core explicitly @@ -76,17 +80,15 @@ const Form = withTheme(require('@rjsf/material-ui-v5').Theme); * The `Stepper` component is the Wizard that is rendered when a user selects a template * @alpha */ - export const Stepper = (stepperProps: StepperProps) => { - const { components = {}, ...props } = stepperProps; + const { layouts = [], components = {}, ...props } = stepperProps; const { ReviewStateComponent = ReviewState, createButtonText = 'Create', reviewButtonText = 'Review', } = components; - const analytics = useAnalytics(); - const { steps } = useTemplateSchema(props.manifest); + const { steps } = useTemplateSchema(props.manifest, layouts); const apiHolder = useApiHolder(); const [activeStep, setActiveStep] = useState(0); const [formState, setFormState] = useFormDataFromQuery(props.initialState); @@ -177,7 +179,7 @@ export const Stepper = (stepperProps: StepperProps) => { fields={extensions} showErrorList={false} onChange={handleChange} - {...(props.FormProps ?? {})} + {...(formProps ?? {})} >