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

feat(global-header): added mount points for global header #2168

Merged
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 .rhdh/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/roadiehq-backstage-plugin-
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/roadiehq-backstage-plugin-argo-cd-backend-dynamic/package.json ./dynamic-plugins/wrappers/roadiehq-backstage-plugin-argo-cd-backend-dynamic/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace-backend-dynamic/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace-backend-dynamic/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-header/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-header/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-catalog-backend-module-marketplace-dynamic/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-catalog-backend-module-marketplace-dynamic/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json
Expand Down
7 changes: 7 additions & 0 deletions app-config.dynamic-plugins.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,13 @@ dynamicPlugins:
menuItem:
icon: bulkImportIcon
text: Bulk import
red-hat-developer-hub.backstage-plugin-global-header:
mountPoints:
- mountPoint: application/header
importName: GlobalHeader
config:
layout:
position: above-main-content
red-hat-developer-hub.backstage-plugin-dynamic-home-page:
dynamicRoutes:
- path: /
Expand Down
1 change: 1 addition & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/roadiehq-backstage-plugin-
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/roadiehq-backstage-plugin-argo-cd-backend-dynamic/package.json ./dynamic-plugins/wrappers/roadiehq-backstage-plugin-argo-cd-backend-dynamic/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace-backend-dynamic/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-marketplace-backend-dynamic/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-header/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-header/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-dynamic-home-page/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-catalog-backend-module-marketplace-dynamic/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-catalog-backend-module-marketplace-dynamic/package.json
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json
Expand Down
39 changes: 34 additions & 5 deletions docs/dynamic-plugins/frontend-plugin-wiring.md
Copy link
Contributor Author

@ciiay ciiay Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,12 @@ This configuration allows you to bind to existing plugins and their routes as we

## Using mount points

