From 57ad6553d04bca7963100f972ed625a32dfe0a90 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Fri, 16 Dec 2022 18:40:10 +0000 Subject: [PATCH 1/7] pass through transformErrors to TemplateWizardPage Signed-off-by: Paul Cowan --- .changeset/perfect-garlics-care.md | 5 +++++ plugins/scaffolder/src/next/Router/Router.test.tsx | 14 ++++++++++++++ plugins/scaffolder/src/next/Router/Router.tsx | 5 ++++- 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/perfect-garlics-care.md diff --git a/.changeset/perfect-garlics-care.md b/.changeset/perfect-garlics-care.md new file mode 100644 index 0000000000..e6f56e6d69 --- /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/src/next/Router/Router.test.tsx b/plugins/scaffolder/src/next/Router/Router.test.tsx index 1aaca1a94b..78e21f57fd 100644 --- a/plugins/scaffolder/src/next/Router/Router.test.tsx +++ b/plugins/scaffolder/src/next/Router/Router.test.tsx @@ -54,6 +54,20 @@ 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 [{ 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..ecf89c8742 100644 --- a/plugins/scaffolder/src/next/Router/Router.tsx +++ b/plugins/scaffolder/src/next/Router/Router.tsx @@ -93,7 +93,10 @@ export const Router = (props: PropsWithChildren) => { path={nextSelectedTemplateRouteRef.path} element={ - + } /> From b23bf0c84d941794aeae22b38f0df2c1630901b9 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Fri, 16 Dec 2022 18:54:10 +0000 Subject: [PATCH 2/7] run api-report Signed-off-by: Paul Cowan --- plugins/scaffolder/api-report.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 68bc893b6f..db928f0c62 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/api-report.md @@ -110,9 +110,9 @@ export type EntityPickerUiOptions = export const EntityTagsPickerFieldExtension: FieldExtensionComponent< string[], { - showCounts?: boolean | undefined; - kinds?: string[] | undefined; helperText?: string | undefined; + kinds?: string[] | undefined; + showCounts?: boolean | undefined; } >; @@ -120,9 +120,9 @@ export const EntityTagsPickerFieldExtension: FieldExtensionComponent< export const EntityTagsPickerFieldSchema: FieldSchema< string[], { - showCounts?: boolean | undefined; - kinds?: string[] | undefined; helperText?: string | undefined; + kinds?: string[] | undefined; + showCounts?: boolean | undefined; } >; From 0fb3edbda2a5846f780fc74bd103401446bad397 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Fri, 16 Dec 2022 19:06:11 +0000 Subject: [PATCH 3/7] fix changeset message Signed-off-by: Paul Cowan --- .changeset/perfect-garlics-care.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/perfect-garlics-care.md b/.changeset/perfect-garlics-care.md index e6f56e6d69..121ffd5c0f 100644 --- a/.changeset/perfect-garlics-care.md +++ b/.changeset/perfect-garlics-care.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder': patch --- -Pass through transformErrors to TemplateWizardPage +Pass through `transformErrors` to `TemplateWizardPage` From 1469f0b2de0b829c585aa32db895ebb1725c1840 Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Mon, 19 Dec 2022 16:46:32 +0000 Subject: [PATCH 4/7] create a FormProps intersection type to augment next/Router props Signed-off-by: Paul Cowan --- plugins/scaffolder/api-report.md | 12 ++++++---- plugins/scaffolder/src/index.ts | 1 + plugins/scaffolder/src/next/Router/Router.tsx | 5 ++-- .../TemplateWizardPage/Stepper/Stepper.tsx | 7 +++--- .../TemplateWizardPage/TemplateWizardPage.tsx | 7 +++--- plugins/scaffolder/src/next/index.ts | 2 ++ plugins/scaffolder/src/next/types.ts | 23 +++++++++++++++++++ 7 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 plugins/scaffolder/src/next/types.ts diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index db928f0c62..808decc89e 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,8 +267,7 @@ export type NextRouterProps = { TaskPageComponent?: React_2.ComponentType<{}>; }; groups?: TemplateGroupFilter[]; - transformErrors?: ErrorTransformer; -}; +} & FormProps; // @alpha export const NextScaffolderPage: ( 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.tsx b/plugins/scaffolder/src/next/Router/Router.tsx index ecf89c8742..9ce1ad9588 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,8 +45,7 @@ export type NextRouterProps = { TaskPageComponent?: React.ComponentType<{}>; }; groups?: TemplateGroupFilter[]; - transformErrors?: ErrorTransformer; -}; +} & FormProps; /** * The Scaffolder Router diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx index fffacae2f2..c11fd450ee 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,11 @@ const useStyles = makeStyles(theme => ({ }, })); -export interface StepperProps { +export type StepperProps = { manifest: TemplateParameterSchema; extensions: NextFieldExtensionOptions[]; onComplete: (values: Record) => Promise; - transformErrors?: ErrorTransformer; -} +} & 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 diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx index 31a5808233..0990bb40ce 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx @@ -44,12 +44,11 @@ import { } from '../../routes'; import { SecretsContext } from '../../components/secrets/SecretsContext'; import { JsonValue } from '@backstage/types'; -import type { ErrorTransformer } from '@rjsf/utils'; +import type { FormProps } from '../types'; -export interface TemplateWizardPageProps { +export type TemplateWizardPageProps = { customFieldExtensions: NextFieldExtensionOptions[]; - transformErrors?: ErrorTransformer; -} +} & FormProps; const useStyles = makeStyles(() => ({ markdown: { diff --git a/plugins/scaffolder/src/next/index.ts b/plugins/scaffolder/src/next/index.ts index 089c268b0b..3a874e3b41 100644 --- a/plugins/scaffolder/src/next/index.ts +++ b/plugins/scaffolder/src/next/index.ts @@ -16,3 +16,5 @@ export * from './Router'; export * from './TemplateListPage'; export * from './TemplateWizardPage'; + +export type { FormProps } from './types'; diff --git a/plugins/scaffolder/src/next/types.ts b/plugins/scaffolder/src/next/types.ts new file mode 100644 index 0000000000..d0d3ed1011 --- /dev/null +++ b/plugins/scaffolder/src/next/types.ts @@ -0,0 +1,23 @@ +/* + * Copyright 2022 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 { FormProps as SchemaFormProps } from '@rjsf/core-v5'; + +/** + * Any `@rjsf/core` form properties that are publicly exposed to the `NextScaffolderpage` + * + * @alpha + */ +export type FormProps = Pick; From ed00e37d58a55b3ae136231ba795e366afbd583a Mon Sep 17 00:00:00 2001 From: Paul Cowan Date: Tue, 20 Dec 2022 09:30:49 +0000 Subject: [PATCH 5/7] make FormProps a prop on next/Router Signed-off-by: Paul Cowan --- .../scaffolder/src/next/Router/Router.test.tsx | 15 +++++++++++---- plugins/scaffolder/src/next/Router/Router.tsx | 5 +++-- .../TemplateWizardPage/Stepper/Stepper.test.tsx | 2 +- .../next/TemplateWizardPage/Stepper/Stepper.tsx | 5 +++-- .../TemplateWizardPage/TemplateWizardPage.tsx | 5 +++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/plugins/scaffolder/src/next/Router/Router.test.tsx b/plugins/scaffolder/src/next/Router/Router.test.tsx index 78e21f57fd..0e890005ac 100644 --- a/plugins/scaffolder/src/next/Router/Router.test.tsx +++ b/plugins/scaffolder/src/next/Router/Router.test.tsx @@ -57,13 +57,20 @@ describe('Router', () => { it('should pass through the transformErrors property', async () => { const transformErrorsMock = jest.fn(); - await renderInTestApp(, { - routeEntries: ['/templates/default/foo'], - }); + await renderInTestApp( + , + { + routeEntries: ['/templates/default/foo'], + }, + ); const mock = TemplateWizardPage as jest.Mock; - const [{ transformErrors }] = mock.mock.calls[0]; + const [ + { + FormProps: { transformErrors }, + }, + ] = mock.mock.calls[0]; expect(transformErrors).toEqual(transformErrors); }); diff --git a/plugins/scaffolder/src/next/Router/Router.tsx b/plugins/scaffolder/src/next/Router/Router.tsx index 9ce1ad9588..7a2d2826e7 100644 --- a/plugins/scaffolder/src/next/Router/Router.tsx +++ b/plugins/scaffolder/src/next/Router/Router.tsx @@ -45,7 +45,8 @@ export type NextRouterProps = { TaskPageComponent?: React.ComponentType<{}>; }; groups?: TemplateGroupFilter[]; -} & FormProps; + FormProps?: FormProps; +}; /** * The Scaffolder Router @@ -94,7 +95,7 @@ export const Router = (props: PropsWithChildren) => { } 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 c11fd450ee..27230b2f7b 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/Stepper/Stepper.tsx @@ -59,7 +59,8 @@ export type StepperProps = { manifest: TemplateParameterSchema; extensions: NextFieldExtensionOptions[]; onComplete: (values: Record) => Promise; -} & FormProps; + 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 @@ -164,7 +165,7 @@ export const Stepper = (props: StepperProps) => { onSubmit={handleNext} fields={extensions} showErrorList={false} - transformErrors={props.transformErrors} + {...(props.FormProps ?? {})} >