diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx index bb37e3143c..013147dc90 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx @@ -22,12 +22,13 @@ import { Button, makeStyles, } from '@material-ui/core'; -import { FormValidation, withTheme } from '@rjsf/core'; +import { FieldValidation, withTheme } from '@rjsf/core'; import { Theme as MuiTheme } from '@rjsf/material-ui'; import React, { useMemo, useState } from 'react'; import { FieldExtensionOptions } from '../../../extensions'; import { TemplateParameterSchema } from '../../../types'; import { createAsyncValidator } from './createAsyncValidators'; +import { createFieldValidation } from './schema'; import { useTemplateSchema } from './useTemplateSchema'; const useStyles = makeStyles(theme => ({ @@ -56,7 +57,9 @@ export const Stepper = (props: StepperProps) => { const apiHolder = useApiHolder(); const [activeStep, setActiveStep] = useState(0); const [formState, setFormState] = useState({}); - const [errors, setErrors] = useState(); + const [errors, setErrors] = useState< + undefined | Record + >(); const styles = useStyles(); const extensions = useMemo(() => { @@ -72,7 +75,8 @@ export const Stepper = (props: StepperProps) => { }, [props.extensions]); const validator = useMemo(() => { - return createAsyncValidator(steps[activeStep].originalSchema, validators, { + const { mergedSchema } = steps[activeStep]; + return createAsyncValidator(mergedSchema, validators, { apiHolder, }); }, [steps, activeStep, validators, apiHolder]); @@ -86,25 +90,13 @@ export const Stepper = (props: StepperProps) => { const errorContext: any = {}; for (const [key] of Object.entries(formData)) { - const localFieldContext = { - __errors: [] as string[], - addError: (message: string) => { - localFieldContext.__errors.push(message); - }, - }; - errorContext[key] = localFieldContext; + errorContext[key] = createFieldValidation(); } - const returnedValidation = await validator( - formData, - errorContext as FormValidation, - ); + const returnedValidation = await validator(formData); const hasErrors = Object.values(returnedValidation).some(i => { - if ('__errors' in i) { - return i.__errors.length > 0; - } - return false; + return i.__errors.length > 0; }); if (hasErrors) { diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/createAsyncValidators.ts b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/createAsyncValidators.ts index 26a51bf376..e2f625892a 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/createAsyncValidators.ts +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/createAsyncValidators.ts @@ -14,15 +14,12 @@ * limitations under the License. */ -import { FieldValidation, FormValidation } from '@rjsf/core'; +import { FieldValidation } from '@rjsf/core'; import { JsonObject } from '@backstage/types'; import { ApiHolder } from '@backstage/core-plugin-api'; import { CustomFieldValidator } from '../../../extensions'; import { Draft07 as JSONSchema } from 'json-schema-library'; - -function isObject(obj: unknown): obj is JsonObject { - return typeof obj === 'object' && obj !== null && !Array.isArray(obj); -} +import { createFieldValidation } from './schema'; export const createAsyncValidator = ( rootSchema: JsonObject, @@ -43,12 +40,7 @@ export const createAsyncValidator = ( if (definitionInSchema && 'ui:field' in definitionInSchema) { const validator = validators[definitionInSchema['ui:field']]; if (validator) { - const fieldValidation = { - __errors: [] as string[], - addError: (message: string) => { - fieldValidation.__errors.push(message); - }, - }; + const fieldValidation = createFieldValidation(); await validator(value, fieldValidation, context); formValidation[key] = fieldValidation; } diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts index 5a33a622bb..d353820004 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/schema.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { JsonObject } from '@backstage/types'; -import { UiSchema } from '@rjsf/core'; +import { FieldValidation, UiSchema } from '@rjsf/core'; function isObject(value: unknown): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); @@ -110,3 +110,19 @@ export const extractSchemaFromStep = ( extractUiSchema(returnSchema, uiSchema); return { uiSchema, schema: returnSchema }; }; + +/** + * @alpha + * Creates a field validation object for use in react jsonschema form + * @returns {FieldValidation} A field validation object that can be used to validate a field + */ +export const createFieldValidation = (): FieldValidation => { + const fieldValidation: FieldValidation = { + __errors: [] as string[], + addError: (message: string) => { + fieldValidation.__errors.push(message); + }, + }; + + return fieldValidation; +}; diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts index dac2c7b61c..926fb8d56e 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/useTemplateSchema.ts @@ -24,7 +24,7 @@ export const useTemplateSchema = ( ): { steps: { uiSchema: UiSchema; - originalSchema: JsonObject; + mergedSchema: JsonObject; schema: JsonObject; title: string; description?: string; @@ -34,7 +34,7 @@ export const useTemplateSchema = ( const steps = manifest.steps.map(({ title, description, schema }) => ({ title, description, - originalSchema: schema, + mergedSchema: schema, ...extractSchemaFromStep(schema), }));