diff --git a/plugins/scaffolder/src/next/Router/Router.test.tsx b/plugins/scaffolder/src/next/Router/Router.test.tsx new file mode 100644 index 0000000000..5094c786e0 --- /dev/null +++ b/plugins/scaffolder/src/next/Router/Router.test.tsx @@ -0,0 +1,83 @@ +/* + * 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 from 'react'; +import { TemplateListPage } from '../TemplateListPage'; +import { TemplateWizardPage } from '../TemplateWizardPage'; +import { Router } from './Router'; +import { renderInTestApp } from '@backstage/test-utils'; +import { + createScaffolderFieldExtension, + ScaffolderFieldExtensions, +} from '../../extensions'; +import { scaffolderPlugin } from '../../plugin'; + +jest.mock('../TemplateListPage', () => ({ + TemplateListPage: jest.fn(() => null), +})); + +jest.mock('../TemplateWizardPage', () => ({ + TemplateWizardPage: jest.fn(() => null), +})); + +describe('Router', () => { + beforeEach(() => { + (TemplateWizardPage as jest.Mock).mockClear(); + (TemplateListPage as jest.Mock).mockClear(); + }); + describe('/', () => { + it('should render the TemplateListPage', async () => { + await renderInTestApp(); + + expect(TemplateListPage).toHaveBeenCalled(); + }); + }); + + describe('/templates/:templateName', () => { + it('should render the TemplateWizard page', async () => { + await renderInTestApp(, { routeEntries: ['/templates/foo'] }); + + expect(TemplateWizardPage).toHaveBeenCalled(); + }); + + it('should extract the fieldExtensions and pass them through', async () => { + const mockComponent = () => null; + const CustomFieldExtension = scaffolderPlugin.provide( + createScaffolderFieldExtension({ + name: 'custom', + component: mockComponent, + }), + ); + + await renderInTestApp( + + + + + , + { routeEntries: ['/templates/foo'] }, + ); + + const mock = TemplateWizardPage as jest.Mock; + const [{ customFieldExtensions }] = mock.mock.calls[0]; + + expect(customFieldExtensions).toEqual( + expect.arrayContaining([ + expect.objectContaining({ name: 'custom', component: mockComponent }), + ]), + ); + }); + }); +}); diff --git a/plugins/scaffolder/src/next/Router/Router.tsx b/plugins/scaffolder/src/next/Router/Router.tsx new file mode 100644 index 0000000000..8ab9755ab2 --- /dev/null +++ b/plugins/scaffolder/src/next/Router/Router.tsx @@ -0,0 +1,81 @@ +/* + * 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, { PropsWithChildren } from 'react'; +import { Routes, Route, useOutlet } from 'react-router'; +import { TemplateListPage } from '../TemplateListPage'; +import { SecretsContextProvider } from '../TemplateWizardPage/SecretsContext'; +import { TemplateWizardPage } from '../TemplateWizardPage'; +import { + FieldExtensionOptions, + FIELD_EXTENSION_WRAPPER_KEY, + FIELD_EXTENSION_KEY, + DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS, +} from '../../extensions'; + +import { useElementFilter } from '@backstage/core-plugin-api'; +import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; + +export type RouterProps = { + TemplateCardComponent?: React.ComponentType<{ + template: TemplateEntityV1beta3; + }>; +}; + +export const Router = (props: PropsWithChildren) => { + const { TemplateCardComponent } = props; + + const outlet = useOutlet() || props.children; + + const customFieldExtensions = useElementFilter(outlet, elements => + elements + .selectByComponentData({ + key: FIELD_EXTENSION_WRAPPER_KEY, + }) + .findComponentData({ + key: FIELD_EXTENSION_KEY, + }), + ); + + const fieldExtensions = [ + ...customFieldExtensions, + ...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter( + ({ name }) => + !customFieldExtensions.some( + customFieldExtension => customFieldExtension.name === name, + ), + ), + ]; + + return ( + + + } + /> + + + + + } + /> + + ); +}; diff --git a/plugins/scaffolder/src/next/Router/index.ts b/plugins/scaffolder/src/next/Router/index.ts new file mode 100644 index 0000000000..5ad2ae6e6a --- /dev/null +++ b/plugins/scaffolder/src/next/Router/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 { Router } from './Router';