diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 0ab468db1b..43253a319c 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -33,6 +33,8 @@ import { ReviewState, type ReviewStateProps } from '../ReviewState'; import { useTemplateSchema } from '../../hooks/useTemplateSchema'; import validator from '@rjsf/validator-ajv6'; import { useFormDataFromQuery } from '../../hooks'; +import type { FormProps, NextLayoutOptions } from '../../types'; +import { useTransformSchemaToProps } from './useTransformSchemaToProps'; const useStyles = makeStyles(theme => ({ backButton: { @@ -85,7 +87,7 @@ export const Stepper = (stepperProps: StepperProps) => { reviewButtonText = 'Review', } = components; const analytics = useAnalytics(); - const { steps } = useTemplateSchema(props.manifest, layouts); + const { steps } = useTemplateSchema(props.manifest); const apiHolder = useApiHolder(); const [activeStep, setActiveStep] = useState(0); const [formState, setFormState] = useFormDataFromQuery(props.initialState); @@ -151,6 +153,8 @@ export const Stepper = (stepperProps: StepperProps) => { setFormState(current => ({ ...current, ...formData })); }; + const currentStep = useTransformSchemaToProps(steps[activeStep], layouts); + return ( <> @@ -170,8 +174,8 @@ export const Stepper = (stepperProps: StepperProps) => { extraErrors={errors as unknown as ErrorSchema} formData={formState} formContext={{ formData: formState }} - schema={steps[activeStep].schema} - uiSchema={steps[activeStep].uiSchema} + schema={currentStep.schema} + uiSchema={currentStep.uiSchema} onSubmit={handleNext} fields={extensions} showErrorList={false} diff --git a/plugins/scaffolder-react/src/next/components/Stepper/useTransformSchemaToProps.ts b/plugins/scaffolder-react/src/next/components/Stepper/useTransformSchemaToProps.ts new file mode 100644 index 0000000000..3c71f4c14b --- /dev/null +++ b/plugins/scaffolder-react/src/next/components/Stepper/useTransformSchemaToProps.ts @@ -0,0 +1,46 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { type NextLayoutOptions } from '../../types'; +import { type ParsedTemplateSchema } from './useTemplateSchema'; + +export const useTransformSchemaToProps = ( + step: ParsedTemplateSchema, + layouts: NextLayoutOptions[] = [], +): ParsedTemplateSchema => { + const objectFieldTemplate = step?.uiSchema['ui:ObjectFieldTemplate'] as + | string + | undefined; + + if (typeof objectFieldTemplate !== 'string') { + return step; + } + + const Layout = layouts.find( + layout => layout.name === objectFieldTemplate, + )?.component; + + if (!Layout) { + return step; + } + + return { + ...step, + uiSchema: { + ...step.uiSchema, + ['ui:ObjectFieldTemplate']: Layout, + }, + }; +}; diff --git a/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx b/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx index cbeb0bf66b..153c48468d 100644 --- a/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx +++ b/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.test.tsx @@ -233,42 +233,4 @@ describe('useTemplateSchema', () => { }); }); }); - - it('should replace ui:ObjectFieldTemplate with actual component', () => { - const layouts = [{ name: 'TwoColumn', component: jest.fn() }]; - - const manifest: TemplateParameterSchema = { - title: 'Test Template', - description: 'Test Template Description', - steps: [ - { - title: 'Step 1', - description: 'Step 1 Description', - schema: { - type: 'object', - 'ui:ObjectFieldTemplate': 'TwoColumn', - properties: { - field1: { - type: 'string', - }, - }, - }, - }, - ], - }; - - const { result } = renderHook(() => useTemplateSchema(manifest, layouts), { - wrapper: ({ children }) => ( - true }]]} - > - {children} - - ), - }); - - const [{ uiSchema }] = result.current.steps; - - expect(uiSchema['ui:ObjectFieldTemplate']).toEqual(layouts[0].component); - }); }); diff --git a/plugins/scaffolder/src/layouts/types.ts b/plugins/scaffolder/src/layouts/types.ts index 94186d82bf..dd17134dc5 100644 --- a/plugins/scaffolder/src/layouts/types.ts +++ b/plugins/scaffolder/src/layouts/types.ts @@ -16,7 +16,7 @@ import type { FormProps } from '@rjsf/core'; /** - * The field template from \@rjsf/core which is a react component that gets passed \@rjsf/core field related props. + * The field template from `@rjsf/core` which is a react component that gets passed `@rjsf/core` field related props. * * @public */ diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/useTransformSchemaToProps.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/useTransformSchemaToProps.test.tsx new file mode 100644 index 0000000000..69a685f4e8 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardPage/useTransformSchemaToProps.test.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React from 'react'; +import { renderHook } from '@testing-library/react-hooks'; +import { TestApiProvider } from '@backstage/test-utils'; +import { type ParsedTemplateSchema } from './Stepper/useTemplateSchema'; +import { useTransformSchemaToProps } from './Stepper/useTransformSchemaToProps'; + +describe('useTransformSchemaToProps', () => { + it('should replace ui:ObjectFieldTemplate with actual component', () => { + const layouts = [{ name: 'TwoColumn', component: jest.fn() }]; + + const step: ParsedTemplateSchema = { + title: 'Fill in some steps', + mergedSchema: {}, + schema: {}, + uiSchema: { + 'ui:ObjectFieldTemplate': 'TwoColumn' as any, + name: { + 'ui:field': 'EntityNamePicker', + 'ui:autofocus': true, + }, + }, + }; + + const { result } = renderHook( + () => useTransformSchemaToProps(step, layouts), + { + wrapper: ({ children }) => ( + {children} + ), + }, + ); + + const { uiSchema } = result.current; + + expect(uiSchema['ui:ObjectFieldTemplate']).toEqual(layouts[0].component); + }); +}); diff --git a/plugins/scaffolder/src/next/types.ts b/plugins/scaffolder/src/next/types.ts index e9204537f0..d23c073661 100644 --- a/plugins/scaffolder/src/next/types.ts +++ b/plugins/scaffolder/src/next/types.ts @@ -23,7 +23,7 @@ import type { FormProps as SchemaFormProps } from '@rjsf/core-v5'; /** - * The field template from \@rjsf/core which is a react component that gets passed \@rjsf/core field related props. + * The field template from `@rjsf/core` which is a react component that gets passed `@rjsf/core` field related props. * * @alpha */