From 16cd6d442a52822431d04c6734acad75b25d4010 Mon Sep 17 00:00:00 2001 From: bogdannechyporenko Date: Wed, 26 Oct 2022 22:00:32 +0200 Subject: [PATCH] Incorporated a feedback Signed-off-by: bnechyporenko Signed-off-by: bogdannechyporenko --- plugins/scaffolder/api-report.md | 33 ++++++++-------- .../MultistepJsonForm/MultistepJsonForm.tsx | 30 +++++++++++---- .../MultistepJsonForm/ReviewStep.tsx | 38 +++++++------------ plugins/scaffolder/src/components/Router.tsx | 4 +- .../components/TemplatePage/TemplatePage.tsx | 4 +- plugins/scaffolder/src/components/index.ts | 2 +- plugins/scaffolder/src/components/types.ts | 16 ++++---- 7 files changed, 68 insertions(+), 59 deletions(-) diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 8ea360f050..4c447d4ab1 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -34,7 +34,8 @@ import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { UIOptionsType } from '@rjsf/utils'; -import { UiSchema } from '@rjsf/utils'; +import { UiSchema } from '@rjsf/core'; +import { UiSchema as UiSchema_2 } from '@rjsf/utils'; // @alpha export function createNextScaffolderFieldExtension< @@ -132,18 +133,6 @@ export type FieldExtensionOptions< validation?: CustomFieldValidator; }; -// @public -export type LastStepFormProps = { - disableButtons: boolean; - finishButtonLabel?: string; - formData: Record; - handleBack: () => void; - handleCreate: () => void; - handleReset: () => void; - onFinish?: () => Promise; - steps: Step[]; -}; - // @public export type LayoutComponent<_TInputProps> = () => null; @@ -196,7 +185,7 @@ export interface NextFieldExtensionComponentProps< TUiOptions = {}, > extends PropsWithChildren> { // (undocumented) - uiSchema?: UiSchema & { + uiSchema?: UiSchema_2 & { 'ui:options'?: TUiOptions & UIOptionsType; }; } @@ -309,13 +298,27 @@ export interface RepoUrlPickerUiOptions { }; } +// @public +export type ReviewStepProps = { + disableButtons: boolean; + formData: JsonObject; + handleBack: () => void; + handleReset: () => void; + handleCreate: () => void; + steps: { + uiSchema: UiSchema; + mergedSchema: JsonObject; + schema: JsonObject; + }[]; +}; + // @public (undocumented) export const rootRouteRef: RouteRef; // @public export type RouterProps = { components?: { - LastStepFormComponent?: ComponentType; + ReviewStepComponent?: ComponentType; TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta3; diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx index ce0e3616fd..4b14f42d65 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/MultistepJsonForm.tsx @@ -21,6 +21,7 @@ import { Stepper, Typography, } from '@material-ui/core'; +import { JsonObject } from '@backstage/types'; import { errorApiRef, featureFlagsApiRef, @@ -28,16 +29,17 @@ import { useRouteRefParams, useApi, } from '@backstage/core-plugin-api'; -import { FormProps, IChangeEvent, withTheme } from '@rjsf/core'; +import { FormProps, IChangeEvent, UiSchema, withTheme } from '@rjsf/core'; import { Theme as MuiTheme } from '@rjsf/material-ui'; import React, { ComponentType, useState } from 'react'; import { transformSchemaToProps } from './schema'; import cloneDeep from 'lodash/cloneDeep'; import * as fieldOverrides from './FieldOverrides'; import { LayoutOptions } from '../../layouts'; -import { ReviewStepComponentProps, Step } from '../types'; +import { ReviewStepProps, Step } from '../types'; import { ReviewStep } from './ReviewStep'; import { selectedTemplateRouteRef } from '../../routes'; +import { extractSchemaFromStep } from '../../next/TemplateWizardPage/Stepper/schema'; const Form = withTheme(MuiTheme); @@ -57,9 +59,26 @@ export type MultistepJsonFormProps = { fields?: FormProps['fields']; finishButtonLabel?: string; layouts: LayoutOptions[]; - ReviewStepComponent?: ComponentType; + ReviewStepComponent?: ComponentType; }; +function getUiSchemasFromSteps(steps: Step[]): { + uiSchema: UiSchema; + mergedSchema: JsonObject; + schema: JsonObject; +}[] { + const res = steps.map(({ schema }) => ({ + mergedSchema: schema, + ...extractSchemaFromStep(schema), + })); + + return res as unknown as { + uiSchema: UiSchema; + mergedSchema: JsonObject; + schema: JsonObject; + }[]; +} + /** * Creates the dynamic form for a scaffolder template. */ @@ -71,7 +90,6 @@ export const MultistepJsonForm = (props: MultistepJsonFormProps) => { onFinish, fields, widgets, - finishButtonLabel, layouts, ReviewStepComponent, } = props; @@ -196,10 +214,8 @@ export const MultistepJsonForm = (props: MultistepJsonFormProps) => { handleBack={handleBack} handleCreate={handleCreate} handleReset={handleReset} - finishButtonLabel={finishButtonLabel} formData={formData} - onFinish={onFinish} - steps={steps} + steps={getUiSchemasFromSteps(steps)} /> )} diff --git a/plugins/scaffolder/src/components/MultistepJsonForm/ReviewStep.tsx b/plugins/scaffolder/src/components/MultistepJsonForm/ReviewStep.tsx index 52e94aa1f7..1d60a49102 100644 --- a/plugins/scaffolder/src/components/MultistepJsonForm/ReviewStep.tsx +++ b/plugins/scaffolder/src/components/MultistepJsonForm/ReviewStep.tsx @@ -18,29 +18,16 @@ import React from 'react'; import { Content, StructuredMetadataTable } from '@backstage/core-components'; import { UiSchema } from '@rjsf/core'; import { JsonObject } from '@backstage/types'; -import { ReviewStepComponentProps, Step } from '../types'; +import { ReviewStepProps } from '../types'; -export function getUiSchemasFromSteps(steps: Step[]): UiSchema[] { - const uiSchemas: Array = []; - steps.forEach(step => { - const schemaProps = step.schema.properties as JsonObject; - for (const key in schemaProps) { - if (schemaProps.hasOwnProperty(key)) { - const uiSchema = schemaProps[key] as UiSchema; - uiSchema.name = key; - uiSchemas.push(uiSchema); - } - } - }); - return uiSchemas; -} - -export function getReviewData(formData: Record, steps: Step[]) { - const uiSchemas = getUiSchemasFromSteps(steps); +export function getReviewData( + formData: Record, + steps: UiSchema[], +) { const reviewData: Record = {}; for (const key in formData) { if (formData.hasOwnProperty(key)) { - const uiSchema = uiSchemas.find(us => us.name === key); + const uiSchema = steps.find(us => us.name === key); if (!uiSchema) { reviewData[key] = formData[key]; @@ -77,15 +64,13 @@ export function getReviewData(formData: Record, steps: Step[]) { * The component displaying the Last Step in scaffolder template form. * Which represents the summary of the input provided by the end user. */ -export const ReviewStep = (props: ReviewStepComponentProps) => { +export const ReviewStep = (props: ReviewStepProps) => { const { disableButtons, - finishButtonLabel, formData, handleBack, handleCreate, handleReset, - onFinish, steps, } = props; return ( @@ -94,7 +79,10 @@ export const ReviewStep = (props: ReviewStepComponentProps) => { Review and create ({ uiSchema: step.uiSchema })), + )} /> diff --git a/plugins/scaffolder/src/components/Router.tsx b/plugins/scaffolder/src/components/Router.tsx index 4f394ebccf..da7ceebe5b 100644 --- a/plugins/scaffolder/src/components/Router.tsx +++ b/plugins/scaffolder/src/components/Router.tsx @@ -46,7 +46,7 @@ import { } from '../routes'; import { ListTasksPage } from './ListTasksPage'; import { LayoutOptions, LAYOUTS_KEY, LAYOUTS_WRAPPER_KEY } from '../layouts'; -import { ReviewStepComponentProps } from './types'; +import { ReviewStepProps } from './types'; /** * The props for the entrypoint `ScaffolderPage` component the plugin. @@ -54,7 +54,7 @@ import { ReviewStepComponentProps } from './types'; */ export type RouterProps = { components?: { - ReviewStepComponent?: ComponentType; + ReviewStepComponent?: ComponentType; TemplateCardComponent?: | ComponentType<{ template: TemplateEntityV1beta3 }> | undefined; diff --git a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx index 64d62943b1..01dc3b832d 100644 --- a/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx +++ b/plugins/scaffolder/src/components/TemplatePage/TemplatePage.tsx @@ -41,7 +41,7 @@ import { } from '@backstage/core-plugin-api'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { LayoutOptions } from '../../layouts'; -import { ReviewStepComponentProps } from '../types'; +import { ReviewStepProps } from '../types'; const useTemplateParameterSchema = (templateRef: string) => { const scaffolderApi = useApi(scaffolderApiRef); @@ -57,7 +57,7 @@ export const TemplatePage = ({ customFieldExtensions = [], layouts = [], }: { - ReviewStepComponent?: ComponentType; + ReviewStepComponent?: ComponentType; customFieldExtensions?: FieldExtensionOptions[]; layouts?: LayoutOptions[]; }) => { diff --git a/plugins/scaffolder/src/components/index.ts b/plugins/scaffolder/src/components/index.ts index 7c84ca7be2..b10e1f9e76 100644 --- a/plugins/scaffolder/src/components/index.ts +++ b/plugins/scaffolder/src/components/index.ts @@ -19,4 +19,4 @@ export { TemplateTypePicker } from './TemplateTypePicker'; export * from './secrets'; export { TaskPage } from './TaskPage'; export type { RouterProps } from './Router'; -export type { ReviewStepComponentProps, Step } from './types'; +export type { ReviewStepProps, Step } from './types'; diff --git a/plugins/scaffolder/src/components/types.ts b/plugins/scaffolder/src/components/types.ts index 97cb8d2d88..45528b46f3 100644 --- a/plugins/scaffolder/src/components/types.ts +++ b/plugins/scaffolder/src/components/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { FormProps } from '@rjsf/core'; +import { FormProps, UiSchema } from '@rjsf/core'; import { JsonObject } from '@backstage/types'; /** @@ -33,13 +33,15 @@ export type Step = { * * @public */ -export type ReviewStepComponentProps = { +export type ReviewStepProps = { disableButtons: boolean; - finishButtonLabel?: string; - formData: Record; + formData: JsonObject; handleBack: () => void; - handleCreate: () => void; handleReset: () => void; - onFinish?: () => Promise; - steps: Step[]; + handleCreate: () => void; + steps: { + uiSchema: UiSchema; + mergedSchema: JsonObject; + schema: JsonObject; + }[]; };