diff --git a/packages/app/src/components/SecurityTab/SecurityTab.tsx b/packages/app/src/components/SecurityTab/SecurityTab.tsx index 2adca3b0ef..af4d66320d 100644 --- a/packages/app/src/components/SecurityTab/SecurityTab.tsx +++ b/packages/app/src/components/SecurityTab/SecurityTab.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ /* * Copyright 2022 The Backstage Authors * @@ -16,10 +15,30 @@ */ import React from 'react'; -import { useEntity } from '@backstage/plugin-catalog-react'; +import { + TemplateWizardContent, + useGetCustomFields, +} from '@backstage/plugin-scaffolder'; -export function SecurityTab(): JSX.Element | null { - const entity = useEntity(); - console.log(entity); - return

Security

; +interface SecurityTabProps { + customExtensionsElement: React.ReactNode; +} + +export function SecurityTab(props: SecurityTabProps): JSX.Element | null { + // eslint-disable-next-line no-alert + const onComplete = async () => alert('success!!!!'); + const onError = (error: Error | undefined) => ( +

{error?.message ?? 'Houston we have a problem.'}

+ ); + const fieldExtensions = useGetCustomFields(props.customExtensionsElement); + + return ( + + ); } diff --git a/plugins/scaffolder/src/next/Router/Router.tsx b/plugins/scaffolder/src/next/Router/Router.tsx index 784e17dd55..a7bf16e033 100644 --- a/plugins/scaffolder/src/next/Router/Router.tsx +++ b/plugins/scaffolder/src/next/Router/Router.tsx @@ -52,7 +52,9 @@ export type NextRouterProps = { */ export const Router = (props: PropsWithChildren) => { const { components: { TemplateCardComponent } = {} } = props; + const outlet = useOutlet() || props.children; + const customFieldExtensions = useCustomFieldExtensions(outlet); const fieldExtensions = [ diff --git a/plugins/scaffolder/src/next/Router/index.ts b/plugins/scaffolder/src/next/Router/index.ts index dac1db7b3a..b4df375d1d 100644 --- a/plugins/scaffolder/src/next/Router/index.ts +++ b/plugins/scaffolder/src/next/Router/index.ts @@ -13,5 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export { Router } from './Router'; +export { Router, useGetCustomFields } from './Router'; export type { NextRouterProps } from './Router'; diff --git a/plugins/scaffolder/src/next/TemplateWizardContent/TemplateWizardContent.tsx b/plugins/scaffolder/src/next/TemplateWizardContent/TemplateWizardContent.tsx new file mode 100644 index 0000000000..4dfc9be9e5 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardContent/TemplateWizardContent.tsx @@ -0,0 +1,112 @@ +/* + * 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 React, { useEffect } from 'react'; +import { + Content, + Header, + InfoCard, + MarkdownContent, + Page, + Progress, +} from '@backstage/core-components'; +import { stringifyEntityRef } from '@backstage/catalog-model'; +import { useTemplateParameterSchema } from '../TemplateWizardPage/TemplateWizardPage'; +import { NextFieldExtensionOptions } from '../../extensions'; +import type { ErrorTransformer } from '@rjsf/utils'; +import type { JsonValue } from '@backstage/types'; +import { makeStyles } from '@material-ui/core'; +import { BackstageTheme } from '@backstage/theme'; +import { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { Stepper } from '../TemplateWizardPage/Stepper'; + +const useStyles = makeStyles(() => ({ + markdown: { + /** to make the styles for React Markdown not leak into the description */ + '& :first-child': { + marginTop: 0, + }, + '& :last-child': { + marginBottom: 0, + }, + }, +})); + +export interface TemplateWizardContentProps { + namespace: string; + templateName: string; + customFieldExtensions: NextFieldExtensionOptions[]; + transformErrors?: ErrorTransformer; + onComplete: (values: Record) => Promise; + onError(error: Error | undefined): JSX.Element | null; +} + +export const TemplateWizardContent = ( + props: TemplateWizardContentProps, +): JSX.Element | null => { + const styles = useStyles(); + const templateRef = stringifyEntityRef({ + kind: 'Template', + namespace: props.namespace, + name: props.templateName, + }); + + const errorApi = useApi(errorApiRef); + + const { loading, manifest, error } = useTemplateParameterSchema(templateRef); + + useEffect(() => { + if (error) { + errorApi.post(new Error(`Failed to load template, ${error}`)); + } + }, [error, errorApi]); + + if (error) { + return props.onError(error); + } + + return ( + +
+ + {loading && } + {manifest && ( + + } + noPadding + titleTypographyProps={{ component: 'h2' }} + > + + + )} + + + ); +}; diff --git a/plugins/scaffolder/src/next/TemplateWizardContent/index.ts b/plugins/scaffolder/src/next/TemplateWizardContent/index.ts new file mode 100644 index 0000000000..3fda3efb4f --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateWizardContent/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export { TemplateWizardContent } from './TemplateWizardContent'; diff --git a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx index c4e8a51e1f..4ff47e41e9 100644 --- a/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx +++ b/plugins/scaffolder/src/next/TemplateWizardPage/TemplateWizardPage.tsx @@ -13,20 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useEffect } from 'react'; -import { - Page, - Header, - Content, - Progress, - InfoCard, - MarkdownContent, -} from '@backstage/core-components'; +import React from 'react'; import { Navigate, useNavigate } from 'react-router-dom'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { AnalyticsContext, - errorApiRef, useApi, useRouteRef, useRouteRefParams, @@ -34,37 +25,21 @@ import { import { scaffolderApiRef, useTemplateSecrets, -} from '@backstage/plugin-scaffolder-react'; -import useAsync from 'react-use/lib/useAsync'; -import { makeStyles } from '@material-ui/core'; -import { BackstageTheme } from '@backstage/theme'; -import { - Stepper, NextFieldExtensionOptions, } from '@backstage/plugin-scaffolder-react'; +import useAsync from 'react-use/lib/useAsync'; import { JsonValue } from '@backstage/types'; import { FormProps } from '../types'; import { nextRouteRef } from '../routes'; import { scaffolderTaskRouteRef, selectedTemplateRouteRef } from '../../routes'; +import { TemplateWizardContent } from '../TemplateWizardContent/TemplateWizardContent'; type TemplateWizardPageProps = { customFieldExtensions: NextFieldExtensionOptions[]; FormProps?: FormProps; }; -const useStyles = makeStyles(() => ({ - markdown: { - /** to make the styles for React Markdown not leak into the description */ - '& :first-child': { - marginTop: 0, - }, - '& :last-child': { - marginBottom: 0, - }, - }, -})); - -const useTemplateParameterSchema = (templateRef: string) => { +export const useTemplateParameterSchema = (templateRef: string) => { const scaffolderApi = useApi(scaffolderApiRef); const { value, loading, error } = useAsync( () => scaffolderApi.getTemplateParameterSchema(templateRef), @@ -75,7 +50,6 @@ const useTemplateParameterSchema = (templateRef: string) => { }; export const TemplateWizardPage = (props: TemplateWizardPageProps) => { - const styles = useStyles(); const rootRef = useRouteRef(nextRouteRef); const taskRoute = useRouteRef(scaffolderTaskRouteRef); const { secrets } = useTemplateSecrets(); @@ -91,9 +65,6 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => { name: templateName, }); - const errorApi = useApi(errorApiRef); - const { loading, manifest, error } = useTemplateParameterSchema(templateRef); - const onComplete = async (values: Record) => { const { taskId } = await scaffolderApi.scaffold({ templateRef, @@ -104,48 +75,17 @@ export const TemplateWizardPage = (props: TemplateWizardPageProps) => { navigate(taskRoute({ taskId })); }; - useEffect(() => { - if (error) { - errorApi.post(new Error(`Failed to load template, ${error}`)); - } - }, [error, errorApi]); - - if (error) { - return ; - } + const onError = () => ; return ( - -
- - {loading && } - {manifest && ( - - } - noPadding - titleTypographyProps={{ component: 'h2' }} - > - - - )} - - + ); }; diff --git a/plugins/scaffolder/src/next/index.ts b/plugins/scaffolder/src/next/index.ts index 1ca9096459..fd88d57551 100644 --- a/plugins/scaffolder/src/next/index.ts +++ b/plugins/scaffolder/src/next/index.ts @@ -18,3 +18,4 @@ export * from './TemplateListPage'; export * from './TemplateWizardPage'; export * from './types'; export * from './routes'; +export * from './TemplateWizardContent';