docs: migrate getting-started docs to new frontend system
Migrate four documentation pages to use the new frontend system as the primary content, moving the old frontend system instructions to --old files following the established convention: - configure-app-with-plugins: rewrite for auto-discovered plugins - customize-theme: rewrite for ThemeBlueprint/extension-based theming - enable-public-entry: make new frontend system the primary content - quickstart-app-plugin: remove manual sidebar wiring instructions Each main file gets an info banner linking to the --old variant, and each --old file gets a banner linking back to the current guide. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Made-with: Cursor
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
---
|
||||
id: configure-app-with-plugins--old
|
||||
title: Configuring App with plugins (Old Frontend System)
|
||||
description: Documentation on How Configuring App with plugins
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is for Backstage apps that still use the old frontend
|
||||
system. If your app uses the new frontend system, read the
|
||||
[current guide](./configure-app-with-plugins.md) instead.
|
||||
::::
|
||||
|
||||
Audience: Developers
|
||||
|
||||
:::note Note
|
||||
Backstage plugins are primarily written using [TypeScript](https://www.typescriptlang.org), [Node.js](https://nodejs.org) and [React](https://reactjs.org). Having an understanding of these technologies will be beneficial on your journey to customizing Backstage!
|
||||
:::
|
||||
|
||||
## Summary
|
||||
|
||||
Backstage plugins customize the app for your needs. There is a
|
||||
[plugin directory](https://backstage.io/plugins) with plugins for many common
|
||||
infrastructure needs - CI/CD, monitoring, auditing, and more.
|
||||
|
||||
## Adding existing plugins to your app
|
||||
|
||||
The following steps assume that you have
|
||||
[created a Backstage app](./index.md) and want to add an existing plugin
|
||||
to it.
|
||||
|
||||
You can find many wonderful plugins out there for Backstage, for example through the [Community Plugins Repository](https://github.com/backstage/community-plugins) and the [Backstage Plugin Directory](https://backstage.io/plugins).
|
||||
|
||||
Adding plugins to your Backstage app is generally a simple process, and ideally each plugin will come with its own documentation on how to install and configure it. In this example we will add the [Tech Radar plugin](https://github.com/backstage/community-plugins/tree/main/workspaces/tech-radar/plugins/tech-radar) to our Backstage app.
|
||||
|
||||
1. Add the plugin's npm package to the repo:
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage-community/plugin-tech-radar
|
||||
```
|
||||
|
||||
Note the plugin is added to the `app` package, rather than the root
|
||||
`package.json`. Backstage Apps are set up as monorepos with
|
||||
[Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/). Frontend UI Plugins are generally added to the `app` folder, while Backend Plugins are added to the `backend` folder. In the example above, the plugin is added to the `app` package because we are adding the frontend plugin.
|
||||
|
||||
2. Now, modify your app routes to include the Router component exported from the tech radar, for example:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
/* highlight-add-start */
|
||||
import { TechRadarPage } from '@backstage-community/plugin-tech-radar';
|
||||
/* highlight-add-end */
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
/* highlight-add-start */
|
||||
<Route
|
||||
path="/tech-radar"
|
||||
element={<TechRadarPage width={1500} height={800} />}
|
||||
/>
|
||||
/* highlight-add-end */
|
||||
</FlatRoutes>
|
||||
);
|
||||
```
|
||||
|
||||
This is just one example, and if you'd like to continue adding the Tech Radar plugin you can do so by going [here](https://github.com/backstage/community-plugins/tree/main/workspaces/tech-radar/plugins/tech-radar), keep in mind each Backstage instance may integrate content or
|
||||
cards to suit their needs on different pages, tabs, etc. In addition, while some
|
||||
plugins such as this example are designed to be used in a stand-alone fashion,
|
||||
others may be intended to annotate or support specific software catalog entities
|
||||
and would be added elsewhere in the app.
|
||||
|
||||
### Adding a plugin page to the Sidebar
|
||||
|
||||
In a standard Backstage app created with
|
||||
[@backstage/create-app](./index.md), the sidebar is managed inside
|
||||
`packages/app/src/components/Root/Root.tsx`. The file exports the entire
|
||||
`Sidebar` element of your app, which you can extend with additional entries by
|
||||
adding new `SidebarItem` elements.
|
||||
|
||||
For example, if you install the `api-docs` plugin, a matching `SidebarItem`
|
||||
could be something like this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
// Import icon from Material UI
|
||||
import ExtensionIcon from '@material-ui/icons/Extension';
|
||||
|
||||
// ... inside the AppSidebar component
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />;
|
||||
```
|
||||
|
||||
You can also use your own SVGs directly as icon components. Just make sure they
|
||||
are sized according to the Material UI's
|
||||
[SvgIcon](https://material-ui.com/api/svg-icon/) default of 24x24px, and wrap
|
||||
the SVG elements in a `SvgIcon` component like this:
|
||||
|
||||
```tsx
|
||||
import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon';
|
||||
|
||||
export const ExampleIcon = (props: SvgIconProps) => (
|
||||
<SvgIcon {...props}>
|
||||
<g>
|
||||
<path ... />
|
||||
<path ... />
|
||||
</g>
|
||||
</SvgIcon>
|
||||
);
|
||||
```
|
||||
|
||||
On mobile devices the `Sidebar` is displayed at the bottom of the screen. For
|
||||
customizing the experience you can group `SidebarItems` in a `SidebarGroup`
|
||||
(Example 1) or create a `SidebarGroup` with a link (Example 2). All
|
||||
`SidebarGroup`s are displayed in the bottom navigation with an icon.
|
||||
|
||||
```tsx
|
||||
// Example 1
|
||||
<SidebarGroup icon={<MenuIcon />} label="Menu">
|
||||
...
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
|
||||
...
|
||||
<SidebarGroup />
|
||||
```
|
||||
|
||||
```tsx
|
||||
// Example 2
|
||||
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
|
||||
...
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
|
||||
...
|
||||
<SidebarGroup />
|
||||
```
|
||||
|
||||
If no `SidebarGroup` is provided a default menu will display the `Sidebar`
|
||||
content.
|
||||
@@ -4,6 +4,13 @@ title: Configuring App with plugins
|
||||
description: Documentation on How Configuring App with plugins
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is written for the new frontend system, which is the default
|
||||
in new Backstage apps. If your Backstage app still uses the old frontend system,
|
||||
read the [old frontend system version of this guide](./configure-app-with-plugins--old.md)
|
||||
instead.
|
||||
::::
|
||||
|
||||
Audience: Developers
|
||||
|
||||
:::note Note
|
||||
@@ -26,100 +33,64 @@ You can find many wonderful plugins out there for Backstage, for example through
|
||||
|
||||
Adding plugins to your Backstage app is generally a simple process, and ideally each plugin will come with its own documentation on how to install and configure it. In this example we will add the [Tech Radar plugin](https://github.com/backstage/community-plugins/tree/main/workspaces/tech-radar/plugins/tech-radar) to our Backstage app.
|
||||
|
||||
1. Add the plugin's npm package to the repo:
|
||||
### 1. Install the plugin package
|
||||
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage-community/plugin-tech-radar
|
||||
```
|
||||
Add the plugin's npm package to the repo:
|
||||
|
||||
Note the plugin is added to the `app` package, rather than the root
|
||||
`package.json`. Backstage Apps are set up as monorepos with
|
||||
[Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/). Frontend UI Plugins are generally added to the `app` folder, while Backend Plugins are added to the `backend` folder. In the example above, the plugin is added to the `app` package because we are adding the frontend plugin.
|
||||
|
||||
2. Now, modify your app routes to include the Router component exported from the tech radar, for example:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
/* highlight-add-start */
|
||||
import { TechRadarPage } from '@backstage-community/plugin-tech-radar';
|
||||
/* highlight-add-end */
|
||||
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
/* highlight-add-start */
|
||||
<Route
|
||||
path="/tech-radar"
|
||||
element={<TechRadarPage width={1500} height={800} />}
|
||||
/>
|
||||
/* highlight-add-end */
|
||||
</FlatRoutes>
|
||||
);
|
||||
```
|
||||
|
||||
This is just one example, and if you'd like to continue adding the Tech Radar plugin you can do so by going [here](https://github.com/backstage/community-plugins/tree/main/workspaces/tech-radar/plugins/tech-radar), keep in mind each Backstage instance may integrate content or
|
||||
cards to suit their needs on different pages, tabs, etc. In addition, while some
|
||||
plugins such as this example are designed to be used in a stand-alone fashion,
|
||||
others may be intended to annotate or support specific software catalog entities
|
||||
and would be added elsewhere in the app.
|
||||
|
||||
### Adding a plugin page to the Sidebar
|
||||
|
||||
In a standard Backstage app created with
|
||||
[@backstage/create-app](./index.md), the sidebar is managed inside
|
||||
`packages/app/src/components/Root/Root.tsx`. The file exports the entire
|
||||
`Sidebar` element of your app, which you can extend with additional entries by
|
||||
adding new `SidebarItem` elements.
|
||||
|
||||
For example, if you install the `api-docs` plugin, a matching `SidebarItem`
|
||||
could be something like this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
// Import icon from Material UI
|
||||
import ExtensionIcon from '@material-ui/icons/Extension';
|
||||
|
||||
// ... inside the AppSidebar component
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />;
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage-community/plugin-tech-radar
|
||||
```
|
||||
|
||||
You can also use your own SVGs directly as icon components. Just make sure they
|
||||
are sized according to the Material UI's
|
||||
[SvgIcon](https://material-ui.com/api/svg-icon/) default of 24x24px, and wrap
|
||||
the SVG elements in a `SvgIcon` component like this:
|
||||
Note the plugin is added to the `app` package, rather than the root
|
||||
`package.json`. Backstage Apps are set up as monorepos with
|
||||
[Yarn workspaces](https://classic.yarnpkg.com/en/docs/workspaces/). Frontend UI plugins are generally added to the `app` folder, while backend plugins are added to the `backend` folder.
|
||||
|
||||
```tsx
|
||||
import SvgIcon, { SvgIconProps } from '@material-ui/core/SvgIcon';
|
||||
### 2. Verify the plugin is available
|
||||
|
||||
export const ExampleIcon = (props: SvgIconProps) => (
|
||||
<SvgIcon {...props}>
|
||||
<g>
|
||||
<path ... />
|
||||
<path ... />
|
||||
</g>
|
||||
</SvgIcon>
|
||||
);
|
||||
With the new frontend system, plugins are automatically discovered and installed in your app through [feature discovery](../frontend-system/building-apps/05-installing-plugins.md#feature-discovery). This is enabled by the default `app-config.yaml` setting:
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
app:
|
||||
packages: all
|
||||
```
|
||||
|
||||
On mobile devices the `Sidebar` is displayed at the bottom of the screen. For
|
||||
customizing the experience you can group `SidebarItems` in a `SidebarGroup`
|
||||
(Example 1) or create a `SidebarGroup` with a link (Example 2). All
|
||||
`SidebarGroup`s are displayed in the bottom navigation with an icon.
|
||||
Once the package is installed, the plugin is ready to use — no code changes needed! Start your app with `yarn start` and navigate to `/tech-radar` to see the plugin in action.
|
||||
|
||||
```tsx
|
||||
// Example 1
|
||||
<SidebarGroup icon={<MenuIcon />} label="Menu">
|
||||
...
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
|
||||
...
|
||||
<SidebarGroup />
|
||||
This is just one example, and if you'd like to continue adding the Tech Radar plugin you can do so by going [here](https://github.com/backstage/community-plugins/tree/main/workspaces/tech-radar/plugins/tech-radar). Keep in mind each Backstage instance may integrate content or cards to suit their needs on different pages, tabs, etc. In addition, while some plugins such as this example are designed to be used in a stand-alone fashion, others may be intended to annotate or support specific software catalog entities and would be added elsewhere in the app.
|
||||
|
||||
### 3. Optional: Configure the plugin
|
||||
|
||||
Plugins can be further customized through configuration in your `app-config.yaml`. For example, you can configure extension settings under the `app.extensions` section:
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
app:
|
||||
extensions:
|
||||
- page:tech-radar:
|
||||
config:
|
||||
path: /tech-radar
|
||||
```
|
||||
|
||||
```tsx
|
||||
// Example 2
|
||||
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
|
||||
...
|
||||
<SidebarItem icon={ExtensionIcon} to="api-docs" text="APIs" />
|
||||
...
|
||||
<SidebarGroup />
|
||||
For details on what configuration options are available, refer to the plugin's own documentation or the [Configuring Extensions](../frontend-system/building-apps/02-configuring-extensions.md) guide.
|
||||
|
||||
### 4. Optional: Manually install a plugin
|
||||
|
||||
If you need more control over plugin installation, or if [feature discovery](../frontend-system/building-apps/05-installing-plugins.md#feature-discovery) is not enabled, you can install plugins manually by importing them and passing them to `createApp`:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import techRadarPlugin from '@backstage-community/plugin-tech-radar/alpha';
|
||||
|
||||
const app = createApp({
|
||||
features: [techRadarPlugin],
|
||||
});
|
||||
|
||||
export default app.createRoot();
|
||||
```
|
||||
|
||||
If no `SidebarGroup` is provided a default menu will display the `Sidebar`
|
||||
content.
|
||||
For more details and alternative installation methods, see [Installing Plugins](../frontend-system/building-apps/05-installing-plugins.md).
|
||||
|
||||
## How the sidebar works
|
||||
|
||||
In the new frontend system, plugins that provide a page automatically register a navigation item in the sidebar. You don't need to manually add `SidebarItem` elements for most plugins.
|
||||
|
||||
If you want to customize sidebar behavior — for example, reordering items, grouping them, or adding custom entries — you can override the built-in `app/nav` extension. See the [migration guide's sidebar section](../frontend-system/building-apps/08-migrating.md#app-root-sidebar) for details on how to create a custom sidebar layout.
|
||||
|
||||
@@ -0,0 +1,619 @@
|
||||
---
|
||||
id: custom-theme--old
|
||||
title: 005 - Customize your App's theme (Old Frontend System)
|
||||
description: Documentation on customizing the look and feel of your Backstage app.
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is for Backstage apps that still use the old frontend
|
||||
system. If your app uses the new frontend system, read the
|
||||
[current guide](./customize-theme.md) instead.
|
||||
::::
|
||||
|
||||
Backstage ships with a default theme with a light and dark mode variant. The themes are provided as a part of the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package, which also includes utilities for customizing the default theme, or creating completely new themes.
|
||||
|
||||
## Creating a Custom Theme
|
||||
|
||||
The easiest way to create a new theme is to use the `createUnifiedTheme` function exported by the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package. You can use it to override some basic parameters of the default theme such as the color palette and font.
|
||||
|
||||
For example, you can create a new theme based on the default light theme like this:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
}),
|
||||
fontFamily: 'Comic Sans MS',
|
||||
defaultPageTheme: 'home',
|
||||
});
|
||||
```
|
||||
|
||||
:::note Note
|
||||
|
||||
we recommend creating a `theme` folder in `packages/app/src` to place your theme file to keep things nicely organized.
|
||||
|
||||
:::
|
||||
|
||||
You can also create a theme from scratch that matches the `BackstageTheme` type exported by [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme). See the
|
||||
[Material UI docs on theming](https://material-ui.com/customization/theming/) for more information about how that can be done.
|
||||
|
||||
## Using your Custom Theme
|
||||
|
||||
To add a custom theme to your Backstage app, you pass it as configuration to `createApp`.
|
||||
|
||||
For example, adding the theme that we created in the previous section can be done like this:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import { UnifiedThemeProvider} from '@backstage/theme';
|
||||
import { myTheme } from './themes/myTheme';
|
||||
|
||||
const app = createApp({
|
||||
apis: ...,
|
||||
plugins: ...,
|
||||
themes: [{
|
||||
id: 'my-theme',
|
||||
title: 'My Custom Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (
|
||||
<UnifiedThemeProvider theme={myTheme} children={children} />
|
||||
),
|
||||
}]
|
||||
})
|
||||
```
|
||||
|
||||
Note that your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.dark` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
|
||||
|
||||
## Example of a custom theme
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
genPageTheme,
|
||||
palettes,
|
||||
shapes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: {
|
||||
...palettes.light,
|
||||
primary: {
|
||||
main: '#343b58',
|
||||
},
|
||||
secondary: {
|
||||
main: '#565a6e',
|
||||
},
|
||||
error: {
|
||||
main: '#8c4351',
|
||||
},
|
||||
warning: {
|
||||
main: '#8f5e15',
|
||||
},
|
||||
info: {
|
||||
main: '#34548a',
|
||||
},
|
||||
success: {
|
||||
main: '#485e30',
|
||||
},
|
||||
background: {
|
||||
default: '#d5d6db',
|
||||
paper: '#d5d6db',
|
||||
},
|
||||
banner: {
|
||||
info: '#34548a',
|
||||
error: '#8c4351',
|
||||
text: '#343b58',
|
||||
link: '#565a6e',
|
||||
},
|
||||
errorBackground: '#8c4351',
|
||||
warningBackground: '#8f5e15',
|
||||
infoBackground: '#343b58',
|
||||
navigation: {
|
||||
background: '#343b58',
|
||||
indicator: '#8f5e15',
|
||||
color: '#d5d6db',
|
||||
selectedColor: '#ffffff',
|
||||
},
|
||||
},
|
||||
}),
|
||||
defaultPageTheme: 'home',
|
||||
fontFamily: 'Comic Sans MS',
|
||||
/* below drives the header colors */
|
||||
pageTheme: {
|
||||
home: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
documentation: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave2,
|
||||
}),
|
||||
tool: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.round }),
|
||||
service: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
website: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
library: genPageTheme({
|
||||
colors: ['#8c4351', '#343b58'],
|
||||
shape: shapes.wave,
|
||||
}),
|
||||
other: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
app: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
apis: genPageTheme({ colors: ['#8c4351', '#343b58'], shape: shapes.wave }),
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For a more complete example of a custom theme including Backstage and Material UI component overrides, see the [Aperture theme](https://github.com/backstage/demo/blob/master/packages/app/src/theme/aperture.ts) from the [Backstage demo site](https://demo.backstage.io).
|
||||
|
||||
## Custom Typography
|
||||
|
||||
When creating a custom theme you can also customize various aspects of the default typography, here's an example using simplified theme:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
typography: {
|
||||
htmlFontSize: 16,
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
h1: {
|
||||
fontSize: 54,
|
||||
fontWeight: 700,
|
||||
marginBottom: 10,
|
||||
},
|
||||
h2: {
|
||||
fontSize: 40,
|
||||
fontWeight: 700,
|
||||
marginBottom: 8,
|
||||
},
|
||||
h3: {
|
||||
fontSize: 32,
|
||||
fontWeight: 700,
|
||||
marginBottom: 6,
|
||||
},
|
||||
h4: {
|
||||
fontWeight: 700,
|
||||
fontSize: 28,
|
||||
marginBottom: 6,
|
||||
},
|
||||
h5: {
|
||||
fontWeight: 700,
|
||||
fontSize: 24,
|
||||
marginBottom: 4,
|
||||
},
|
||||
h6: {
|
||||
fontWeight: 700,
|
||||
fontSize: 20,
|
||||
marginBottom: 2,
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
If you wanted to only override a sub-set of the typography setting, for example just `h1` then you would do this:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
defaultTypography,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
typography: {
|
||||
...defaultTypography,
|
||||
htmlFontSize: 16,
|
||||
fontFamily: 'Roboto, sans-serif',
|
||||
h1: {
|
||||
fontSize: 72,
|
||||
fontWeight: 700,
|
||||
marginBottom: 10,
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Custom Fonts
|
||||
|
||||
To add custom fonts, you first need to store the font so that it can be imported. We suggest creating the `assets/fonts` directory in your front-end application `src` folder.
|
||||
|
||||
You can then declare the font style following the `@font-face` syntax from [Material UI Typography](https://mui.com/material-ui/customization/typography/).
|
||||
|
||||
After that you can then utilize the `styleOverrides` of `MuiCssBaseline` under components to add a font to the `@font-face` array.
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2';
|
||||
|
||||
const myCustomFont = {
|
||||
fontFamily: 'My-Custom-Font',
|
||||
fontStyle: 'normal',
|
||||
fontDisplay: 'swap',
|
||||
fontWeight: 300,
|
||||
src: `
|
||||
local('My-Custom-Font'),
|
||||
url(${MyCustomFont}) format('woff2'),
|
||||
`,
|
||||
};
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
fontFamily: 'My-Custom-Font',
|
||||
palette: palettes.light,
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
'@font-face': [myCustomFont],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
If you want to utilize different or multiple fonts, then you can set the top level `fontFamily` to what you want for your body, and then override `fontFamily` in `typography` to control fonts for various headings.
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import MyCustomFont from '../assets/fonts/My-Custom-Font.woff2';
|
||||
import myAwesomeFont from '../assets/fonts/My-Awesome-Font.woff2';
|
||||
|
||||
const myCustomFont = {
|
||||
fontFamily: 'My-Custom-Font',
|
||||
fontStyle: 'normal',
|
||||
fontDisplay: 'swap',
|
||||
fontWeight: 300,
|
||||
src: `
|
||||
local('My-Custom-Font'),
|
||||
url(${MyCustomFont}) format('woff2'),
|
||||
`,
|
||||
};
|
||||
|
||||
const myAwesomeFont = {
|
||||
fontFamily: 'My-Awesome-Font',
|
||||
fontStyle: 'normal',
|
||||
fontDisplay: 'swap',
|
||||
fontWeight: 300,
|
||||
src: `
|
||||
local('My-Awesome-Font'),
|
||||
url(${myAwesomeFont}) format('woff2'),
|
||||
`,
|
||||
};
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
fontFamily: 'My-Custom-Font',
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
'@font-face': [myCustomFont, myAwesomeFont],
|
||||
},
|
||||
},
|
||||
},
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
typography: {
|
||||
...defaultTypography,
|
||||
htmlFontSize: 16,
|
||||
fontFamily: 'My-Custom-Font',
|
||||
h1: {
|
||||
fontSize: 72,
|
||||
fontWeight: 700,
|
||||
marginBottom: 10,
|
||||
fontFamily: 'My-Awesome-Font',
|
||||
},
|
||||
},
|
||||
defaultPageTheme: 'home',
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
## Overriding Backstage and Material UI components styles
|
||||
|
||||
When creating a custom theme you would be applying different values to component's CSS rules that use the theme object. For example, a Backstage component's styles might look like this:
|
||||
|
||||
```tsx
|
||||
const useStyles = makeStyles<BackstageTheme>(
|
||||
theme => ({
|
||||
header: {
|
||||
padding: theme.spacing(3),
|
||||
boxShadow: '0 0 8px 3px rgba(20, 20, 20, 0.3)',
|
||||
backgroundImage: theme.page.backgroundImage,
|
||||
},
|
||||
}),
|
||||
{ name: 'BackstageHeader' },
|
||||
);
|
||||
```
|
||||
|
||||
Notice how the `padding` is getting its value from `theme.spacing`, that means that setting a value for spacing in your custom theme would affect this component padding property and the same goes for `backgroundImage` which uses `theme.page.backgroundImage`. However, the `boxShadow` property doesn't reference any value from the theme, that means that creating a custom theme wouldn't be enough to alter the `box-shadow` property or to add css rules that aren't already defined like a margin. For these cases you should also create an override.
|
||||
|
||||
Here's how you would do that:
|
||||
|
||||
```ts title="packages/app/src/theme/myTheme.ts"
|
||||
import {
|
||||
createBaseThemeOptions,
|
||||
createUnifiedTheme,
|
||||
palettes,
|
||||
} from '@backstage/theme';
|
||||
|
||||
export const myTheme = createUnifiedTheme({
|
||||
...createBaseThemeOptions({
|
||||
palette: palettes.light,
|
||||
}),
|
||||
fontFamily: 'Comic Sans MS',
|
||||
defaultPageTheme: 'home',
|
||||
components: {
|
||||
BackstageHeader: {
|
||||
styleOverrides: {
|
||||
header: ({ theme }) => ({
|
||||
width: 'auto',
|
||||
margin: '20px',
|
||||
boxShadow: 'none',
|
||||
borderBottom: `4px solid ${theme.palette.primary.main}`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Custom Logo
|
||||
|
||||
In addition to a custom theme, you can also customize the logo displayed at the far top left of the site.
|
||||
|
||||
In your frontend app, locate `src/components/Root/` folder. You'll find two components:
|
||||
|
||||
- `LogoFull.tsx` - A larger logo used when the Sidebar navigation is opened.
|
||||
- `LogoIcon.tsx` - A smaller logo used when the Sidebar navigation is closed.
|
||||
|
||||
To replace the images, you can simply replace the relevant code in those components with raw SVG definitions.
|
||||
|
||||
You can also use another web image format such as PNG by importing it. To do this, place your new image into a new subdirectory such as `src/components/Root/logo/my-company-logo.png`, and then add this code:
|
||||
|
||||
```tsx
|
||||
import MyCustomLogoFull from './logo/my-company-logo.png';
|
||||
|
||||
const LogoFull = () => {
|
||||
return <img src={MyCustomLogoFull} />;
|
||||
};
|
||||
```
|
||||
|
||||
## Icons
|
||||
|
||||
So far you've seen how to create your own theme and add your own logo, in the following sections you'll be shown how to override the existing icons and how to add more icons
|
||||
|
||||
### Custom Icons
|
||||
|
||||
You can also customize the Project's _default_ icons.
|
||||
|
||||
You can change the following [icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx).
|
||||
|
||||
#### Requirements
|
||||
|
||||
- Files in `.svg` format
|
||||
- React components created for the icons
|
||||
|
||||
#### Create React Component
|
||||
|
||||
In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `CustomIcons.tsx` file.
|
||||
|
||||
```tsx title="customIcons.tsx"
|
||||
import { SvgIcon, SvgIconProps } from '@material-ui/core';
|
||||
|
||||
export const ExampleIcon = (props: SvgIconProps) => (
|
||||
<SvgIcon {...props} viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
width="1em"
|
||||
height="1em"
|
||||
display="inline-block"
|
||||
d="M11.6335 10.8398C11.6335 11.6563 12.065 12.9922 13.0863 12.9922C14.1075 12.9922 14.539 11.6563 14.539 10.8398C14.539 10.0234 14.1075 8.6875 13.0863 8.6875C12.065 8.6875 11.6335 10.0234 11.6335 10.8398V10.8398ZM2.38419e-07 8.86719C2.38419e-07 10.1133 0.126667 11.4336 0.692709 12.5781C2.19292 15.5703 6.3175 15.5 9.27042 15.5C12.2708 15.5 16.6408 15.6055 18.2004 12.5781C18.7783 11.4453 19 10.1133 19 8.86719C19 7.23047 18.4498 5.68359 17.3573 4.42969C17.5631 3.8125 17.6621 3.16406 17.6621 2.52344C17.6621 1.68359 17.4681 1.26172 17.0842 0.5C15.291 0.5 14.1431 0.851562 12.7775 1.90625C11.6296 1.63672 10.45 1.51562 9.26646 1.51562C8.19771 1.51562 7.12104 1.62891 6.08396 1.875C4.73813 0.832031 3.59021 0.5 1.81687 0.5C1.42896 1.26172 1.23896 1.68359 1.23896 2.52344C1.23896 3.16406 1.34188 3.80078 1.54375 4.40625C0.455209 5.67188 2.38419e-07 7.23047 2.38419e-07 8.86719V8.86719ZM2.54521 10.8398C2.54521 9.125 3.60208 7.61328 5.45458 7.61328C6.20271 7.61328 6.91917 7.74609 7.67125 7.84766C8.26104 7.9375 8.85083 7.97266 9.45646 7.97266C10.0581 7.97266 10.6479 7.9375 11.2417 7.84766C11.9819 7.74609 12.7063 7.61328 13.4583 7.61328C15.3108 7.61328 16.3677 9.125 16.3677 10.8398C16.3677 14.2695 13.1852 14.7969 10.4144 14.7969H8.50646C5.72375 14.7969 2.54521 14.2734 2.54521 10.8398V10.8398ZM5.81479 8.6875C6.83604 8.6875 7.2675 10.0234 7.2675 10.8398C7.2675 11.6563 6.83604 12.9922 5.81479 12.9922C4.79354 12.9922 4.36208 11.6563 4.36208 10.8398C4.36208 10.0234 4.79354 8.6875 5.81479 8.6875Z"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
```
|
||||
|
||||
#### Using the custom icon
|
||||
|
||||
Supply your custom icon in `packages/app/src/App.tsx`
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
/* highlight-add-next-line */
|
||||
import { ExampleIcon } from './assets/customIcons'
|
||||
|
||||
|
||||
const app = createApp({
|
||||
apis,
|
||||
components: {
|
||||
{/* ... */}
|
||||
},
|
||||
themes: [
|
||||
{/* ... */}
|
||||
],
|
||||
/* highlight-add-start */
|
||||
icons: {
|
||||
github: ExampleIcon,
|
||||
},
|
||||
/* highlight-add-end */
|
||||
bindRoutes({ bind }) {
|
||||
{/* ... */}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Adding Icons
|
||||
|
||||
You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that:
|
||||
|
||||
1. First you will want to open your `App.tsx` in `/packages/app/src`
|
||||
2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';`
|
||||
3. Next you want to add the icon like this to your `createApp`:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
const app = createApp({
|
||||
apis: ...,
|
||||
plugins: ...,
|
||||
/* highlight-add-start */
|
||||
icons: {
|
||||
alert: AlarmIcon,
|
||||
},
|
||||
/* highlight-add-end */
|
||||
themes: ...,
|
||||
components: ...,
|
||||
});
|
||||
```
|
||||
|
||||
4. Now we can reference `alert` for our icon in our entity links like this:
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
description: Artist Lookup
|
||||
links:
|
||||
- url: https://example.com/alert
|
||||
title: Alerts
|
||||
icon: alert
|
||||
```
|
||||
|
||||
And this is the result:
|
||||
|
||||

|
||||
|
||||
Another way you can use these icons is from the `AppContext` like this:
|
||||
|
||||
```ts
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
|
||||
const app = useApp();
|
||||
const alertIcon = app.getSystemIcon('alert');
|
||||
```
|
||||
|
||||
You might want to use this method if you have an icon you want to use in several locations.
|
||||
|
||||
:::note Note
|
||||
|
||||
If the icon is not available as one of the default icons or one you've added then it will fall back to Material UI's `LanguageIcon`
|
||||
|
||||
:::
|
||||
|
||||
## Custom Sidebar
|
||||
|
||||
As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar.
|
||||
|
||||
### Sidebar Sub-menu
|
||||
|
||||
For this example we'll show you how you can expand the sidebar with a sub-menu:
|
||||
|
||||
1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives
|
||||
2. Then we want to add the following import for `useApp`:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
```
|
||||
|
||||
3. Then update the `@backstage/core-components` import like this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import {
|
||||
Sidebar,
|
||||
sidebarConfig,
|
||||
SidebarDivider,
|
||||
SidebarGroup,
|
||||
SidebarItem,
|
||||
SidebarPage,
|
||||
SidebarScrollWrapper,
|
||||
SidebarSpace,
|
||||
useSidebarOpenState,
|
||||
Link,
|
||||
/* highlight-add-start */
|
||||
GroupIcon,
|
||||
SidebarSubmenu,
|
||||
SidebarSubmenuItem,
|
||||
/* highlight-add-end */
|
||||
} from '@backstage/core-components';
|
||||
```
|
||||
|
||||
4. Finally replace `<SidebarItem icon={HomeIcon} to="catalog" text="Home" />` with this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
<SidebarItem icon={HomeIcon} to="catalog" text="Home">
|
||||
<SidebarSubmenu title="Catalog">
|
||||
<SidebarSubmenuItem
|
||||
title="Domains"
|
||||
to="catalog?filters[kind]=domain"
|
||||
icon={useApp().getSystemIcon('kind:domain')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Systems"
|
||||
to="catalog?filters[kind]=system"
|
||||
icon={useApp().getSystemIcon('kind:system')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Components"
|
||||
to="catalog?filters[kind]=component"
|
||||
icon={useApp().getSystemIcon('kind:component')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="APIs"
|
||||
to="catalog?filters[kind]=api"
|
||||
icon={useApp().getSystemIcon('kind:api')}
|
||||
/>
|
||||
<SidebarDivider />
|
||||
<SidebarSubmenuItem
|
||||
title="Resources"
|
||||
to="catalog?filters[kind]=resource"
|
||||
icon={useApp().getSystemIcon('kind:resource')}
|
||||
/>
|
||||
<SidebarDivider />
|
||||
<SidebarSubmenuItem
|
||||
title="Groups"
|
||||
to="catalog?filters[kind]=group"
|
||||
icon={useApp().getSystemIcon('kind:group')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Users"
|
||||
to="catalog?filters[kind]=user"
|
||||
icon={useApp().getSystemIcon('kind:user')}
|
||||
/>
|
||||
</SidebarSubmenu>
|
||||
</SidebarItem>
|
||||
```
|
||||
|
||||
When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this:
|
||||
|
||||

|
||||
|
||||
You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar)
|
||||
|
||||
## Custom Homepage
|
||||
|
||||
In addition to a custom theme, a custom logo, you can also customize the
|
||||
homepage of your app. Read the full guide on the [next page](../../getting-started/homepage.md).
|
||||
|
||||
## Migrating to Material UI v5
|
||||
|
||||
We now support Material UI v5 in Backstage. Check out our [migration guide](../../tutorials/migrate-to-mui5.md) to get started.
|
||||
@@ -4,6 +4,13 @@ title: 005 - Customize your App's theme
|
||||
description: Documentation on customizing the look and feel of your Backstage app.
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is written for the new frontend system, which is the default
|
||||
in new Backstage apps. If your Backstage app still uses the old frontend system,
|
||||
read the [old frontend system version of this guide](./customize-theme--old.md)
|
||||
instead.
|
||||
::::
|
||||
|
||||
Backstage ships with a default theme with a light and dark mode variant. The themes are provided as a part of the [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme) package, which also includes utilities for customizing the default theme, or creating completely new themes.
|
||||
|
||||
## Creating a Custom Theme
|
||||
@@ -30,7 +37,7 @@ export const myTheme = createUnifiedTheme({
|
||||
|
||||
:::note Note
|
||||
|
||||
we recommend creating a `theme` folder in `packages/app/src` to place your theme file to keep things nicely organized.
|
||||
We recommend creating a `theme` folder in `packages/app/src` to place your theme file to keep things nicely organized.
|
||||
|
||||
:::
|
||||
|
||||
@@ -39,34 +46,59 @@ You can also create a theme from scratch that matches the `BackstageTheme` type
|
||||
|
||||
## Using your Custom Theme
|
||||
|
||||
To add a custom theme to your Backstage app, you pass it as configuration to `createApp`.
|
||||
In the new frontend system, themes are installed as extensions using `ThemeBlueprint` from `@backstage/plugin-app-react`. The theme extension is then bundled into a frontend module and passed to `createApp`.
|
||||
|
||||
For example, adding the theme that we created in the previous section can be done like this:
|
||||
First, install the required packages:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { ThemeProvider } from '@material-ui/core/styles';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import { UnifiedThemeProvider} from '@backstage/theme';
|
||||
import { myTheme } from './themes/myTheme';
|
||||
|
||||
const app = createApp({
|
||||
apis: ...,
|
||||
plugins: ...,
|
||||
themes: [{
|
||||
id: 'my-theme',
|
||||
title: 'My Custom Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (
|
||||
<UnifiedThemeProvider theme={myTheme} children={children} />
|
||||
),
|
||||
}]
|
||||
})
|
||||
```bash title="From your Backstage root directory"
|
||||
yarn --cwd packages/app add @backstage/frontend-plugin-api @backstage/plugin-app-react
|
||||
```
|
||||
|
||||
Note that your list of custom themes overrides the default themes. If you still want to use the default themes, they are exported as `themes.light` and `themes.dark` from [`@backstage/theme`](https://www.npmjs.com/package/@backstage/theme).
|
||||
Then create the theme extension and install it in your app:
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { ThemeBlueprint } from '@backstage/plugin-app-react';
|
||||
import { UnifiedThemeProvider } from '@backstage/theme';
|
||||
import LightIcon from '@material-ui/icons/WbSunny';
|
||||
import { myTheme } from './theme/myTheme';
|
||||
|
||||
const myThemeExtension = ThemeBlueprint.make({
|
||||
name: 'my-theme',
|
||||
params: {
|
||||
theme: {
|
||||
id: 'my-theme',
|
||||
title: 'My Custom Theme',
|
||||
variant: 'light',
|
||||
icon: <LightIcon />,
|
||||
Provider: ({ children }) => (
|
||||
<UnifiedThemeProvider theme={myTheme} children={children} />
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
features: [
|
||||
createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [myThemeExtension],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export default app.createRoot();
|
||||
```
|
||||
|
||||
Your custom theme extension will be added alongside the built-in light and dark themes. If you want your custom theme to _replace_ the default themes, you can disable them in `app-config.yaml`:
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
app:
|
||||
extensions:
|
||||
- theme:app/light: false
|
||||
- theme:app/dark: false
|
||||
```
|
||||
|
||||
## Example of a custom theme
|
||||
|
||||
@@ -396,117 +428,85 @@ const LogoFull = () => {
|
||||
|
||||
## Icons
|
||||
|
||||
So far you've seen how to create your own theme and add your own logo, in the following sections you'll be shown how to override the existing icons and how to add more icons
|
||||
So far you've seen how to create your own theme and add your own logo. In the following sections you'll be shown how to override the existing icons and how to add more icons.
|
||||
|
||||
### Custom Icons
|
||||
|
||||
You can also customize the Project's _default_ icons.
|
||||
|
||||
You can change the following [icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx).
|
||||
|
||||
#### Requirements
|
||||
|
||||
- Files in `.svg` format
|
||||
- React components created for the icons
|
||||
|
||||
#### Create React Component
|
||||
|
||||
In your front-end application, locate the `src` folder. We suggest creating the `assets/icons` directory and `CustomIcons.tsx` file.
|
||||
|
||||
```tsx title="customIcons.tsx"
|
||||
import { SvgIcon, SvgIconProps } from '@material-ui/core';
|
||||
|
||||
export const ExampleIcon = (props: SvgIconProps) => (
|
||||
<SvgIcon {...props} viewBox="0 0 24 24">
|
||||
<path
|
||||
fill="currentColor"
|
||||
width="1em"
|
||||
height="1em"
|
||||
display="inline-block"
|
||||
d="M11.6335 10.8398C11.6335 11.6563 12.065 12.9922 13.0863 12.9922C14.1075 12.9922 14.539 11.6563 14.539 10.8398C14.539 10.0234 14.1075 8.6875 13.0863 8.6875C12.065 8.6875 11.6335 10.0234 11.6335 10.8398V10.8398ZM2.38419e-07 8.86719C2.38419e-07 10.1133 0.126667 11.4336 0.692709 12.5781C2.19292 15.5703 6.3175 15.5 9.27042 15.5C12.2708 15.5 16.6408 15.6055 18.2004 12.5781C18.7783 11.4453 19 10.1133 19 8.86719C19 7.23047 18.4498 5.68359 17.3573 4.42969C17.5631 3.8125 17.6621 3.16406 17.6621 2.52344C17.6621 1.68359 17.4681 1.26172 17.0842 0.5C15.291 0.5 14.1431 0.851562 12.7775 1.90625C11.6296 1.63672 10.45 1.51562 9.26646 1.51562C8.19771 1.51562 7.12104 1.62891 6.08396 1.875C4.73813 0.832031 3.59021 0.5 1.81687 0.5C1.42896 1.26172 1.23896 1.68359 1.23896 2.52344C1.23896 3.16406 1.34188 3.80078 1.54375 4.40625C0.455209 5.67188 2.38419e-07 7.23047 2.38419e-07 8.86719V8.86719ZM2.54521 10.8398C2.54521 9.125 3.60208 7.61328 5.45458 7.61328C6.20271 7.61328 6.91917 7.74609 7.67125 7.84766C8.26104 7.9375 8.85083 7.97266 9.45646 7.97266C10.0581 7.97266 10.6479 7.9375 11.2417 7.84766C11.9819 7.74609 12.7063 7.61328 13.4583 7.61328C15.3108 7.61328 16.3677 9.125 16.3677 10.8398C16.3677 14.2695 13.1852 14.7969 10.4144 14.7969H8.50646C5.72375 14.7969 2.54521 14.2734 2.54521 10.8398V10.8398ZM5.81479 8.6875C6.83604 8.6875 7.2675 10.0234 7.2675 10.8398C7.2675 11.6563 6.83604 12.9922 5.81479 12.9922C4.79354 12.9922 4.36208 11.6563 4.36208 10.8398C4.36208 10.0234 4.79354 8.6875 5.81479 8.6875Z"
|
||||
/>
|
||||
</SvgIcon>
|
||||
);
|
||||
```
|
||||
|
||||
#### Using the custom icon
|
||||
|
||||
Supply your custom icon in `packages/app/src/App.tsx`
|
||||
You can customize the app's default icons using `IconBundleBlueprint` from `@backstage/plugin-app-react`. This creates an extension that overrides built-in icons.
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
/* highlight-add-next-line */
|
||||
import { ExampleIcon } from './assets/customIcons'
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import { IconBundleBlueprint } from '@backstage/plugin-app-react';
|
||||
import { ExampleIcon } from './assets/customIcons';
|
||||
|
||||
const customIconBundle = IconBundleBlueprint.make({
|
||||
name: 'custom-icons',
|
||||
params: {
|
||||
icons: {
|
||||
github: ExampleIcon,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const app = createApp({
|
||||
apis,
|
||||
components: {
|
||||
{/* ... */}
|
||||
},
|
||||
themes: [
|
||||
{/* ... */}
|
||||
features: [
|
||||
createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [customIconBundle],
|
||||
}),
|
||||
],
|
||||
/* highlight-add-start */
|
||||
icons: {
|
||||
github: ExampleIcon,
|
||||
},
|
||||
/* highlight-add-end */
|
||||
bindRoutes({ bind }) {
|
||||
{/* ... */}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
export default app.createRoot();
|
||||
```
|
||||
|
||||
### Adding Icons
|
||||
|
||||
You can add more icons, if the [default icons](https://github.com/backstage/backstage/blob/master/packages/app-defaults/src/defaults/icons.tsx) do not fit your needs, so that they can be used in other places like for Links in your entities. For this example we'll be using icons from[Material UI](https://v4.mui.com/components/material-icons/) and specifically the `AlarmIcon`. Here's how to do that:
|
||||
You can register additional icons so that they can be used in other places like entity links. For example, to add an `alert` icon:
|
||||
|
||||
1. First you will want to open your `App.tsx` in `/packages/app/src`
|
||||
2. Then you want to import your icon, add this to the rest of your imports: `import AlarmIcon from '@material-ui/icons/Alarm';`
|
||||
3. Next you want to add the icon like this to your `createApp`:
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
import AlarmIcon from '@material-ui/icons/Alarm';
|
||||
import { IconBundleBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
```tsx title="packages/app/src/App.tsx"
|
||||
const app = createApp({
|
||||
apis: ...,
|
||||
plugins: ...,
|
||||
/* highlight-add-start */
|
||||
icons: {
|
||||
alert: AlarmIcon,
|
||||
},
|
||||
/* highlight-add-end */
|
||||
themes: ...,
|
||||
components: ...,
|
||||
});
|
||||
```
|
||||
const extraIcons = IconBundleBlueprint.make({
|
||||
name: 'extra-icons',
|
||||
params: {
|
||||
icons: {
|
||||
alert: AlarmIcon,
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
4. Now we can reference `alert` for our icon in our entity links like this:
|
||||
You can then reference `alert` for your icon in entity links like this:
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
description: Artist Lookup
|
||||
links:
|
||||
- url: https://example.com/alert
|
||||
title: Alerts
|
||||
icon: alert
|
||||
```
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
description: Artist Lookup
|
||||
links:
|
||||
- url: https://example.com/alert
|
||||
title: Alerts
|
||||
icon: alert
|
||||
```
|
||||
|
||||
And this is the result:
|
||||
And this is the result:
|
||||
|
||||

|
||||

|
||||
|
||||
Another way you can use these icons is from the `AppContext` like this:
|
||||
Another way you can use these icons is from the `AppContext` like this:
|
||||
|
||||
```ts
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
```ts
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
|
||||
const app = useApp();
|
||||
const alertIcon = app.getSystemIcon('alert');
|
||||
```
|
||||
const app = useApp();
|
||||
const alertIcon = app.getSystemIcon('alert');
|
||||
```
|
||||
|
||||
You might want to use this method if you have an icon you want to use in several locations.
|
||||
You might want to use this method if you have an icon you want to use in several locations.
|
||||
|
||||
:::note Note
|
||||
|
||||
@@ -516,92 +516,7 @@ If the icon is not available as one of the default icons or one you've added the
|
||||
|
||||
## Custom Sidebar
|
||||
|
||||
As you've seen there are many ways that you can customize your Backstage app. The following section will show you how you can customize the sidebar.
|
||||
|
||||
### Sidebar Sub-menu
|
||||
|
||||
For this example we'll show you how you can expand the sidebar with a sub-menu:
|
||||
|
||||
1. Open the `Root.tsx` file located in `packages/app/src/components/Root` as this is where the sidebar code lives
|
||||
2. Then we want to add the following import for `useApp`:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
```
|
||||
|
||||
3. Then update the `@backstage/core-components` import like this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
import {
|
||||
Sidebar,
|
||||
sidebarConfig,
|
||||
SidebarDivider,
|
||||
SidebarGroup,
|
||||
SidebarItem,
|
||||
SidebarPage,
|
||||
SidebarScrollWrapper,
|
||||
SidebarSpace,
|
||||
useSidebarOpenState,
|
||||
Link,
|
||||
/* highlight-add-start */
|
||||
GroupIcon,
|
||||
SidebarSubmenu,
|
||||
SidebarSubmenuItem,
|
||||
/* highlight-add-end */
|
||||
} from '@backstage/core-components';
|
||||
```
|
||||
|
||||
4. Finally replace `<SidebarItem icon={HomeIcon} to="catalog" text="Home" />` with this:
|
||||
|
||||
```tsx title="packages/app/src/components/Root/Root.tsx"
|
||||
<SidebarItem icon={HomeIcon} to="catalog" text="Home">
|
||||
<SidebarSubmenu title="Catalog">
|
||||
<SidebarSubmenuItem
|
||||
title="Domains"
|
||||
to="catalog?filters[kind]=domain"
|
||||
icon={useApp().getSystemIcon('kind:domain')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Systems"
|
||||
to="catalog?filters[kind]=system"
|
||||
icon={useApp().getSystemIcon('kind:system')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Components"
|
||||
to="catalog?filters[kind]=component"
|
||||
icon={useApp().getSystemIcon('kind:component')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="APIs"
|
||||
to="catalog?filters[kind]=api"
|
||||
icon={useApp().getSystemIcon('kind:api')}
|
||||
/>
|
||||
<SidebarDivider />
|
||||
<SidebarSubmenuItem
|
||||
title="Resources"
|
||||
to="catalog?filters[kind]=resource"
|
||||
icon={useApp().getSystemIcon('kind:resource')}
|
||||
/>
|
||||
<SidebarDivider />
|
||||
<SidebarSubmenuItem
|
||||
title="Groups"
|
||||
to="catalog?filters[kind]=group"
|
||||
icon={useApp().getSystemIcon('kind:group')}
|
||||
/>
|
||||
<SidebarSubmenuItem
|
||||
title="Users"
|
||||
to="catalog?filters[kind]=user"
|
||||
icon={useApp().getSystemIcon('kind:user')}
|
||||
/>
|
||||
</SidebarSubmenu>
|
||||
</SidebarItem>
|
||||
```
|
||||
|
||||
When you startup your Backstage app and hover over the Home option on the sidebar you'll now see a nice sub-menu appear with links to the various Kinds in your Catalog. It would look like this:
|
||||
|
||||

|
||||
|
||||
You can see more ways to use this in the [Storybook Sidebar examples](https://backstage.io/storybook/?path=/story/layout-sidebar--sample-scalable-sidebar)
|
||||
In the new frontend system, the sidebar is managed by the built-in `app/nav` extension. You can customize it by creating a `NavContentBlueprint` extension. See the [sidebar customization](../frontend-system/building-apps/08-migrating.md#app-root-sidebar) documentation for detailed instructions on creating a custom sidebar layout with sub-menus and custom grouping.
|
||||
|
||||
## Custom Homepage
|
||||
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
id: enable-public-entry--old
|
||||
title: Enabling a public entry point (Old Frontend System)
|
||||
description: A guide for how to experiment with public and protected Backstage app bundles
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is for Backstage apps that still use the old frontend
|
||||
system. If your app uses the new frontend system, read the
|
||||
[current guide](./enable-public-entry.md) instead.
|
||||
::::
|
||||
|
||||
# Enable Public Entry (Experimental)
|
||||
|
||||
In this tutorial, you will learn how to restrict access to your main Backstage app bundle to authenticated users only.
|
||||
|
||||
It is expected that the protected bundle feature will be refined in future development iterations, but for now, here is a simplified explanation of how it works:
|
||||
|
||||
Your Backstage app bundle is split into two code entries:
|
||||
|
||||
- Public entry point containing login pages;
|
||||
- There is also a protected main entry point that contains the code for what you see after signing in.
|
||||
|
||||
With that, Backstage's cli and backend will detect public entry point and serve it to unauthenticated users, while serving the main, protected entry point only to authenticated users.
|
||||
|
||||
## Requirements
|
||||
|
||||
- The app needs to be served by the `app-backend` plugin, or this won't work;
|
||||
- Also it will only work for those using `backstage-cli` to build and serve their Backstage app.
|
||||
|
||||
## Step-by-step
|
||||
|
||||
1. Create a `index-public-experimental.tsx` in your app `src` folder.
|
||||
:::note
|
||||
The filename is a convention, so it is not currently configurable.
|
||||
:::
|
||||
|
||||
2. This file is the public entry point for your application, and it should only contain what unauthenticated users should see:
|
||||
|
||||
```tsx title="in packages/app/src/index-public-experimental.tsx"
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { AppRouter } from '@backstage/core-app-api';
|
||||
import {
|
||||
AlertDisplay,
|
||||
OAuthRequestDialog,
|
||||
SignInPage,
|
||||
} from '@backstage/core-components';
|
||||
import {
|
||||
configApiRef,
|
||||
discoveryApiRef,
|
||||
createApiFactory,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { CookieAuthRedirect } from '@backstage/plugin-auth-react';
|
||||
|
||||
// Notice that this is only setting up what is needed by the sign-in pages
|
||||
const app = createApp({
|
||||
// If you have any custom APIs that your sign-in page depends on, you need to add them here
|
||||
apis: [],
|
||||
components: {
|
||||
SignInPage: props => {
|
||||
return (
|
||||
<SignInPage
|
||||
{...props}
|
||||
providers={['guest']}
|
||||
title="Select a sign-in method"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const App = app.createRoot(
|
||||
<>
|
||||
<AlertDisplay transientTimeoutMs={2500} />
|
||||
<OAuthRequestDialog />
|
||||
<AppRouter>
|
||||
{/* This component triggers an authenticated redirect to the main app, while staying on the same URL */}
|
||||
<CookieAuthRedirect />
|
||||
</AppRouter>
|
||||
</>,
|
||||
);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
||||
```
|
||||
|
||||
:::note
|
||||
The frontend will handle cookie refreshing automatically, so you don't have to worry about it.
|
||||
:::
|
||||
|
||||
3. Let's verify that everything is working locally. From your project root folder, run the following commands to build the app and start the backend:
|
||||
|
||||
```sh
|
||||
# building the app package
|
||||
yarn workspace app start
|
||||
# starting the backend api
|
||||
yarn start-backend
|
||||
```
|
||||
|
||||
4. Visit http://localhost:7007 to see the public app and validate that the _index.html_ response only contains a minimal application.
|
||||
:::note
|
||||
Regular app serving will always serve protected apps without authenticating.
|
||||
:::
|
||||
|
||||
5. Finally, as soon as you log in, you will be redirected to the main app home page (inspect the page and see that the protected bundle was served from the app backend after the redirect).
|
||||
|
||||
That's it!
|
||||
@@ -4,6 +4,13 @@ title: Enabling a public entry point
|
||||
description: A guide for how to experiment with public and protected Backstage app bundles
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is written for the new frontend system, which is the default
|
||||
in new Backstage apps. If your Backstage app still uses the old frontend system,
|
||||
read the [old frontend system version of this guide](./enable-public-entry--old.md)
|
||||
instead.
|
||||
::::
|
||||
|
||||
# Enable Public Entry (Experimental)
|
||||
|
||||
In this tutorial, you will learn how to restrict access to your main Backstage app bundle to authenticated users only.
|
||||
@@ -33,51 +40,21 @@ With that, Backstage's cli and backend will detect public entry point and serve
|
||||
|
||||
```tsx title="in packages/app/src/index-public-experimental.tsx"
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { createApp } from '@backstage/app-defaults';
|
||||
import { AppRouter } from '@backstage/core-app-api';
|
||||
import {
|
||||
AlertDisplay,
|
||||
OAuthRequestDialog,
|
||||
SignInPage,
|
||||
} from '@backstage/core-components';
|
||||
import {
|
||||
configApiRef,
|
||||
discoveryApiRef,
|
||||
createApiFactory,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { CookieAuthRedirect } from '@backstage/plugin-auth-react';
|
||||
import { signInPageModule } from './overrides/SignInPage';
|
||||
import { appModulePublicSignIn } from '@backstage/plugin-app/alpha';
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
|
||||
// Notice that this is only setting up what is needed by the sign-in pages
|
||||
const app = createApp({
|
||||
// If you have any custom APIs that your sign-in page depends on, you need to add them here
|
||||
apis: [],
|
||||
components: {
|
||||
SignInPage: props => {
|
||||
return (
|
||||
<SignInPage
|
||||
{...props}
|
||||
providers={['guest']}
|
||||
title="Select a sign-in method"
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
features: [signInPageModule, appModulePublicSignIn],
|
||||
});
|
||||
|
||||
const App = app.createRoot(
|
||||
<>
|
||||
<AlertDisplay transientTimeoutMs={2500} />
|
||||
<OAuthRequestDialog />
|
||||
<AppRouter>
|
||||
{/* This component triggers an authenticated redirect to the main app, while staying on the same URL */}
|
||||
<CookieAuthRedirect />
|
||||
</AppRouter>
|
||||
</>,
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
app.createRoot(),
|
||||
);
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
|
||||
```
|
||||
|
||||
The `signInPageModule` is your custom sign-in page extension that you should already have configured in your app. The `appModulePublicSignIn` from `@backstage/plugin-app/alpha` provides the `CookieAuthRedirect` component that triggers an authenticated redirect to the main app after sign-in.
|
||||
|
||||
:::note
|
||||
The frontend will handle cookie refreshing automatically, so you don't have to worry about it.
|
||||
:::
|
||||
@@ -99,20 +76,3 @@ With that, Backstage's cli and backend will detect public entry point and serve
|
||||
5. Finally, as soon as you log in, you will be redirected to the main app home page (inspect the page and see that the protected bundle was served from the app backend after the redirect).
|
||||
|
||||
That's it!
|
||||
|
||||
## New Frontend System
|
||||
|
||||
If your app uses the new frontend system, you can still use the public entry point feature. The `index-public-experimental.tsx` file does end up looking a bit different in this case:
|
||||
|
||||
```tsx title="in packages/app/src/index-public-experimental.tsx"
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { signInPageModule } from './overrides/SignInPage';
|
||||
import { appModulePublicSignIn } from '@backstage/plugin-app/alpha';
|
||||
import { createApp } from '@backstage/frontend-defaults';
|
||||
|
||||
const app = createApp({
|
||||
features: [signInPageModule, appModulePublicSignIn],
|
||||
});
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(app.createRoot());
|
||||
```
|
||||
|
||||
@@ -0,0 +1,315 @@
|
||||
---
|
||||
id: quickstart-app-plugin--old
|
||||
title: Adding Custom Plugin to Existing Monorepo App (Old Frontend System)
|
||||
description: Tutorial for adding a custom plugin to an existing Backstage monorepo application
|
||||
---
|
||||
|
||||
::::info
|
||||
This documentation is for Backstage apps that still use the old frontend
|
||||
system. If your app uses the new frontend system, read the
|
||||
[current guide](./quickstart-app-plugin.md) instead.
|
||||
::::
|
||||
|
||||
###### September 15th 2020 - v0.1.1-alpha.21
|
||||
|
||||
<br />
|
||||
|
||||
> This document takes you through setting up a new plugin for your existing
|
||||
> monorepo with a _GitHub provider already setup_. If you don't have either of
|
||||
> those, you can clone
|
||||
> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app)
|
||||
> which this document builds on.
|
||||
>
|
||||
> This document does not cover authoring a plugin for sharing with the Backstage
|
||||
> community. That will have to be a later discussion.
|
||||
>
|
||||
> We start with a skeleton plugin install. And after verifying its
|
||||
> functionality, extend the Sidebar to make our life easy. Finally, we add
|
||||
> custom code to display GitHub repository information.
|
||||
>
|
||||
> This document assumes you have Node.js 16 active along with Yarn and Python.
|
||||
> Please note, that at the time of this writing, the current version is
|
||||
> 0.1.1-alpha.21. This guide can still be used with future versions, just,
|
||||
> verify as you go. If you run into issues, you can compare your setup with mine
|
||||
> here >
|
||||
> [simple-backstage-app-plugin](https://github.com/johnson-jesse/simple-backstage-app-plugin).
|
||||
|
||||
## The Skeleton Plugin
|
||||
|
||||
1. Start by using the built-in creator. From the terminal and root of your
|
||||
project run: `yarn new` and select `frontend-plugin`.
|
||||
1. Enter a plugin ID. I used `github-playground`
|
||||
1. When the process finishes, let's start the backend:
|
||||
`yarn --cwd packages/backend start`
|
||||
1. If you see errors starting, refer to
|
||||
[Auth Configuration](https://backstage.io/docs/auth/) for more information on
|
||||
environment variables.
|
||||
1. And now the frontend, from a new terminal window and the root of your
|
||||
project: `yarn start`
|
||||
1. As usual, a browser window should popup loading the App.
|
||||
1. Now manually navigate to our plugin page from your browser:
|
||||
`http://localhost:3000/github-playground`
|
||||
1. You should see successful verbiage for this endpoint,
|
||||
`Welcome to github-playground!`
|
||||
|
||||
## The Shortcut
|
||||
|
||||
Let's add a shortcut.
|
||||
|
||||
1. Open and modify `root: packages > app > src > components > Root.tsx` with the
|
||||
following:
|
||||
|
||||
```tsx
|
||||
import GitHubIcon from '@material-ui/icons/GitHub';
|
||||
...
|
||||
<SidebarItem icon={GitHubIcon} to="github-playground" text="GitHub Repository" />
|
||||
```
|
||||
|
||||
Simple! The App will reload with your changes automatically. You should now see
|
||||
a GitHub icon displayed in the sidebar. Clicking that will link to our new
|
||||
plugin. And now, the API fun begins.
|
||||
|
||||
## The Identity
|
||||
|
||||
Our first modification will be to extract information from the Identity API.
|
||||
|
||||
1. Start by opening
|
||||
`root: plugins > github-playground > src > components > ExampleComponent > ExampleComponent.tsx`
|
||||
1. Add two new imports
|
||||
|
||||
```tsx
|
||||
// Add identityApiRef to the list of imported from core
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
```
|
||||
|
||||
3. Adjust the ExampleComponent from inline to block
|
||||
|
||||
_from inline:_
|
||||
|
||||
```tsx
|
||||
const ExampleComponent = () => ( ... )
|
||||
```
|
||||
|
||||
_to block:_
|
||||
|
||||
```tsx
|
||||
const ExampleComponent = () => {
|
||||
|
||||
return (
|
||||
...
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
4. Now add our hook and const data before the return statement
|
||||
|
||||
```tsx
|
||||
// our API hook
|
||||
const identityApi = useApi(identityApiRef);
|
||||
|
||||
// data to use
|
||||
const userId = identityApi.getUserId();
|
||||
const profile = identityApi.getProfile();
|
||||
```
|
||||
|
||||
5. Finally, update the InfoCard's jsx to use our new data
|
||||
|
||||
```tsx
|
||||
<InfoCard title={userId}>
|
||||
<Typography variant="body1">
|
||||
{`${profile.displayName} | ${profile.email}`}
|
||||
</Typography>
|
||||
</InfoCard>
|
||||
```
|
||||
|
||||
If everything is saved, you should see your name, id, and email on the
|
||||
github-playground page. Our data accessed is synchronous. So we just grab and
|
||||
go.
|
||||
|
||||
https://github.com/backstage/backstage/tree/master/contrib
|
||||
|
||||
6. Here is the entire file for reference
|
||||
[ExampleComponent.tsx](https://github.com/backstage/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleComponent.md)
|
||||
|
||||
## The Wipe
|
||||
|
||||
The last file we will touch is ExampleFetchComponent. Because of the number of
|
||||
changes, let's start by wiping this component clean.
|
||||
|
||||
1. Start by opening
|
||||
`root: plugins > github-playground > src > components > ExampleFetchComponent > ExampleFetchComponent.tsx`
|
||||
1. Replace everything in the file with the following:
|
||||
|
||||
```tsx
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import { Table, TableColumn, Progress } from '@backstage/core-components';
|
||||
import { githubAuthApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { graphql } from '@octokit/graphql';
|
||||
|
||||
export const ExampleFetchComponent = () => {
|
||||
return <div>Nothing to see yet</div>;
|
||||
};
|
||||
```
|
||||
|
||||
3. Save that and ensure you see no errors. Comment out the unused imports if
|
||||
your linter gets in the way.
|
||||
|
||||
###### We will add a lot to this file for the sake of ease. Please don't do this in productional code!
|
||||
|
||||
## The Graph Model
|
||||
|
||||
GitHub has a GraphQL API available for interacting. Let's start by adding our
|
||||
basic repository query
|
||||
|
||||
1. Add the query const statement outside ExampleFetchComponent
|
||||
|
||||
```tsx
|
||||
const query = `{
|
||||
viewer {
|
||||
repositories(first: 100) {
|
||||
totalCount
|
||||
nodes {
|
||||
name
|
||||
createdAt
|
||||
description
|
||||
diskUsage
|
||||
isFork
|
||||
}
|
||||
pageInfo {
|
||||
endCursor
|
||||
hasNextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}`;
|
||||
```
|
||||
|
||||
2. Using this structure as a guide, we will break our query into type parts
|
||||
3. Add the following outside of ExampleFetchComponent
|
||||
|
||||
```tsx
|
||||
type Node = {
|
||||
name: string;
|
||||
createdAt: string;
|
||||
description: string;
|
||||
diskUsage: number;
|
||||
isFork: boolean;
|
||||
};
|
||||
|
||||
type Viewer = {
|
||||
repositories: {
|
||||
totalCount: number;
|
||||
nodes: Node[];
|
||||
pageInfo: {
|
||||
endCursor: string;
|
||||
hasNextPage: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## The Table Model
|
||||
|
||||
Using Backstage's own component library, let's define a custom table. This
|
||||
component will get used if we have data to display.
|
||||
|
||||
1. Add the following outside of ExampleFetchComponent
|
||||
|
||||
```tsx
|
||||
type DenseTableProps = {
|
||||
viewer: Viewer;
|
||||
};
|
||||
|
||||
export const DenseTable = ({ viewer }: DenseTableProps) => {
|
||||
const columns: TableColumn[] = [
|
||||
{ title: 'Name', field: 'name' },
|
||||
{ title: 'Created', field: 'createdAt' },
|
||||
{ title: 'Description', field: 'description' },
|
||||
{ title: 'Disk Usage', field: 'diskUsage' },
|
||||
{ title: 'Fork', field: 'isFork' },
|
||||
];
|
||||
|
||||
return (
|
||||
<Table
|
||||
title="List Of User's Repositories"
|
||||
options={{ search: false, paging: false }}
|
||||
columns={columns}
|
||||
data={viewer.repositories.nodes}
|
||||
/>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
## The Fetch
|
||||
|
||||
We're ready to flush out our fetch component
|
||||
|
||||
1. Add our api hook inside ExampleFetchComponent
|
||||
|
||||
```tsx
|
||||
const auth = useApi(githubAuthApiRef);
|
||||
```
|
||||
|
||||
2. The access token we need to make our GitHub request and the request itself is
|
||||
obtained in an asynchronous manner.
|
||||
3. Add the `useAsync` block inside the ExampleFetchComponent
|
||||
|
||||
```tsx
|
||||
const { value, loading, error } = useAsync(async (): Promise<any> => {
|
||||
const token = await auth.getAccessToken();
|
||||
|
||||
const gqlEndpoint = graphql.defaults({
|
||||
// Uncomment baseUrl if using enterprise
|
||||
// baseUrl: 'https://github.MY-BIZ.com/api',
|
||||
headers: {
|
||||
authorization: `token ${token}`,
|
||||
},
|
||||
});
|
||||
const { viewer } = await gqlEndpoint(query);
|
||||
return viewer;
|
||||
}, []);
|
||||
```
|
||||
|
||||
4. The resolved data is conveniently destructured with `value` containing our
|
||||
Viewer type. `loading` as a boolean, self explanatory. And `error` which is
|
||||
present only if necessary. So let's use those as the first 3 of 4 multi
|
||||
return statements.
|
||||
5. Add the _if return_ blocks below our async block
|
||||
|
||||
```tsx
|
||||
if (loading) return <Progress />;
|
||||
if (error) return <Alert severity="error">{error.message}</Alert>;
|
||||
if (value && value.repositories) return <DenseTable viewer={value} />;
|
||||
```
|
||||
|
||||
6. The third line here utilizes our custom table accepting our Viewer type.
|
||||
7. Finally, we add our _else return_ block to catch any other scenarios.
|
||||
|
||||
```tsx
|
||||
return (
|
||||
<Table
|
||||
title="List Of User's Repositories"
|
||||
options={{ search: false, paging: false }}
|
||||
columns={[]}
|
||||
data={[]}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
8. After saving that, and given we don't have any errors, you should see a table
|
||||
with basic information on your repositories.
|
||||
9. Here is the entire file for reference
|
||||
[ExampleFetchComponent.tsx](https://github.com/backstage/backstage/tree/master/contrib/docs/tutorials/quickstart-app-plugin/ExampleFetchComponent.md)
|
||||
10. We finished! You should see your own GitHub repository's information
|
||||
displayed in a basic table. If you run into issues, you can compare the repo
|
||||
that backs this document,
|
||||
[simple-backstage-app-plugin](https://github.com/johnson-jesse/simple-backstage-app-plugin)
|
||||
|
||||
## Where to go from here
|
||||
|
||||
> Break apart ExampleFetchComponent into smaller logical parts contained in
|
||||
> their own files. Rename your components to something other than ExampleXxx.
|
||||
>
|
||||
> You might be really proud of a plugin you develop. Consider sharing it with
|
||||
> the Backstage community by contributing to the [community-plugins repository](https://github.com/backstage/community-plugins).
|
||||
@@ -4,35 +4,27 @@ title: Adding Custom Plugin to Existing Monorepo App
|
||||
description: Tutorial for adding a custom plugin to an existing Backstage monorepo application
|
||||
---
|
||||
|
||||
###### September 15th 2020 - v0.1.1-alpha.21
|
||||
|
||||
<br />
|
||||
::::info
|
||||
This documentation is written for the new frontend system, which is the default
|
||||
in new Backstage apps. If your Backstage app still uses the old frontend system,
|
||||
read the [old frontend system version of this guide](./quickstart-app-plugin--old.md)
|
||||
instead.
|
||||
::::
|
||||
|
||||
> This document takes you through setting up a new plugin for your existing
|
||||
> monorepo with a _GitHub provider already setup_. If you don't have either of
|
||||
> those, you can clone
|
||||
> [simple-backstage-app](https://github.com/johnson-jesse/simple-backstage-app)
|
||||
> which this document builds on.
|
||||
> monorepo with a _GitHub provider already setup_.
|
||||
>
|
||||
> This document does not cover authoring a plugin for sharing with the Backstage
|
||||
> community. That will have to be a later discussion.
|
||||
>
|
||||
> We start with a skeleton plugin install. And after verifying its
|
||||
> functionality, extend the Sidebar to make our life easy. Finally, we add
|
||||
> custom code to display GitHub repository information.
|
||||
>
|
||||
> This document assumes you have Node.js 16 active along with Yarn and Python.
|
||||
> Please note, that at the time of this writing, the current version is
|
||||
> 0.1.1-alpha.21. This guide can still be used with future versions, just,
|
||||
> verify as you go. If you run into issues, you can compare your setup with mine
|
||||
> here >
|
||||
> [simple-backstage-app-plugin](https://github.com/johnson-jesse/simple-backstage-app-plugin).
|
||||
> functionality, we add custom code to display GitHub repository information.
|
||||
|
||||
## The Skeleton Plugin
|
||||
|
||||
1. Start by using the built-in creator. From the terminal and root of your
|
||||
project run: `yarn new` and select `frontend-plugin`.
|
||||
1. Enter a plugin ID. I used `github-playground`
|
||||
1. Enter a plugin ID. We'll use `github-playground` for this tutorial.
|
||||
1. When the process finishes, let's start the backend:
|
||||
`yarn --cwd packages/backend start`
|
||||
1. If you see errors starting, refer to
|
||||
@@ -41,27 +33,15 @@ description: Tutorial for adding a custom plugin to an existing Backstage monore
|
||||
1. And now the frontend, from a new terminal window and the root of your
|
||||
project: `yarn start`
|
||||
1. As usual, a browser window should popup loading the App.
|
||||
1. Now manually navigate to our plugin page from your browser:
|
||||
1. Now manually navigate to the plugin page from your browser:
|
||||
`http://localhost:3000/github-playground`
|
||||
1. You should see successful verbiage for this endpoint,
|
||||
`Welcome to github-playground!`
|
||||
|
||||
## The Shortcut
|
||||
|
||||
Let's add a shortcut.
|
||||
|
||||
1. Open and modify `root: packages > app > src > components > Root.tsx` with the
|
||||
following:
|
||||
|
||||
```tsx
|
||||
import GitHubIcon from '@material-ui/icons/GitHub';
|
||||
...
|
||||
<SidebarItem icon={GitHubIcon} to="github-playground" text="GitHub Repository" />
|
||||
```
|
||||
|
||||
Simple! The App will reload with your changes automatically. You should now see
|
||||
a GitHub icon displayed in the sidebar. Clicking that will link to our new
|
||||
plugin. And now, the API fun begins.
|
||||
With the new frontend system, plugins are auto-discovered when installed as
|
||||
dependencies of your `packages/app` package. The plugin was already added there
|
||||
by `yarn new`, so the route and a sidebar item are available without any manual
|
||||
wiring in `App.tsx` or `Root.tsx`.
|
||||
|
||||
## The Identity
|
||||
|
||||
@@ -72,7 +52,6 @@ Our first modification will be to extract information from the Identity API.
|
||||
1. Add two new imports
|
||||
|
||||
```tsx
|
||||
// Add identityApiRef to the list of imported from core
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
```
|
||||
|
||||
@@ -98,10 +77,8 @@ const ExampleComponent = () => {
|
||||
4. Now add our hook and const data before the return statement
|
||||
|
||||
```tsx
|
||||
// our API hook
|
||||
const identityApi = useApi(identityApiRef);
|
||||
|
||||
// data to use
|
||||
const userId = identityApi.getUserId();
|
||||
const profile = identityApi.getProfile();
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user