From 66a2a870bccab753ff80f9d093f3519850bb36d2 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 7 Sep 2020 20:04:23 +0200 Subject: [PATCH] plugins/tech-radar: migrate to new plugin pattern and avoid using api for config --- packages/app/src/App.tsx | 5 + packages/app/src/apis.ts | 10 -- .../default-app/packages/app/src/App.tsx | 5 + .../default-app/packages/app/src/apis.ts | 10 -- plugins/tech-radar/README.md | 108 ++++------------- plugins/tech-radar/dev/index.tsx | 12 +- plugins/tech-radar/package.json | 2 +- plugins/tech-radar/src/api.ts | 35 ------ .../src/components/RadarComponent.test.tsx | 2 +- .../src/components/RadarPage.test.tsx | 110 ++++-------------- .../tech-radar/src/components/RadarPage.tsx | 66 ++++++----- plugins/tech-radar/src/index.ts | 2 + plugins/tech-radar/src/plugin.ts | 4 - 13 files changed, 102 insertions(+), 269 deletions(-) diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 8ddf53783d..8482bc9a8a 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -28,6 +28,7 @@ import { hot } from 'react-hot-loader/root'; import { providers } from './identityProviders'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; import { Route, Routes, Navigate } from 'react-router'; import { EntityPage } from './components/catalog/EntityPage'; @@ -60,6 +61,10 @@ const AppRoutes = () => ( element={} /> } /> + } + /> {...deprecatedAppRoutes} diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index b391e95d62..b79a6850bd 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -51,8 +51,6 @@ import { LighthouseRestApi, } from '@backstage/plugin-lighthouse'; -import { techRadarApiRef, TechRadar } from '@backstage/plugin-tech-radar'; - import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci'; import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog'; @@ -182,14 +180,6 @@ export const apis = (config: ConfigApi) => { }), ); - builder.add( - techRadarApiRef, - new TechRadar({ - width: 1500, - height: 800, - }), - ); - builder.add(catalogApiRef, new CatalogClient({ discoveryApi })); builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx index 3e6a0ef00e..06ef257931 100644 --- a/packages/create-app/templates/default-app/packages/app/src/App.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx @@ -11,6 +11,7 @@ import { AppSidebar } from './sidebar'; import { Route, Routes, Navigate } from 'react-router'; import { Router as CatalogRouter } from '@backstage/plugin-catalog'; import { Router as DocsRouter } from '@backstage/plugin-techdocs'; +import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; import { EntityPage } from './components/catalog/EntityPage'; const app = createApp({ @@ -35,6 +36,10 @@ const App: FC<{}> = () => ( element={} /> } /> + } + /> {deprecatedAppRoutes} diff --git a/packages/create-app/templates/default-app/packages/app/src/apis.ts b/packages/create-app/templates/default-app/packages/app/src/apis.ts index 14351eaba7..ba4343801f 100644 --- a/packages/create-app/templates/default-app/packages/app/src/apis.ts +++ b/packages/create-app/templates/default-app/packages/app/src/apis.ts @@ -29,8 +29,6 @@ import { TechDocsStorageApi, } from '@backstage/plugin-techdocs'; -import { techRadarApiRef, TechRadar } from '@backstage/plugin-tech-radar'; - import { catalogApiRef, CatalogClient } from '@backstage/plugin-catalog'; import { CircleCIApi, circleCIApiRef } from '@backstage/plugin-circleci'; @@ -72,14 +70,6 @@ export const apis = (config: ConfigApi) => { builder.add(scaffolderApiRef, new ScaffolderApi({ discoveryApi })); - builder.add( - techRadarApiRef, - new TechRadar({ - width: 1500, - height: 800, - }), - ); - builder.add( techdocsStorageApiRef, new TechDocsStorageApi({ apiOrigin: techdocsStorageUrl }), diff --git a/plugins/tech-radar/README.md b/plugins/tech-radar/README.md index fb9d220b8c..cbe4dd22eb 100644 --- a/plugins/tech-radar/README.md +++ b/plugins/tech-radar/README.md @@ -29,71 +29,34 @@ For either simple or advanced installations, you'll need to add the dependency u yarn add @backstage/plugin-tech-radar ``` -### Simple Configuration +### Configuration -In your `apis.ts` set up the simple "out of the box" implementation for Tech Radar: - -```ts -import { ApiHolder, ApiRegistry } from '@backstage/core'; -import { - techRadarApiRef, - TechRadar, -} from '@backstage/plugin-tech-radar'; - -const builder = ApiRegistry.builder(); - -builder.add(techRadarApiRef, new TechRadar({ - width: 1400, - height: 800 -)); - -export default builder.build() as ApiHolder; -``` - -Congrats, you're done! We'll just load it with [example data](src/sampleData.ts) to get you started. Just go to to see it live in action. - -And if you'd like to configure it more, such as providing it with your own data, see the `TechRadarApi` TypeScript interface below for the options: - -```ts -export interface TechRadarComponentProps { - width: number; - height: number; - getData?: () => Promise; - svgProps?: object; -} - -export interface TechRadarApi extends TechRadarComponentProps { - title?: string; - subtitle?: string; -} -``` - -You can see the API directly over at [src/api.ts](./src/api.ts). - -### Advanced Configuration - -This way won't expose an `/tech-radar` path. Instead, you'll need to create your own Backstage plugin and use the Tech Radar as any other React UI component. - -In your Backstage app, run the following command: - -```sh -yarn create-plugin -``` - -In your plugin, in any React component you'd like to import the Tech Radar, do the following: +Modify your app routes to include the Router component exported from the tech radar, for example: ```tsx -import { TechRadarComponent } from '@backstage/plugin-tech-radar'; +import { Router as TechRadarRouter } from '@backstage/plugin-tech-radar'; -function MyCustomRadar() { - return ; -} +// Inside App component + + {/* other routes ... */} + } + /> + {/* other routes ... */} +; ``` -If you'd like to configure it more, see the `TechRadarComponentProps` TypeScript interface for options: +If you'd like to configure it more, see the `TechRadarPageProps` and `TechRadarComponentProps` types for options: ```ts -export interface TechRadarComponentProps { +export type TechRadarPageProps = TechRadarComponentProps & { + title?: string; + subtitle?: string; + pageTitle?: string; +}; + +export interface TechRadarPageProps { width: number; height: number; getData?: () => Promise; @@ -101,8 +64,6 @@ export interface TechRadarComponentProps { } ``` -You can see the API directly over at [src/api.ts](./src/api.ts). - ## Frequently Asked Questions ### Who created the Tech Radar? @@ -111,7 +72,7 @@ You can see the API directly over at [src/api.ts](./src/api.ts). ### How do I load in my own data? -It's simple. In both the Simple (Backstage plugin) and Advanced (React component) configurations, you can pass through a `getData` prop which expects a `Promise` signature. See more in [src/api.ts](./src/api.ts). +It's simple, you can pass through a `getData` prop which expects a `Promise` signature. Here's an example: @@ -133,42 +94,21 @@ const getHardCodedData = () => ], }); -// Simple -builder.add(techRadarApiRef, new TechRadar({ - width: 1400, - height: 800, - getData: getHardCodedData -)); - -// Advanced - +; ``` ### How do I write tests? You can use the `svgProps` option to pass custom React props to the `` element we create for the Tech Radar. This complements well with the `data-testid` attribute and the `@testing-library/react` library we use in Backstage. -```ts -// Simple -builder.add( - techRadarApiRef, - new TechRadar({ - width: 1400, - height: 800, - svgProps: { - 'data-testid': 'tech-radar-svg', - }, - }), -); - -// Advanced +```tsx ; +/> // Then, in your tests... // const { getByTestId } = render(...); diff --git a/plugins/tech-radar/dev/index.tsx b/plugins/tech-radar/dev/index.tsx index ac19b63a90..92eb6da567 100644 --- a/plugins/tech-radar/dev/index.tsx +++ b/plugins/tech-radar/dev/index.tsx @@ -15,14 +15,6 @@ */ import { createDevApp } from '@backstage/dev-utils'; -import { plugin } from '../src/plugin'; -import { techRadarApiRef, TechRadar } from '../src'; +import { plugin } from '../src'; -createDevApp() - .registerPlugin(plugin) - .registerApiFactory({ - implements: techRadarApiRef, - deps: {}, - factory: () => new TechRadar({ width: 1500, height: 800 }), - }) - .render(); +createDevApp().registerPlugin(plugin).render(); diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 4039f40878..f5639c7581 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -22,7 +22,7 @@ }, "dependencies": { "@backstage/core": "^0.1.1-alpha.21", - "@backstage/test-utils-core": "^0.1.1-alpha.21", + "@backstage/test-utils": "^0.1.1-alpha.21", "@backstage/theme": "^0.1.1-alpha.21", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", diff --git a/plugins/tech-radar/src/api.ts b/plugins/tech-radar/src/api.ts index 856cf4ff3c..7a6e790136 100644 --- a/plugins/tech-radar/src/api.ts +++ b/plugins/tech-radar/src/api.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { createApiRef } from '@backstage/core'; import { MovedState } from './utils/types'; /** @@ -72,37 +71,3 @@ export interface TechRadarApi extends TechRadarComponentProps { subtitle?: string; pageTitle?: string; } - -export const techRadarApiRef = createApiRef({ - id: 'plugin.techradar', - description: 'Used by the Tech Radar to render the visualization', -}); - -export class TechRadar implements TechRadarApi { - // Default columns - public width: TechRadarApi['width']; - public height: TechRadarApi['height']; - public getData: TechRadarApi['getData']; - public svgProps: TechRadarApi['svgProps']; - public title: TechRadarApi['title']; - public subtitle: TechRadarApi['subtitle']; - public pageTitle: TechRadarApi['pageTitle']; - - constructor(overrideOptions: TechRadarApi) { - const defaultOptions: Partial = { - title: 'Tech Radar', - subtitle: 'Pick the recommended technologies for your projects', - pageTitle: 'Company Radar', - }; - - const options = { ...defaultOptions, ...overrideOptions }; - - this.width = options.width; - this.height = options.height; - this.getData = options.getData; - this.svgProps = options.svgProps; - this.title = options.title; - this.subtitle = options.subtitle; - this.pageTitle = options.pageTitle; - } -} diff --git a/plugins/tech-radar/src/components/RadarComponent.test.tsx b/plugins/tech-radar/src/components/RadarComponent.test.tsx index 2f3fcff198..5591d74b82 100644 --- a/plugins/tech-radar/src/components/RadarComponent.test.tsx +++ b/plugins/tech-radar/src/components/RadarComponent.test.tsx @@ -19,7 +19,7 @@ import { render, waitForElement } from '@testing-library/react'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core'; -import { withLogCollector } from '@backstage/test-utils-core'; +import { withLogCollector } from '@backstage/test-utils'; import GetBBoxPolyfill from '../utils/polyfills/getBBox'; import RadarComponent from './RadarComponent'; diff --git a/plugins/tech-radar/src/components/RadarPage.test.tsx b/plugins/tech-radar/src/components/RadarPage.test.tsx index c318985acd..97fae16380 100644 --- a/plugins/tech-radar/src/components/RadarPage.test.tsx +++ b/plugins/tech-radar/src/components/RadarPage.test.tsx @@ -19,11 +19,10 @@ import { render, waitForElement } from '@testing-library/react'; import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { ApiRegistry, ApiProvider, errorApiRef } from '@backstage/core'; -import { withLogCollector } from '@backstage/test-utils-core'; import GetBBoxPolyfill from '../utils/polyfills/getBBox'; -import { techRadarApiRef, TechRadar } from '../index'; -import RadarPage from './RadarPage'; +import { RadarPage } from './RadarPage'; +import { MockErrorApi, wrapInTestApp } from '@backstage/test-utils'; describe('RadarPage', () => { beforeAll(() => { @@ -35,24 +34,18 @@ describe('RadarPage', () => { }); it('should render a progress bar', async () => { - const errorApi = { post: () => {} }; - const techRadarApi = new TechRadar({ + const techRadarProps = { width: 1200, height: 800, svgProps: { 'data-testid': 'tech-radar-svg' }, - }); + }; const { getByTestId, queryByTestId } = render( - - - - - , + wrapInTestApp( + + + , + ), ); expect(getByTestId('progress')).toBeInTheDocument(); @@ -61,24 +54,18 @@ describe('RadarPage', () => { }); it('should render a header with a svg', async () => { - const errorApi = { post: () => {} }; - const techRadarApi = new TechRadar({ + const techRadarProps = { width: 1200, height: 800, svgProps: { 'data-testid': 'tech-radar-svg' }, - }); + }; const { getByText, getByTestId } = render( - - - - - , + wrapInTestApp( + + + , + ), ); await waitForElement(() => getByTestId('tech-radar-svg')); @@ -90,78 +77,29 @@ describe('RadarPage', () => { }); it('should call the errorApi if load fails', async () => { - const errorApi = { post: jest.fn() }; + const errorApi = new MockErrorApi({ collect: true }); const techRadarLoadFail = () => Promise.reject(new Error('404 Page Not Found')); - const techRadarApi = new TechRadar({ + const techRadarProps = { width: 1200, height: 800, getData: techRadarLoadFail, svgProps: { 'data-testid': 'tech-radar-svg' }, - }); + }; const { queryByTestId } = render( - - + + , ); await waitForElement(() => !queryByTestId('progress')); - expect(errorApi.post).toHaveBeenCalledTimes(1); - expect(errorApi.post).toHaveBeenCalledWith(new Error('404 Page Not Found')); + expect(errorApi.getErrors()).toEqual([ + { error: new Error('404 Page Not Found'), context: undefined }, + ]); expect(queryByTestId('tech-radar-svg')).not.toBeInTheDocument(); }); - - it('should not render without errorApiRef', () => { - const techRadarApi = new TechRadar({ - width: 1200, - height: 800, - }); - - expect( - withLogCollector(['error'], () => { - expect(() => { - render( - - - - - , - ); - }).toThrow(); - }).error[0], - ).toMatch( - /^Error: Uncaught \[Error: No implementation available for apiRef{core.error}\]/, - ); - }); - - it('should not render without techRadarApiRef', () => { - const errorApi = { post: () => {} }; - - expect( - withLogCollector(['error'], () => { - expect(() => { - render( - - - - - , - ); - }).toThrow(); - }).error[0], - ).toMatch( - /^Error: Uncaught \[Error: No implementation available for apiRef{plugin.techradar}\]/, - ); - }); }); diff --git a/plugins/tech-radar/src/components/RadarPage.tsx b/plugins/tech-radar/src/components/RadarPage.tsx index 1aefb72eca..519c9afeee 100644 --- a/plugins/tech-radar/src/components/RadarPage.tsx +++ b/plugins/tech-radar/src/components/RadarPage.tsx @@ -24,36 +24,46 @@ import { HeaderLabel, SupportButton, pageTheme, - useApi, } from '@backstage/core'; import RadarComponent from '../components/RadarComponent'; -import { techRadarApiRef, TechRadarApi } from '../api'; +import { TechRadarComponentProps } from '../api'; -const RadarPage = (): JSX.Element => { - const techRadarApi = useApi(techRadarApiRef); - - return ( - -
- - -
- - - - This is used for visualizing the official guidelines of different - areas of software development such as languages, frameworks, - infrastructure and processes. - - - - - - - - -
- ); +export type TechRadarPageProps = TechRadarComponentProps & { + title?: string; + subtitle?: string; + pageTitle?: string; }; -export default RadarPage; +export const RadarPage = ({ + title, + subtitle, + pageTitle, + ...props +}: TechRadarPageProps): JSX.Element => ( + +
+ + +
+ + + + This is used for visualizing the official guidelines of different + areas of software development such as languages, frameworks, + infrastructure and processes. + + + + + + + + +
+); + +RadarPage.defaultProps = { + title: 'Tech Radar', + subtitle: 'Pick the recommended technologies for your projects', + pageTitle: 'Company Radar', +}; diff --git a/plugins/tech-radar/src/index.ts b/plugins/tech-radar/src/index.ts index df22e57558..d7b4921e9a 100644 --- a/plugins/tech-radar/src/index.ts +++ b/plugins/tech-radar/src/index.ts @@ -16,6 +16,8 @@ export { plugin } from './plugin'; +export { RadarPage as Router } from './components/RadarPage'; + /** * The TypeScript API for configuring Tech Radar. */ diff --git a/plugins/tech-radar/src/plugin.ts b/plugins/tech-radar/src/plugin.ts index 3497a66f5d..d8dc41af2e 100644 --- a/plugins/tech-radar/src/plugin.ts +++ b/plugins/tech-radar/src/plugin.ts @@ -15,11 +15,7 @@ */ import { createPlugin } from '@backstage/core'; -import RadarPage from './components/RadarPage'; export const plugin = createPlugin({ id: 'tech-radar', - register({ router }) { - router.registerRoute('/tech-radar', RadarPage); - }, });