Skip to content

Commit ef10f2e

Browse files
feat(fab): add new mountpoints support for providers and listeners (#2312)
Signed-off-by: debsmita1 <[email protected]> Signed-off-by: Christoph Jerolimov <[email protected]> Co-authored-by: debsmita1 <[email protected]>
1 parent c724c77 commit ef10f2e

File tree

16 files changed

+306
-60
lines changed

16 files changed

+306
-60
lines changed

.rhdh/docker/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/roadiehq-backstage-plugin-
7474
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
7575
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
7676
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
77+
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button/package.json
7778
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
7879
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
7980
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

app-config.dynamic-plugins.yaml

+11
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,17 @@ dynamicPlugins:
258258
config:
259259
layout:
260260
position: above-main-content
261+
red-hat-developer-hub.backstage-plugin-global-floating-action-button:
262+
mountPoints:
263+
- mountPoint: application/listener
264+
importName: DynamicGlobalFloatingActionButton
265+
- mountPoint: global.floatingactionbutton/component
266+
importName: NullComponent
267+
config:
268+
icon: github
269+
label: Git
270+
toolTip: Github
271+
to: https://github.com/redhat-developer/rhdh
261272
red-hat-developer-hub.backstage-plugin-dynamic-home-page:
262273
dynamicRoutes:
263274
- path: /

docker/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/roadiehq-backstage-plugin-
7575
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
7676
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
7777
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
78+
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button/package.json
7879
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
7980
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
8081
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

docs/dynamic-plugins/frontend-plugin-wiring.md

+36
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,42 @@ Each global header entry requires the following attributes:
400400

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

403+
### Adding application listeners
404+
405+
The users can add application listeners using the `application/listener` mount point. Below is an example that uses the aforesaid mount point:
406+
407+
```yaml
408+
# app-config.yaml
409+
dynamicPlugins:
410+
frontend:
411+
<package_name>: # plugin_package_name same as `scalprum.name` key in plugin's `package.json`
412+
mountPoints:
413+
- mountPoint: application/listener
414+
importName: <exported listener component>
415+
```
416+
417+
Users can configure multiple application listeners by adding entries to the `mountPoints` field.
418+
419+
### Adding application providers
420+
421+
The users can add application providers using the `application/provider` mount point. Below is an example that uses the aforesaid mount point to configure a context provider:
422+
423+
```yaml
424+
# app-config.yaml
425+
dynamicPlugins:
426+
frontend:
427+
<package_name>: # plugin_package_name same as `scalprum.name` key in plugin's `package.json`
428+
dynamicRoutes:
429+
- path: /<route>
430+
importName: Component # Component you want to load on the route
431+
mountPoints:
432+
- mountPoint: application/provider
433+
importName: <exported provider component>
434+
```
435+
436+
Users can configure multiple application providers by adding entries to the `mountPoints` field.
437+
438+
403439
## Customizing and Adding Entity tabs
404440

405441
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:

dynamic-plugins.default.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,24 @@ plugins:
536536
layout:
537537
position: above-main-content
538538

539+
# Group: Global floating action button
540+
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-floating-action-button
541+
disabled: true
542+
pluginConfig:
543+
dynamicPlugins:
544+
frontend:
545+
red-hat-developer-hub.backstage-plugin-global-floating-action-button:
546+
mountPoints:
547+
- mountPoint: application/listener
548+
importName: DynamicGlobalFloatingActionButton
549+
- mountPoint: global.floatingactionbutton/component
550+
importName: NullComponent
551+
config:
552+
icon: github
553+
label: 'Git'
554+
toolTip: 'Github'
555+
to: https://github.com/redhat-developer/rhdh
556+
539557
# Homepage
540558
- package: ./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-dynamic-home-page
541559
disabled: false
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist-dynamic
2+
dist-scalprum
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require("@backstage/cli/config/eslint-factory")(__dirname);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"name": "red-hat-developer-hub-backstage-plugin-global-floating-action-button",
3+
"version": "0.0.6",
4+
"main": "src/index.ts",
5+
"types": "src/index.ts",
6+
"license": "Apache-2.0",
7+
"publishConfig": {
8+
"access": "public",
9+
"main": "dist/index.cjs.js",
10+
"types": "dist/index.d.ts"
11+
},
12+
"backstage": {
13+
"role": "frontend-plugin",
14+
"supported-versions": "1.35.0",
15+
"pluginId": "red-hat-developer-hub-backstage-plugin-global-floating-action-button",
16+
"pluginPackages": [
17+
"red-hat-developer-hub-backstage-plugin-global-floating-action-button"
18+
]
19+
},
20+
"sideEffects": false,
21+
"scripts": {
22+
"tsc": "tsc",
23+
"build": "backstage-cli package build",
24+
"lint:check": "backstage-cli package lint",
25+
"test": "backstage-cli package test --passWithNoTests --coverage",
26+
"clean": "backstage-cli package clean",
27+
"export-dynamic": "janus-cli package export-dynamic-plugin --in-place",
28+
"export-dynamic:clean": "run export-dynamic --clean"
29+
},
30+
"dependencies": {
31+
"@mui/material": "5.16.14",
32+
"@red-hat-developer-hub/backstage-plugin-global-floating-action-button": "0.0.6"
33+
},
34+
"devDependencies": {
35+
"@backstage/cli": "0.29.6",
36+
"@janus-idp/cli": "3.0.0",
37+
"typescript": "5.7.3"
38+
},
39+
"scalprum": {
40+
"name": "red-hat-developer-hub.backstage-plugin-global-floating-action-button",
41+
"exposedModules": {
42+
"PluginRoot": "./src/index.ts"
43+
}
44+
},
45+
"files": [
46+
"dist",
47+
"dist-scalprum"
48+
],
49+
"keywords": [
50+
"backstage",
51+
"plugin"
52+
],
53+
"repository": {
54+
"type": "git",
55+
"url": "https://github.com/redhat-developer/rhdh",
56+
"directory": "dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button"
57+
},
58+
"author": "Red Hat",
59+
"homepage": "https://red.ht/rhdh",
60+
"bugs": "https://issues.redhat.com/browse/RHIDP"
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { unstable_ClassNameGenerator as ClassNameGenerator } from "@mui/material/className";
2+
3+
ClassNameGenerator.configure((componentName) => {
4+
return componentName.startsWith("v5-")
5+
? componentName
6+
: `v5-${componentName}`;
7+
});
8+
9+
export * from "@red-hat-developer-hub/backstage-plugin-global-floating-action-button";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"extends": "@backstage/cli/config/tsconfig.json",
3+
"include": ["src", "dev", "migrations"],
4+
"exclude": ["node_modules"],
5+
"compilerOptions": {
6+
"outDir": "../../../dist-types/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button",
7+
"rootDir": "."
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": ["//"],
3+
"tasks": {
4+
"tsc": {
5+
"outputs": [
6+
"../../../dist-types/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button/**"
7+
]
8+
}
9+
}
10+
}

packages/app/src/components/AppBase/AppBase.tsx

+64-59
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import { entityPage } from '../catalog/EntityPage';
2424
import DynamicRootContext from '../DynamicRoot/DynamicRootContext';
2525
import { LearningPaths } from '../learningPaths/LearningPathsPage';
2626
import { Root } from '../Root';
27+
import { ApplicationListener } from '../Root/ApplicationListener';
28+
import { ApplicationProvider } from '../Root/ApplicationProvider';
2729
import ConfigUpdater from '../Root/ConfigUpdater';
2830
import { SearchPage } from '../search/SearchPage';
2931
import { settingsPage } from '../UserSettings/SettingsPages';
@@ -74,66 +76,69 @@ const AppBase = () => {
7476
<OAuthRequestDialog />
7577
<AppRouter>
7678
<ConfigUpdater />
79+
<ApplicationListener />
7780
<Root>
78-
<FlatRoutes>
79-
<Route
80-
path="/catalog"
81-
element={
82-
<CatalogIndexPage pagination columns={myCustomColumnsFunc} />
83-
}
84-
/>
85-
<Route
86-
path="/catalog/:namespace/:kind/:name"
87-
element={<CatalogEntityPage />}
88-
>
89-
{entityPage(entityTabOverrides)}
90-
</Route>
91-
<Route
92-
path="/create"
93-
element={
94-
<ScaffolderPage
95-
headerOptions={{ title: 'Software Templates' }}
96-
/>
97-
}
98-
>
99-
<ScaffolderFieldExtensions>
100-
{scaffolderFieldExtensions.map(
101-
({ scope, module, importName, Component }) => (
102-
<Component key={`${scope}-${module}-${importName}`} />
103-
),
104-
)}
105-
</ScaffolderFieldExtensions>
106-
scaffolderFieldExtensions
107-
</Route>
108-
<Route path="/api-docs" element={<ApiExplorerPage />} />
109-
<Route
110-
path="/catalog-import"
111-
element={
112-
<RequirePermission permission={catalogEntityCreatePermission}>
113-
<CatalogImportPage />
114-
</RequirePermission>
115-
}
116-
/>
117-
<Route path="/search" element={<BackstageSearchPage />}>
118-
<SearchPage />
119-
</Route>
120-
<Route path="/settings" element={<UserSettingsPage />}>
121-
{settingsPage}
122-
</Route>
123-
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
124-
<Route path="/learning-paths" element={<LearningPaths />} />
125-
{dynamicRoutes.map(
126-
({ Component, staticJSXContent, path, config: { props } }) => (
127-
<Route
128-
key={path}
129-
path={path}
130-
element={<Component {...props} />}
131-
>
132-
{staticJSXContent}
133-
</Route>
134-
),
135-
)}
136-
</FlatRoutes>
81+
<ApplicationProvider>
82+
<FlatRoutes>
83+
<Route
84+
path="/catalog"
85+
element={
86+
<CatalogIndexPage pagination columns={myCustomColumnsFunc} />
87+
}
88+
/>
89+
<Route
90+
path="/catalog/:namespace/:kind/:name"
91+
element={<CatalogEntityPage />}
92+
>
93+
{entityPage(entityTabOverrides)}
94+
</Route>
95+
<Route
96+
path="/create"
97+
element={
98+
<ScaffolderPage
99+
headerOptions={{ title: 'Software Templates' }}
100+
/>
101+
}
102+
>
103+
<ScaffolderFieldExtensions>
104+
{scaffolderFieldExtensions.map(
105+
({ scope, module, importName, Component }) => (
106+
<Component key={`${scope}-${module}-${importName}`} />
107+
),
108+
)}
109+
</ScaffolderFieldExtensions>
110+
scaffolderFieldExtensions
111+
</Route>
112+
<Route path="/api-docs" element={<ApiExplorerPage />} />
113+
<Route
114+
path="/catalog-import"
115+
element={
116+
<RequirePermission permission={catalogEntityCreatePermission}>
117+
<CatalogImportPage />
118+
</RequirePermission>
119+
}
120+
/>
121+
<Route path="/search" element={<BackstageSearchPage />}>
122+
<SearchPage />
123+
</Route>
124+
<Route path="/settings" element={<UserSettingsPage />}>
125+
{settingsPage}
126+
</Route>
127+
<Route path="/catalog-graph" element={<CatalogGraphPage />} />
128+
<Route path="/learning-paths" element={<LearningPaths />} />
129+
{dynamicRoutes.map(
130+
({ Component, staticJSXContent, path, config: { props } }) => (
131+
<Route
132+
key={path}
133+
path={path}
134+
element={<Component {...props} />}
135+
>
136+
{staticJSXContent}
137+
</Route>
138+
),
139+
)}
140+
</FlatRoutes>
141+
</ApplicationProvider>
137142
</Root>
138143
</AppRouter>
139144
</AppProvider>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { useContext } from 'react';
2+
3+
import { ErrorBoundary } from '@backstage/core-components';
4+
5+
import DynamicRootContext from '../DynamicRoot/DynamicRootContext';
6+
7+
export const ApplicationListener = () => {
8+
const { mountPoints } = useContext(DynamicRootContext);
9+
const listeners = mountPoints['application/listener'] ?? [];
10+
return listeners.map(({ Component }, index) => {
11+
return (
12+
<ErrorBoundary
13+
// eslint-disable-next-line react/no-array-index-key
14+
key={index}
15+
>
16+
<Component />
17+
</ErrorBoundary>
18+
);
19+
});
20+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { useContext, useMemo } from 'react';
2+
3+
import { ErrorBoundary } from '@backstage/core-components';
4+
5+
import DynamicRootContext from '../DynamicRoot/DynamicRootContext';
6+
7+
export const ApplicationProvider = ({
8+
children,
9+
}: React.PropsWithChildren<{}>) => {
10+
const { mountPoints } = useContext(DynamicRootContext);
11+
const providers = useMemo(
12+
() => mountPoints['application/provider'] ?? [],
13+
[mountPoints],
14+
);
15+
if (providers.length === 0) {
16+
return children;
17+
}
18+
return providers.reduceRight((acc, { Component }, index) => {
19+
return (
20+
<ErrorBoundary
21+
// eslint-disable-next-line react/no-array-index-key
22+
key={index}
23+
>
24+
<Component>{acc}</Component>
25+
</ErrorBoundary>
26+
);
27+
}, children);
28+
};

plugins/dynamic-plugins-info/src/components/InternalPluginsMap.ts

+2
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export const InternalPluginsMap: Record<string, string> = {
6565
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-bulk-import',
6666
'red-hat-developer-hub-backstage-plugin-global-header':
6767
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-header',
68+
'red-hat-developer-hub-backstage-plugin-global-floating-action-button':
69+
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-global-floating-action-button',
6870
'red-hat-developer-hub-backstage-plugin-dynamic-home-page':
6971
'./dynamic-plugins/dist/red-hat-developer-hub-backstage-plugin-dynamic-home-page',
7072
'red-hat-developer-hub-backstage-plugin-marketplace':

0 commit comments

Comments
 (0)