diff --git a/.changeset/perfect-garlics-care.md b/.changeset/perfect-garlics-care.md new file mode 100644 index 0000000000..121ffd5c0f --- /dev/null +++ b/.changeset/perfect-garlics-care.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Pass through `transformErrors` to `TemplateWizardPage` diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 68bc893b6f..7611e952dc 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -11,7 +11,6 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; import { DiscoveryApi } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; -import type { ErrorTransformer } from '@rjsf/utils'; import { Extension } from '@backstage/core-plugin-api'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; import { FetchApi } from '@backstage/core-plugin-api'; @@ -19,7 +18,8 @@ import { FieldProps } from '@rjsf/core'; import { FieldProps as FieldProps_2 } from '@rjsf/utils'; import { FieldValidation } from '@rjsf/core'; import { FieldValidation as FieldValidation_2 } from '@rjsf/utils'; -import type { FormProps } from '@rjsf/core'; +import type { FormProps as FormProps_2 } from '@rjsf/core'; +import type { FormProps as FormProps_3 } from '@rjsf/core-v5'; import { IdentityApi } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { JSONSchema7 } from 'json-schema'; @@ -167,6 +167,9 @@ export interface FieldSchema { readonly uiOptionsType: TUiOptions; } +// @alpha +export type FormProps = Pick; + // @public export type LayoutComponent<_TInputProps> = () => null; @@ -179,7 +182,7 @@ export interface LayoutOptions

{ } // @public -export type LayoutTemplate = FormProps['ObjectFieldTemplate']; +export type LayoutTemplate = FormProps_2['ObjectFieldTemplate']; // @public export type ListActionsResponse = Array<{ @@ -264,7 +267,7 @@ export type NextRouterProps = { TaskPageComponent?: React_2.ComponentType<{}>; }; groups?: TemplateGroupFilter[]; - transformErrors?: ErrorTransformer; + FormProps?: FormProps; }; // @alpha diff --git a/plugins/scaffolder/src/index.ts b/plugins/scaffolder/src/index.ts index 0e5e2630ac..1ed1346176 100644 --- a/plugins/scaffolder/src/index.ts +++ b/plugins/scaffolder/src/index.ts @@ -74,6 +74,7 @@ export type { TaskPageProps } from './components/TaskPage'; export { NextScaffolderPage } from './plugin'; export type { NextRouterProps } from './next'; export type { TemplateGroupFilter } from './next'; +export type { FormProps } from './next'; export { createNextScaffolderFieldExtension, type NextCustomFieldValidator, diff --git a/plugins/scaffolder/src/next/Router/Router.test.tsx b/plugins/scaffolder/src/next/Router/Router.test.tsx index 1aaca1a94b..0e890005ac 100644 --- a/plugins/scaffolder/src/next/Router/Router.test.tsx +++ b/plugins/scaffolder/src/next/Router/Router.test.tsx @@ -54,6 +54,27 @@ describe('Router', () => { expect(TemplateWizardPage).toHaveBeenCalled(); }); + it('should pass through the transformErrors property', async () => { + const transformErrorsMock = jest.fn(); + + await renderInTestApp( + , + { + routeEntries: ['/templates/default/foo'], + }, + ); + + const mock = TemplateWizardPage as jest.Mock; + + const [ + { + FormProps: { transformErrors }, + }, + ] = mock.mock.calls[0]; + + expect(transformErrors).toEqual(transformErrors); + }); + it('should extract the fieldExtensions and pass them through', async () => { const mockComponent = () => null; const CustomFieldExtension = scaffolderPlugin.provide( diff --git a/plugins/scaffolder/src/next/Router/Router.tsx b/plugins/scaffolder/src/next/Router/Router.tsx index 9ca1e39681..7a2d2826e7 100644 --- a/plugins/scaffolder/src/next/Router/Router.tsx +++ b/plugins/scaffolder/src/next/Router/Router.tsx @@ -30,7 +30,7 @@ import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateGroupFilter } from '../TemplateListPage/TemplateGroups'; import { nextSelectedTemplateRouteRef } from '../../routes'; import { SecretsContextProvider } from '../../components/secrets/SecretsContext'; -import type { ErrorTransformer } from '@rjsf/utils'; +import type { FormProps } from '../types'; /** * The Props for the Scaffolder Router @@ -45,7 +45,7 @@ export type NextRouterProps = { TaskPageComponent?: React.ComponentType<{}>; }; groups?: TemplateGroupFilter[]; - transformErrors?: ErrorTransformer; + FormProps?: FormProps; }; /** @@ -93,7 +93,10 @@ export const Router = (props: PropsWithChildren) => { path={nextSelectedTemplateRouteRef.path} element={ - + } /> diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.test.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.test.tsx index c8dbb0cb19..af86abf1c6 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.test.tsx @@ -174,7 +174,7 @@ describe('Stepper', () => { manifest={manifest} extensions={[]} onComplete={jest.fn()} - transformErrors={transformErrors} + FormProps={{ transformErrors }} />, ); diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx index fffacae2f2..27230b2f7b 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx @@ -36,9 +36,9 @@ import { useTemplateSchema } from './useTemplateSchema'; import { ReviewState } from './ReviewState'; import validator from '@rjsf/validator-ajv8'; import { selectedTemplateRouteRef } from '../../../routes'; -import type { ErrorTransformer } from '@rjsf/utils'; import { getDefaultFormState } from '@rjsf/utils'; import { useFormData } from './useFormData'; +import { FormProps } from '../../types'; const useStyles = makeStyles(theme => ({ backButton: { @@ -55,12 +55,12 @@ const useStyles = makeStyles(theme => ({ }, })); -export interface StepperProps { +export type StepperProps = { manifest: TemplateParameterSchema; extensions: NextFieldExtensionOptions[]; onComplete: (values: Record) => Promise; - transformErrors?: ErrorTransformer; -} + FormProps?: FormProps; +}; // TODO(blam): We require here, as the types in this package depend on @rjsf/core explicitly // which is what we're using here as the default types, it needs to depend on @rjsf/core-v5 because @@ -165,7 +165,7 @@ export const Stepper = (props: StepperProps) => { onSubmit={handleNext} fields={extensions} showErrorList={false} - transformErrors={props.transformErrors} + {...(props.FormProps ?? {})} >