Skip to content

Commit d61135e

Browse files
committed
feat(fab): add new mountpoints support for providers and listeners
1 parent 92fd715 commit d61135e

File tree

18 files changed

+880
-75
lines changed

18 files changed

+880
-75
lines changed

.rhdh/docker/Dockerfile

+1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-back
7878
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
7979
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
8080
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic/package.json
81+
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
8182
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/parfuemerie-douglas-scaffolder-backend-module-azure-repositories-dynamic/package.json ./dynamic-plugins/wrappers/parfuemerie-douglas-scaffolder-backend-module-azure-repositories-dynamic/package.json
8283
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/pagerduty-backstage-plugin/package.json ./dynamic-plugins/wrappers/pagerduty-backstage-plugin/package.json
8384
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/pagerduty-backstage-plugin-backend-dynamic/package.json ./dynamic-plugins/wrappers/pagerduty-backstage-plugin-backend-dynamic/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
@@ -79,6 +79,7 @@ COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-back
7979
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
8080
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
8181
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic/package.json ./dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import-backend-dynamic/package.json
82+
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
8283
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/parfuemerie-douglas-scaffolder-backend-module-azure-repositories-dynamic/package.json ./dynamic-plugins/wrappers/parfuemerie-douglas-scaffolder-backend-module-azure-repositories-dynamic/package.json
8384
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/pagerduty-backstage-plugin/package.json ./dynamic-plugins/wrappers/pagerduty-backstage-plugin/package.json
8485
COPY $EXTERNAL_SOURCE_NESTED/dynamic-plugins/wrappers/pagerduty-backstage-plugin-backend-dynamic/package.json ./dynamic-plugins/wrappers/pagerduty-backstage-plugin-backend-dynamic/package.json

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

+32
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,38 @@ Here are the default catalog entity routes in the default order:
454454

455455
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)".
456456

457+
## Adding application listeners
458+
459+
The users can add application listeners using the `application/listener` mount point. Below is an example that uses the aforesaid mount point to configure a floating action button:
460+
461+
```yaml
462+
# app-config.yaml
463+
dynamicPlugins:
464+
frontend:
465+
red-hat-developer-hub.backstage-plugin-global-floating-action-button: # plugin_package_name same as `scalprum.name` key in plugin's `package.json`
466+
mountPoints:
467+
- mountPoint: application/listener # mount point for adding a listener
468+
importName: DynamicGlobalFloatingActionButton # add your listener component here
469+
```
470+
471+
Users can configure multiple application listeners by adding entries to the `mountPoints` field.
472+
473+
## Adding application providers
474+
475+
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:
476+
477+
```yaml
478+
# app-config.yaml
479+
dynamicPlugins:
480+
frontend:
481+
red-hat-developer-hub.backstage-plugin-test-plugin: # plugin_package_name same as `scalprum.name` key in plugin's `package.json`
482+
mountPoints:
483+
- mountPoint: application/provider # mount point for adding a provider
484+
importName: ProviderComponent # add your provider component here
485+
```
486+
487+
Users can configure multiple application providers by adding entries to the `mountPoints` field.
488+
457489
## Provide additional Utility APIs
458490

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

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/context
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

dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-bulk-import/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"backstage": {
1313
"role": "frontend-plugin",
1414
"supported-versions": "1.35.0",
15-
"pluginId": "red-hat-developer-hub-backstage-plugin-bulk-import-backend",
15+
"pluginId": "red-hat-developer-hub-backstage-plugin-bulk-import",
1616
"pluginPackages": [
1717
"red-hat-developer-hub-backstage-plugin-bulk-import",
1818
"red-hat-developer-hub-backstage-plugin-bulk-import-backend"
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,62 @@
1+
{
2+
"name": "red-hat-developer-hub-backstage-plugin-global-floating-action-button",
3+
"version": "0.0.3",
4+
"main": "src/index.ts",
5+
"types": "src/index.ts",
6+
"license": "Apache-2.0",
7+
"publishConfig": {
8+
"access": "public",
9+
"main": "dist/index.esm.js",
10+
"types": "dist/index.d.ts"
11+
},
12+
"backstage": {
13+
"role": "frontend-plugin",
14+
"supported-versions": "1.32.5",
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.13",
32+
"@red-hat-developer-hub/backstage-plugin-global-floating-action-button": "0.0.3"
33+
},
34+
"devDependencies": {
35+
"@backstage/cli": "0.28.2",
36+
"@janus-idp/cli": "1.18.5",
37+
"typescript": "5.6.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+
"app-config.yaml"
49+
],
50+
"keywords": [
51+
"backstage",
52+
"plugin"
53+
],
54+
"repository": {
55+
"type": "git",
56+
"url": "https://github.com/redhat-developer/rhdh",
57+
"directory": "dynamic-plugins/wrappers/red-hat-developer-hub-backstage-plugin-global-floating-action-button"
58+
},
59+
"author": "Red Hat",
60+
"homepage": "https://red.ht/rhdh",
61+
"bugs": "https://issues.redhat.com/browse/RHIDP"
62+
}
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,25 @@
1+
import { useContext } 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 = mountPoints['application/provider'] ?? [];
12+
if (providers.length === 0) {
13+
return children;
14+
}
15+
return providers.map(({ Component }, index) => {
16+
return (
17+
<ErrorBoundary
18+
// eslint-disable-next-line react/no-array-index-key
19+
key={index}
20+
>
21+
<Component>{children}</Component>
22+
</ErrorBoundary>
23+
);
24+
});
25+
};

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)