This section aims to allow users dynamic extension of [Catalog Components](https://backstage.io/docs/plugins/composability/#catalog-components), but can be used to extend additional views in the future as well.

Mount points are defined identifiers available across the application. These points specifically allow users to extend existing pages with additional content.

### Customizing Entity page

This section aims to allow users dynamic extension of [Catalog Components](https://backstage.io/docs/plugins/composability/#catalog-components), but can be used to extend additional views in the future as well.

The following mount points are available:

| Mount point | Description | Visible even when no plugins are enabled |
Expand Down Expand Up @@ -371,6 +373,33 @@ plugins:
icon: dialogIcon
```

### Adding application header

The frontend system enables users to customize global headers by specifying configurations in the `app-config.yaml` file. Below is an example configuration:

```yaml
# app-config.yaml
dynamicPlugins:
frontend:
<package_name>: # e.g., preinstalled plugin `red-hat-developer-hub.backstage-plugin-global-header`
mountPoints:
- mountPoint: application/header # mount point for a global header
importName: <header_component> # e.g., `GlobalHeader` for `red-hat-developer-hub.backstage-plugin-global-header`
config:
layout:
position: above-main-content # options: `above-main-content` or `above-sidebar`
```

Each global header entry requires the following attributes:

- `mountPoint`: Defines where the header will be added. Use `application/header` to specify it as a global header.
- `importName`: Specifies the component exported by the global header plugin (e.g., `GlobalHeader`).
- `config.layout.position`: Determines the header's position. Supported values are:
- `above-main-content`: Positions the header above the main content area.
- `above-sidebar`: Positions the header above the sidebar.

Users can configure multiple global headers at different positions by adding entries to the `mountPoints` field.

## Customizing and Adding Entity tabs

Out of the box the frontend system provides an opinionated set of tabs for catalog entity views. This set of tabs can be further customized and extended as needed via the `entityTabs` configuration:
Expand Down Expand Up @@ -425,7 +454,7 @@ Here are the default catalog entity routes in the default order:

The visibility of a tab is derived from the kind of entity that is being displayed along with the visibility guidance mentioned in "[Using mount points](#using-mount-points)".

### Provide additional Utility APIs
## Provide additional Utility APIs

Backstage offers a Utility API mechanism that provide ways for plugins to communicate during their entire life cycle. Utility APIs are registered as:

Expand Down Expand Up @@ -487,7 +516,7 @@ dynamicPlugins:

which would override the default `ScmAuth` API factory that Developer Hub defaults to.

### Provide custom Scaffolder field extensions
## Provide custom Scaffolder field extensions

The Backstage scaffolder component supports specifying [custom form fields](https://backstage.io/docs/features/software-templates/writing-custom-field-extensions/#creating-a-field-extension) for the software template wizard, for example:

Expand Down Expand Up @@ -516,7 +545,7 @@ A plugin can specify multiple field extensions, in which case each field extensi
- `importName` is an optional import name that should reference the value returned the scaffolder field extension API
- `module` is an optional argument which allows you to specify which set of assets you want to access within the plugin. If not provided, the default module named `PluginRoot` is used. This is the same as the key in `scalprum.exposedModules` key in plugin's `package.json`.

### Add a custom Backstage theme or replace the provided theme
## Add a custom Backstage theme or replace the provided theme

The look and feel of a Backstage application is handled by Backstage theming. Out of the box Developer Hub provides a theme with a number of [configuration overrides](../customization.md) that allow for user customization. It's also possible to provide additional Backstage themes as well as replace the out of box Developer Hub themes from a dynamic plugin.

Expand Down
13 changes: 13 additions & 0 deletions dynamic-plugins.default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -522,6 +522,19 @@ plugins:
menuItem:
icon: bulkImportIcon
text: Bulk import
# Global Header
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-header
disabled: true
pluginConfig:
dynamicPlugins:
frontend:
red-hat-developer-hub.backstage-plugin-global-header:
mountPoints:
- mountPoint: application/header
importName: GlobalHeader
config:
layout:
position: above-main-content

# Homepage
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-dynamic-home-page
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dist-dynamic
dist-scalprum
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@backstage/cli/config/eslint-factory')(__dirname);
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"name": "red-hat-developer-hub-backstage-plugin-global-header",
"version": "0.0.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"backstage": {
"role": "frontend-plugin",
"supported-versions": "1.32.6",
"pluginId": "red-hat-developer-hub-backstage-plugin-global-header",
"pluginPackages": [
"red-hat-developer-hub-backstage-plugin-global-header"
]
},
"sideEffects": false,
"scripts": {
"tsc": "tsc",
"build": "backstage-cli package build",
"lint:check": "backstage-cli package lint",
"test": "backstage-cli package test --passWithNoTests --coverage",
"clean": "backstage-cli package clean",
"export-dynamic": "janus-cli package export-dynamic-plugin --in-place",
"export-dynamic:clean": "run export-dynamic --clean"
},
"dependencies": {
"@mui/material": "5.16.14",
"@red-hat-developer-hub/backstage-plugin-global-header": "0.0.2"
},
"devDependencies": {
"@backstage/cli": "0.28.2",
"@janus-idp/cli": "1.18.5",
"typescript": "5.7.3"
},
"files": [
"dist",
"dist-scalprum"
],
"scalprum": {
"name": "red-hat-developer-hub.backstage-plugin-global-header",
"exposedModules": {
"PluginRoot": "./src/index.ts"
}
},
"repository": {
"type": "git",
"url": "https://github.com/redhat-developer/rhdh",
"directory": "dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-header"
},
"maintainers": [
"christoph-jerolimov",
"ciiay"
],
"author": "Red Hat",
"homepage": "https://red.ht/rhdh",
"bugs": "https://issues.redhat.com/browse/RHIDP",
"keywords": [
"support:production",
"lifecycle:active"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@red-hat-developer-hub/backstage-plugin-global-header';
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@backstage/cli/config/tsconfig.json",
"include": ["src", "dev", "migrations"],
"exclude": ["node_modules"],
"compilerOptions": {
"outDir": "../../../dist-types/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-header",
"rootDir": "."
}
}
18 changes: 18 additions & 0 deletions packages/app/src/components/Root/ApplicationHeaders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React, { useContext } from 'react';

import { ErrorBoundary } from '@backstage/core-components';

import DynamicRootContext from '../DynamicRoot/DynamicRootContext';

export const ApplicationHeaders = ({ position }: { position: string }) => {
const { mountPoints } = useContext(DynamicRootContext);
const appHeaderMountPoints = mountPoints['application/header'] ?? [];
return appHeaderMountPoints
?.filter(({ config }) => config?.layout?.position === position)
.map(({ Component, config }, index) => (
// eslint-disable-next-line react/no-array-index-key
<ErrorBoundary key={index}>
<Component {...config?.props} />
</ErrorBoundary>
));
};
91 changes: 48 additions & 43 deletions packages/app/src/components/Root/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { policyEntityReadPermission } from '@janus-idp/backstage-plugin-rbac-com
import DynamicRootContext, {
ResolvedMenuItem,
} from '../DynamicRoot/DynamicRootContext';
import { ApplicationHeaders } from './ApplicationHeaders';
import { MenuIcon } from './MenuIcon';
import { SidebarLogo } from './SidebarLogo';

Expand Down Expand Up @@ -248,49 +249,53 @@ export const Root = ({ children }: PropsWithChildren<{}>) => {
);
};
return (
<SidebarPage>
<Sidebar>
<SidebarLogo />
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
<SidebarSearchModal />
</SidebarGroup>
<SidebarDivider />
<SidebarGroup label="Menu" icon={<MuiMenuIcon />}>
{/* Global nav, not org-specific */}
{renderMenuItems(true, false)}
{/* End global nav */}
<>
<ApplicationHeaders position="above-sidebar" />
<SidebarPage>
<ApplicationHeaders position="above-main-content" />
<Sidebar>
<SidebarLogo />
<SidebarGroup label="Search" icon={<SearchIcon />} to="/search">
<SidebarSearchModal />
</SidebarGroup>
<SidebarDivider />
<SidebarScrollWrapper>
{renderMenuItems(false, false)}
{dynamicRoutes.map(({ scope, menuItem, path }) => {
if (menuItem && 'Component' in menuItem) {
return (
<menuItem.Component
{...(menuItem.config?.props || {})}
key={`${scope}/${path}`}
to={path}
/>
);
}
return null;
})}
</SidebarScrollWrapper>
</SidebarGroup>
<SidebarSpace />
<SidebarDivider />
<SidebarGroup label="Administration" icon={<AdminIcon />}>
{renderMenuItems(false, true)}
</SidebarGroup>
<SidebarDivider />
<SidebarGroup
label="Settings"
to="/settings"
icon={<AccountCircleOutlinedIcon />}
>
<SidebarSettings icon={AccountCircleOutlinedIcon} />
</SidebarGroup>
</Sidebar>
{children}
</SidebarPage>
<SidebarGroup label="Menu" icon={<MuiMenuIcon />}>
{/* Global nav, not org-specific */}
{renderMenuItems(true, false)}
{/* End global nav */}
<SidebarDivider />
<SidebarScrollWrapper>
{renderMenuItems(false, false)}
{dynamicRoutes.map(({ scope, menuItem, path }) => {
if (menuItem && 'Component' in menuItem) {
return (
<menuItem.Component
{...(menuItem.config?.props || {})}
key={`${scope}/${path}`}
to={path}
/>
);
}
return null;
})}
</SidebarScrollWrapper>
</SidebarGroup>
<SidebarSpace />
<SidebarDivider />
<SidebarGroup label="Administration" icon={<AdminIcon />}>
{renderMenuItems(false, true)}
</SidebarGroup>
<SidebarDivider />
<SidebarGroup
label="Settings"
to="/settings"
icon={<AccountCircleOutlinedIcon />}
>
<SidebarSettings icon={AccountCircleOutlinedIcon} />
</SidebarGroup>
</Sidebar>
{children}
</SidebarPage>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ export const InternalPluginsMap: Record<string, string> = {
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic',
'red-hat-developer-hub-backstage-plugin-bulk-import':
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import',
'red-hat-developer-hub-backstage-plugin-global-header':
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-header',
'red-hat-developer-hub-backstage-plugin-dynamic-home-page':
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-dynamic-home-page',
'red-hat-developer-hub-backstage-plugin-marketplace':
Expand Down
Loading
Loading