Skip to content

Commit

Permalink
feat(global-header): added mount points for global header (#2168)
Browse files Browse the repository at this point in the history
* feat(global-header): added mount points for global header

Signed-off-by: Yi Cai <[email protected]>

* fixed prettier check

Signed-off-by: Yi Cai <[email protected]>

* Added wrapper for global header

Signed-off-by: Yi Cai <[email protected]>

* Updated global header plugin version

Signed-off-by: Yi Cai <[email protected]>

* updated yarn.lock for global header plugin

Signed-off-by: Yi Cai <[email protected]>

* added global header plugin in dockerfiles

Signed-off-by: Yi Cai <[email protected]>

* added global header plugin in InternalPluginsMap

Signed-off-by: Yi Cai <[email protected]>

* added global header plugin to app-config.dynamic-plugins

Signed-off-by: Yi Cai <[email protected]>

* addressed review comments

Signed-off-by: Yi Cai <[email protected]>

* code improvement

Signed-off-by: Yi Cai <[email protected]>

* some improvements

Signed-off-by: Yi Cai <[email protected]>

* addressed comments

Signed-off-by: Yi Cai <[email protected]>

* Addressed review comments

Signed-off-by: Yi Cai <[email protected]>

* prettier fix

Signed-off-by: Yi Cai <[email protected]>

* fixed merge conflicts and addressed review comments

Signed-off-by: Yi Cai <[email protected]>

* Addressed review comments

Signed-off-by: Yi Cai <[email protected]>

* yarn dedupe

Signed-off-by: Yi Cai <[email protected]>

* updated md and resolved failed ci check

Signed-off-by: Yi Cai <[email protected]>

---------

Signed-off-by: Yi Cai <[email protected]>
  • Loading branch information
ciiay authored Jan 24, 2025
1 parent 1ad4541 commit 9a7353c
Show file tree
Hide file tree
Showing 14 changed files with 349 additions and 78 deletions.
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
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

0 comments on commit 9a7353c

Please sign in to comment.