Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create frontend plugin test-plugin-st #4

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions plugins/test-plugin-st/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
73 changes: 73 additions & 0 deletions plugins/test-plugin-st/README.md
Original file line number Diff line number Diff line change
@@ -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';

<Route path="/test-plugin-st" element={<PluginPage />} />
```

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<{}>) => (
<SidebarPage>
<Sidebar>
...
<SidebarItem
icon={PluginIcon as IconComponent}
to="test-plugin-st"
text="test-plugin-st"
/>
...
<Sidebar>
</SidebarPage>
);
```

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).
8 changes: 8 additions & 0 deletions plugins/test-plugin-st/app-config.janus-idp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dynamicPlugins:
frontend:
janus-idp.backstage-plugin-test-plugin-st:
apiFactories: []
appIcons: []
dynamicRoutes: []
mountPoints: []
routeBindings: []
1 change: 1 addition & 0 deletions plugins/test-plugin-st/config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export interface Config {}
29 changes: 29 additions & 0 deletions plugins/test-plugin-st/dev/index.tsx
Original file line number Diff line number Diff line change
@@ -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:
(
<TestApiProvider
apis={[
[discoveryApiRef, mockedDiscoveryApi],
]}
>
<PluginPage />
</TestApiProvider>
),
title: 'test-plugin-st',
path: '/test-plugin-st',
icon: () => <PermIdentityOutlinedIcon />
})
.render();
70 changes: 70 additions & 0 deletions plugins/test-plugin-st/package.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
Original file line number Diff line number Diff line change
@@ -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(<ExampleComponent />);
expect(screen.getByText('Welcome to test-plugin-st!')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -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 (
<Page themeId="tool">
<Header
title="Welcome to test-plugin-st"
subtitle="Optional subtitle"
>
<HeaderLabel label="Owner" value="Team X" />
<HeaderLabel label="Lifecycle" value="Alpha" />
</Header>
<Content className={classes.body}>
<ContentHeader title="Plugin title">
<SupportButton>A description of your plugin goes here.</SupportButton>
</ContentHeader>
<Grid container spacing={3} direction="column">
<Grid item>
<InfoCard title="Information card">
<Typography variant="body1">
All content should be wrapped in a card like this.
</Typography>
</InfoCard>
</Grid>
<Grid item>
<ExampleFetchComponent />
</Grid>
</Grid>
</Content>
</Page>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ExampleComponent } from './ExampleComponent';
13 changes: 13 additions & 0 deletions plugins/test-plugin-st/src/components/ExampleComponentIcon.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<SidebarItem text="test-plugin-st" to="/test-plugin-st" icon={PermIdentityOutlinedIcon as IconComponent} />
);
};
Original file line number Diff line number Diff line change
@@ -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(<ExampleFetchComponent />);

// 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();
});
});
Original file line number Diff line number Diff line change
@@ -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: (
<Chip
style={chipStyle}
avatar={<Avatar alt={user.login} src={user.avatar_url} />}
label={user.login}
variant="outlined"
/>
),
repoUrl: user.html_url,
type: user.type,
};
});

return (
<Table
title="Github Users"
options={{ search: false, paging: true }}
columns={columns}
data={data}
/>
);
};

export const ExampleFetchComponent = () => {
const discoveryApi = useApi(discoveryApiRef);
const proxyURL = discoveryApi.getBaseUrl("proxy");

const { value, loading, error } = useAsync(async (): Promise<User[]> => {
const res = await fetch(`${await proxyURL}/github/users`);
const users = await res.json();
return users;
}, []);

if (loading) {
return <Progress />;
} else if (error) {
return <ResponseErrorPanel error={error} />;
}

return <DenseTable users={value || []} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { ExampleFetchComponent } from './ExampleFetchComponent';
14 changes: 14 additions & 0 deletions plugins/test-plugin-st/src/components/Router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from 'react';
import { Route, Routes } from 'react-router-dom';

import { ExampleComponent } from './ExampleComponent';

/**
*
* @public
*/
export const Router = () => (
<Routes>
<Route path="*" element={<ExampleComponent />} />
</Routes>
);
4 changes: 4 additions & 0 deletions plugins/test-plugin-st/src/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { Router } from './Router';
export * from './ExampleComponent';
export { ExampleComponentIcon } from './ExampleComponentIcon';

Loading
Loading