From 1c5bf0b64438372b66cfbe5d371f87e36e3d04c3 Mon Sep 17 00:00:00 2001 From: Debsmita Santra Date: Tue, 16 Jul 2024 12:13:22 +0530 Subject: [PATCH] Create frontend plugin test-plugin-st --- plugins/test-plugin-st/.eslintrc.js | 1 + plugins/test-plugin-st/README.md | 73 +++++++++++++++++++ .../test-plugin-st/app-config.janus-idp.yaml | 8 ++ plugins/test-plugin-st/config.d.ts | 1 + plugins/test-plugin-st/dev/index.tsx | 29 ++++++++ plugins/test-plugin-st/package.json | 70 ++++++++++++++++++ .../ExampleComponent.test.tsx | 27 +++++++ .../ExampleComponent/ExampleComponent.tsx | 50 +++++++++++++ .../src/components/ExampleComponent/index.ts | 1 + .../src/components/ExampleComponentIcon.tsx | 13 ++++ .../ExampleFetchComponent.test.tsx | 19 +++++ .../ExampleFetchComponent.tsx | 72 ++++++++++++++++++ .../components/ExampleFetchComponent/index.ts | 1 + .../test-plugin-st/src/components/Router.tsx | 14 ++++ .../test-plugin-st/src/components/index.ts | 4 + plugins/test-plugin-st/src/index.ts | 1 + plugins/test-plugin-st/src/plugin.test.ts | 7 ++ plugins/test-plugin-st/src/plugin.ts | 34 +++++++++ plugins/test-plugin-st/src/routes.ts | 5 ++ plugins/test-plugin-st/src/setupTests.ts | 1 + .../test-plugin-st/src/utils/getChipStyle.ts | 7 ++ plugins/test-plugin-st/tsconfig.json | 9 +++ plugins/test-plugin-st/turbo.json | 9 +++ 23 files changed, 456 insertions(+) create mode 100644 plugins/test-plugin-st/.eslintrc.js create mode 100644 plugins/test-plugin-st/README.md create mode 100644 plugins/test-plugin-st/app-config.janus-idp.yaml create mode 100644 plugins/test-plugin-st/config.d.ts create mode 100644 plugins/test-plugin-st/dev/index.tsx create mode 100644 plugins/test-plugin-st/package.json create mode 100644 plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.test.tsx create mode 100644 plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.tsx create mode 100644 plugins/test-plugin-st/src/components/ExampleComponent/index.ts create mode 100644 plugins/test-plugin-st/src/components/ExampleComponentIcon.tsx create mode 100644 plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx create mode 100644 plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx create mode 100644 plugins/test-plugin-st/src/components/ExampleFetchComponent/index.ts create mode 100644 plugins/test-plugin-st/src/components/Router.tsx create mode 100644 plugins/test-plugin-st/src/components/index.ts create mode 100644 plugins/test-plugin-st/src/index.ts create mode 100644 plugins/test-plugin-st/src/plugin.test.ts create mode 100644 plugins/test-plugin-st/src/plugin.ts create mode 100644 plugins/test-plugin-st/src/routes.ts create mode 100644 plugins/test-plugin-st/src/setupTests.ts create mode 100644 plugins/test-plugin-st/src/utils/getChipStyle.ts create mode 100644 plugins/test-plugin-st/tsconfig.json create mode 100644 plugins/test-plugin-st/turbo.json diff --git a/plugins/test-plugin-st/.eslintrc.js b/plugins/test-plugin-st/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/test-plugin-st/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/test-plugin-st/README.md b/plugins/test-plugin-st/README.md new file mode 100644 index 0000000000..de6a1df770 --- /dev/null +++ b/plugins/test-plugin-st/README.md @@ -0,0 +1,73 @@ +# test-plugin-st + +Welcome to the test-plugin-st plugin! + +_This plugin was created through the Software Template_ + +## Getting started + +1. Go to the plugin directory and run `yarn install` + + +### To access the plugin in isolation: + +1. Go to the plugin directory + +2. Run `yarn start` + +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. + + +### To access it from the Root of your backstage directory: + + +1. Add the package in the `packages/app/package.json` + + ``` + "@janus-idp/backstage-plugin-test-plugin-st": "^0.1.0", + ``` + + +2. Add Route in `packages/app/src/App.tsx`: + + ```tsx title="packages/app/src/App.tsx" + /* highlight-add-next-line */ + import { PluginPage } from '@janus-idp/backstage-plugin-test-plugin-st'; + + } /> + ``` + +3. Add your plugin as a Sidebar Item in `packages/app/src/components/Root/Root.tsx`: + + ```tsx title="packages/app/src/components/Root/Root.tsx" + /* highlight-add-next-line */ + import { PluginIcon } from '@janus-idp/backstage-plugin-test-plugin-st'; + + export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + ... + + ... + + + ); + ``` + +4. Set-up the Backstage Proxy. Follow https://backstage.io/docs/integrations/github/locations#token-scopes for creating personal access token + + ```yaml title="app-config.yaml" + proxy: + ... + '/github': + target: 'https://api.github.com' + headers: + Authorization: 'token ${GITHUB_TOKEN}' + ``` + +5. Start your application from the root directory, and then navigate to [/test-plugin-st](http://localhost:3000/test-plugin-st). diff --git a/plugins/test-plugin-st/app-config.janus-idp.yaml b/plugins/test-plugin-st/app-config.janus-idp.yaml new file mode 100644 index 0000000000..7036d1fb7f --- /dev/null +++ b/plugins/test-plugin-st/app-config.janus-idp.yaml @@ -0,0 +1,8 @@ +dynamicPlugins: + frontend: + janus-idp.backstage-plugin-test-plugin-st: + apiFactories: [] + appIcons: [] + dynamicRoutes: [] + mountPoints: [] + routeBindings: [] diff --git a/plugins/test-plugin-st/config.d.ts b/plugins/test-plugin-st/config.d.ts new file mode 100644 index 0000000000..5728ccd9e1 --- /dev/null +++ b/plugins/test-plugin-st/config.d.ts @@ -0,0 +1 @@ +export interface Config {} diff --git a/plugins/test-plugin-st/dev/index.tsx b/plugins/test-plugin-st/dev/index.tsx new file mode 100644 index 0000000000..04a67d7c88 --- /dev/null +++ b/plugins/test-plugin-st/dev/index.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { createDevApp } from '@backstage/dev-utils'; +import PermIdentityOutlinedIcon from '@mui/icons-material/PermIdentityOutlined'; +import { plugin, PluginPage } from '../src/plugin'; + +const mockedDiscoveryApi = { + getBaseUrl: async (_param: any) => { + return 'http://localhost:7007/api/proxy'; + }, +}; + +createDevApp() + .registerPlugin(plugin) + .addPage({ + element: + ( + + + + ), + title: 'test-plugin-st', + path: '/test-plugin-st', + icon: () => + }) + .render(); diff --git a/plugins/test-plugin-st/package.json b/plugins/test-plugin-st/package.json new file mode 100644 index 0000000000..bc84a83791 --- /dev/null +++ b/plugins/test-plugin-st/package.json @@ -0,0 +1,70 @@ +{ + "name": "@janus-idp/backstage-plugin-test-plugin-st", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "sideEffects": false, + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "export-dynamic": "janus-cli package export-dynamic-plugin", + "lint": "backstage-cli package lint", + "postpack": "backstage-cli package postpack", + "postversion": "yarn run export-dynamic", + "prepack": "backstage-cli package prepack", + "start": "backstage-cli package start", + "test": "backstage-cli package test --passWithNoTests --coverage", + "tsc": "tsc" + }, + "dependencies": { + "@backstage/core-components": "^0.14.4", + "@backstage/core-plugin-api": "^1.9.2", + "@backstage/theme": "^0.5.5", + "@material-ui/core": "^4.9.13", + "@material-ui/icons": "^4.9.1", + "@material-ui/lab": "^4.0.0-alpha.61", + "@mui/icons-material": "^5.15.21", + "react-use": "^17.2.4" + }, + "peerDependencies": { + "react": "16.13.1 || ^17.0.0 || ^18.0.0", + "react-router-dom": "^6.0.0" + }, + "devDependencies": { + "@backstage/cli": "0.26.6", + "@backstage/core-app-api": "1.12.5", + "@backstage/dev-utils": "1.0.32", + "@backstage/test-utils": "1.5.5", + "@janus-idp/cli": "1.11.1", + "@testing-library/jest-dom": "6.0.0", + "@testing-library/react": "14.0.0", + "@testing-library/user-event": "14.0.0", + "msw": "1.0.0" + }, + "files": [ + "dist", + "config.d.ts", + "dist-scalprum", + "app-config.janus-idp.yaml" + ], + "configSchema": "config.d.ts", + "repository": { + "type": "git", + "url": "[object Object]", + "directory": "test-plugin-st" + }, + "keywords": [ + "backstage", + "plugin" + ] +} diff --git a/plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.test.tsx b/plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.test.tsx new file mode 100644 index 0000000000..6ccf5054fd --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.test.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { ExampleComponent } from './ExampleComponent'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { screen } from '@testing-library/react'; +import { + setupRequestMockHandlers, + renderInTestApp, +} from "@backstage/test-utils"; + +describe('ExampleComponent', () => { + const server = setupServer(); + // Enable sane handlers for network requests + setupRequestMockHandlers(server); + + // setup mock response + beforeEach(() => { + server.use( + rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))), + ); + }); + + it('should render', async () => { + await renderInTestApp(); + expect(screen.getByText('Welcome to test-plugin-st!')).toBeInTheDocument(); + }); +}); diff --git a/plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.tsx b/plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.tsx new file mode 100644 index 0000000000..e56175f987 --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleComponent/ExampleComponent.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Typography, Grid, makeStyles } from "@material-ui/core"; +import { + InfoCard, + Header, + Page, + Content, + ContentHeader, + HeaderLabel, + SupportButton, +} from "@backstage/core-components"; +import { ExampleFetchComponent } from "../ExampleFetchComponent"; + +const usePaperStyles = makeStyles((theme) => ({ + body: { + backgroundColor: theme.palette.background.paper, + }, +})); + +export const ExampleComponent = () => { + const classes = usePaperStyles(); + return ( + +
+ + +
+ + + A description of your plugin goes here. + + + + + + All content should be wrapped in a card like this. + + + + + + + + +
+ ); +}; diff --git a/plugins/test-plugin-st/src/components/ExampleComponent/index.ts b/plugins/test-plugin-st/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..8b8437521b --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleComponent/index.ts @@ -0,0 +1 @@ +export { ExampleComponent } from './ExampleComponent'; diff --git a/plugins/test-plugin-st/src/components/ExampleComponentIcon.tsx b/plugins/test-plugin-st/src/components/ExampleComponentIcon.tsx new file mode 100644 index 0000000000..280dbe7fc2 --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleComponentIcon.tsx @@ -0,0 +1,13 @@ +import React from 'react'; + +import { SidebarItem } from '@backstage/core-components'; +import PermIdentityOutlinedIcon from '@mui/icons-material/PermIdentityOutlined'; +import { IconComponent } from '@backstage/core-plugin-api'; + + +export const ExampleComponentIcon = () => { + + return ( + + ); +}; diff --git a/plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx b/plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx new file mode 100644 index 0000000000..c1035a8d3c --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.test.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { ExampleFetchComponent } from './ExampleFetchComponent'; + +describe('ExampleFetchComponent', () => { + it('renders the user table', async () => { + render(); + + // Wait for the table to render + const table = await screen.findByRole('table'); + const nationality = screen.getAllByText("GB") + // Assert that the table contains the expected user data + expect(table).toBeInTheDocument(); + expect(screen.getByAltText('Carolyn')).toBeInTheDocument(); + expect(screen.getByText('Carolyn Moore')).toBeInTheDocument(); + expect(screen.getByText('carolyn.moore@example.com')).toBeInTheDocument(); + expect(nationality[0]).toBeInTheDocument(); + }); +}); \ No newline at end of file diff --git a/plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx b/plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx new file mode 100644 index 0000000000..faef1cfd95 --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleFetchComponent/ExampleFetchComponent.tsx @@ -0,0 +1,72 @@ +import React from "react"; +import useAsync from "react-use/lib/useAsync"; +import { + Table, + TableColumn, + Progress, + ResponseErrorPanel, +} from "@backstage/core-components"; +import { discoveryApiRef, useApi } from "@backstage/core-plugin-api"; +import { Avatar, Chip } from "@material-ui/core"; +import { useTheme } from "@material-ui/core/styles"; +import { getChipStyle } from "../../utils/getChipStyle"; + +type User = { + repoUrl: string; + githubId: string; + type: string; +}; + +export const DenseTable = ({ users }: any) => { + const theme = useTheme(); + const chipStyle = getChipStyle(theme); + const columns: TableColumn[] = [ + { title: "GithubId", field: "githubId" }, + { title: "Repository URL", field: "repoUrl" }, + { title: "Type", field: "type" }, + ]; + + const data = users.map((user: any) => { + return { + id: user.login, + githubId: ( + } + label={user.login} + variant="outlined" + /> + ), + repoUrl: user.html_url, + type: user.type, + }; + }); + + return ( + + ); +}; + +export const ExampleFetchComponent = () => { + const discoveryApi = useApi(discoveryApiRef); + const proxyURL = discoveryApi.getBaseUrl("proxy"); + + const { value, loading, error } = useAsync(async (): Promise => { + const res = await fetch(`${await proxyURL}/github/users`); + const users = await res.json(); + return users; + }, []); + + if (loading) { + return ; + } else if (error) { + return ; + } + + return ; +}; diff --git a/plugins/test-plugin-st/src/components/ExampleFetchComponent/index.ts b/plugins/test-plugin-st/src/components/ExampleFetchComponent/index.ts new file mode 100644 index 0000000000..41a43e84f1 --- /dev/null +++ b/plugins/test-plugin-st/src/components/ExampleFetchComponent/index.ts @@ -0,0 +1 @@ +export { ExampleFetchComponent } from './ExampleFetchComponent'; diff --git a/plugins/test-plugin-st/src/components/Router.tsx b/plugins/test-plugin-st/src/components/Router.tsx new file mode 100644 index 0000000000..ed6299fb81 --- /dev/null +++ b/plugins/test-plugin-st/src/components/Router.tsx @@ -0,0 +1,14 @@ +import React from 'react'; +import { Route, Routes } from 'react-router-dom'; + +import { ExampleComponent } from './ExampleComponent'; + +/** + * + * @public + */ +export const Router = () => ( + + } /> + +); diff --git a/plugins/test-plugin-st/src/components/index.ts b/plugins/test-plugin-st/src/components/index.ts new file mode 100644 index 0000000000..5e0a17ce08 --- /dev/null +++ b/plugins/test-plugin-st/src/components/index.ts @@ -0,0 +1,4 @@ +export { Router } from './Router'; +export * from './ExampleComponent'; +export { ExampleComponentIcon } from './ExampleComponentIcon'; + diff --git a/plugins/test-plugin-st/src/index.ts b/plugins/test-plugin-st/src/index.ts new file mode 100644 index 0000000000..9646e8d8fb --- /dev/null +++ b/plugins/test-plugin-st/src/index.ts @@ -0,0 +1 @@ +export { plugin, PluginPage } from './plugin'; diff --git a/plugins/test-plugin-st/src/plugin.test.ts b/plugins/test-plugin-st/src/plugin.test.ts new file mode 100644 index 0000000000..bd922f62aa --- /dev/null +++ b/plugins/test-plugin-st/src/plugin.test.ts @@ -0,0 +1,7 @@ +import { plugin } from './plugin'; + +describe('test-plugin-st', () => { + it('should export plugin', () => { + expect(plugin).toBeDefined(); + }); +}); diff --git a/plugins/test-plugin-st/src/plugin.ts b/plugins/test-plugin-st/src/plugin.ts new file mode 100644 index 0000000000..1536b821c9 --- /dev/null +++ b/plugins/test-plugin-st/src/plugin.ts @@ -0,0 +1,34 @@ +import { + createComponentExtension, + createPlugin, + createRoutableExtension, +} from '@backstage/core-plugin-api'; + +import { rootRouteRef } from './routes'; + +export const plugin = createPlugin({ + id: 'test-plugin-st', + routes: { + root: rootRouteRef, + }, +}); + +export const PluginPage = plugin.provide( + createRoutableExtension({ + name: 'PluginPage', + component: () => + import('./components').then(m => m.Router), + mountPoint: rootRouteRef, + }), +); + +export const PluginIcon = plugin.provide( + createComponentExtension({ + name: 'PluginIcon', + component: { + lazy: () => import('./components').then(m => m.ExampleComponentIcon), + }, + }), +); + + diff --git a/plugins/test-plugin-st/src/routes.ts b/plugins/test-plugin-st/src/routes.ts new file mode 100644 index 0000000000..115627995e --- /dev/null +++ b/plugins/test-plugin-st/src/routes.ts @@ -0,0 +1,5 @@ +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'test-plugin-st', +}); diff --git a/plugins/test-plugin-st/src/setupTests.ts b/plugins/test-plugin-st/src/setupTests.ts new file mode 100644 index 0000000000..7b0828bfa8 --- /dev/null +++ b/plugins/test-plugin-st/src/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/plugins/test-plugin-st/src/utils/getChipStyle.ts b/plugins/test-plugin-st/src/utils/getChipStyle.ts new file mode 100644 index 0000000000..9e11373fad --- /dev/null +++ b/plugins/test-plugin-st/src/utils/getChipStyle.ts @@ -0,0 +1,7 @@ +import { Theme } from "@material-ui/core"; + +export const getChipStyle = (theme: Theme) => { + return { + backgroundColor: theme.palette.type === "dark" ? "#3d5061" : "#e7f1fa", + }; +}; diff --git a/plugins/test-plugin-st/tsconfig.json b/plugins/test-plugin-st/tsconfig.json new file mode 100644 index 0000000000..6242dfde9a --- /dev/null +++ b/plugins/test-plugin-st/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@backstage/cli/config/tsconfig.json", + "include": ["plugins/test-plugin-st/src", "plugins//dev"], + "exclude": ["node_modules"], + "compilerOptions": { + "outDir": "../../dist-types/plugins/test-plugin-st", + "rootDir": "." + } +} diff --git a/plugins/test-plugin-st/turbo.json b/plugins/test-plugin-st/turbo.json new file mode 100644 index 0000000000..ba1d819424 --- /dev/null +++ b/plugins/test-plugin-st/turbo.json @@ -0,0 +1,9 @@ +{ + "extends": ["//"], + "pipeline": { + "tsc": { + "outputs": ["../../dist-types/plugins/test-plugin-st/**"], + "dependsOn": ["^tsc"] + } + } +}