Skip to content

Commit 8e52153

Browse files
committed
feat: allow overriding build info
1 parent a3aeae0 commit 8e52153

File tree

8 files changed

+245
-28
lines changed

8 files changed

+245
-28
lines changed

.ibm/pipelines/resources/config_map/app-config-rhdh.yaml

+8
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,11 @@ argocd:
176176
token: temp
177177
permission:
178178
enabled: false
179+
180+
buildInfo:
181+
title: 'RHDH Build info'
182+
card:
183+
TechDocs builder: 'local'
184+
Authentication provider: 'Github'
185+
RBAC: disabled
186+
full: true

docs/customization.md

+24-8
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ app:
8888
branding:
8989
theme:
9090
light:
91-
primaryColor: '#38be8b'
91+
primaryColor: "#38be8b"
9292
dark:
93-
primaryColor: '#ab75cf'
93+
primaryColor: "#ab75cf"
9494
```
9595

9696
![Example Light Mode Primary Color](images/example-light-mode-primary-color.png)
@@ -107,11 +107,11 @@ app:
107107
branding:
108108
theme:
109109
light:
110-
headerColor1: 'hsl(204 100% 71%)'
111-
headerColor2: 'color(a98-rgb 1 0 0)'
110+
headerColor1: "hsl(204 100% 71%)"
111+
headerColor2: "color(a98-rgb 1 0 0)"
112112
dark:
113-
headerColor1: '#0000d0'
114-
headerColor2: 'rgb(255 246 140)'
113+
headerColor1: "#0000d0"
114+
headerColor2: "rgb(255 246 140)"
115115
```
116116

117117
![Example Light Mode Banner](images/example-light-mode-banner.png)
@@ -126,9 +126,9 @@ app:
126126
branding:
127127
theme:
128128
light:
129-
navigationIndicatorColor: '#be0000'
129+
navigationIndicatorColor: "#be0000"
130130
dark:
131-
navigationIndicatorColor: '#f4eea9'
131+
navigationIndicatorColor: "#f4eea9"
132132
```
133133

134134
![Example Light Mode Sidebar Indicator](images/example-sidebar-indicator-light.png)
@@ -162,3 +162,19 @@ app:
162162
If support is not configured, it would look as below.
163163

164164
![Example Support Not Configured](images/support-not-configured.png)
165+
166+
## Customizing Build info content in the user settings page
167+
168+
To customize the build info content in the user settings page, provide your content to the `buildInfo` field in the `app-config.yaml` file.
169+
170+
Example configurations:
171+
172+
```
173+
buildInfo:
174+
title: <Specify the title you want to display in the build info card.>
175+
card:
176+
TechDocs builder: 'local'
177+
Authentication provider: 'Github'
178+
RBAC: disabled
179+
full: true # If set to true, only the information specified in this configuration will be displayed. If set to false, the provided details will be shown along with the build versions.
180+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { test } from "@playwright/test";
2+
import { Common } from "../../utils/common";
3+
import { UIhelper } from "../../utils/ui-helper";
4+
import { UI_HELPER_ELEMENTS } from "../../support/pageObjects/global-obj";
5+
6+
test.describe("Test user settings info card", () => {
7+
let uiHelper: UIhelper;
8+
9+
test.beforeEach(async ({ page }) => {
10+
const common = new Common(page);
11+
await common.loginAsGuest();
12+
13+
uiHelper = new UIhelper(page);
14+
});
15+
16+
test("Check if customized build info is rendered", async ({ page }) => {
17+
await uiHelper.openSidebar("Home");
18+
page.getByText("Guest").click();
19+
await page.getByRole("menuitem", { name: "Settings" }).click();
20+
await uiHelper.verifyTextInSelector(
21+
UI_HELPER_ELEMENTS.MuiCardHeader,
22+
"RHDH Build info",
23+
);
24+
await uiHelper.verifyTextInSelector(
25+
UI_HELPER_ELEMENTS.MuiCard("RHDH Build info"),
26+
"TechDocs builder: local\nAuthentication provider: Github",
27+
);
28+
await page.getByTitle("Show more").click();
29+
await uiHelper.verifyTextInSelector(
30+
UI_HELPER_ELEMENTS.MuiCard("RHDH Build info"),
31+
"TechDocs builder: local\nAuthentication provider: Github\nRBAC: disabled",
32+
);
33+
});
34+
});

packages/app/config.d.ts

+10
Original file line numberDiff line numberDiff line change
@@ -187,4 +187,14 @@ export interface Config {
187187
* @visibility frontend
188188
*/
189189
includeTransitiveGroupOwnership?: boolean;
190+
191+
/**
192+
* Allows you to customize RHDH Metadata card
193+
* @deepVisibility frontend
194+
*/
195+
buildInfo?: {
196+
title: string;
197+
card: { [key: string]: string };
198+
full?: boolean;
199+
};
190200
}

packages/app/src/build-metadata.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"title": "RHDH Metadata",
3-
"card": [
4-
"RHDH Version: 1.6.0",
5-
"Backstage Version: 1.35.1",
6-
"Last Commit: repo @ 2025-01-31T14:15:06Z"
7-
]
3+
"card": {
4+
"RHDH Version": "1.6.0",
5+
"Backstage Version": "1.35.1",
6+
"Last Commit": "repo @ 2025-01-31T14:15:06Z"
7+
}
88
}
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,147 @@
1-
import { renderInTestApp } from '@backstage/test-utils';
1+
import { configApiRef } from '@backstage/core-plugin-api';
2+
import {
3+
mockApis,
4+
renderInTestApp,
5+
TestApiProvider,
6+
} from '@backstage/test-utils';
27

38
import { userEvent } from '@testing-library/user-event';
49

510
import { InfoCard } from './InfoCard';
611

712
describe('InfoCard', () => {
813
it('should render essential versions by default', async () => {
9-
const renderResult = await renderInTestApp(<InfoCard />);
14+
const mockConfig = mockApis.config({
15+
data: {
16+
buildInfo: {},
17+
},
18+
});
19+
const renderResult = await renderInTestApp(
20+
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
21+
<InfoCard />
22+
</TestApiProvider>,
23+
);
1024
expect(renderResult.getByText(/RHDH Version/)).toBeInTheDocument();
1125
expect(renderResult.getByText(/Backstage Version/)).toBeInTheDocument();
1226
});
1327

1428
it('should hide the build time by default and show it on click', async () => {
15-
const renderResult = await renderInTestApp(<InfoCard />);
29+
const mockConfig = mockApis.config({
30+
data: {
31+
buildInfo: {},
32+
},
33+
});
34+
const renderResult = await renderInTestApp(
35+
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
36+
<InfoCard />
37+
</TestApiProvider>,
38+
);
1639
expect(renderResult.queryByText(/Last Commit/)).toBeNull();
1740
await userEvent.click(renderResult.getByText(/RHDH Version/));
1841
expect(renderResult.getByText(/Last Commit/)).toBeInTheDocument();
1942
});
43+
44+
it('should render the customized values when build info is configured', async () => {
45+
const mockConfig = mockApis.config({
46+
data: {
47+
buildInfo: {
48+
title: 'RHDH Build info',
49+
card: {
50+
'TechDocs builder': 'local',
51+
'Authentication provider': 'Github',
52+
RBAC: 'disabled',
53+
},
54+
},
55+
},
56+
});
57+
const renderResult = await renderInTestApp(
58+
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
59+
<InfoCard />
60+
</TestApiProvider>,
61+
);
62+
expect(renderResult.queryByText(/TechDocs builder/)).toBeInTheDocument();
63+
expect(
64+
renderResult.queryByText(/Authentication provider/),
65+
).toBeInTheDocument();
66+
await userEvent.click(renderResult.getByText(/TechDocs builder/));
67+
expect(renderResult.getByText(/RBAC/)).toBeInTheDocument();
68+
});
69+
70+
it('should append the customized values along with RHDH versions when build info is configured with `full` set to false', async () => {
71+
const mockConfig = mockApis.config({
72+
data: {
73+
buildInfo: {
74+
title: 'RHDH Build info',
75+
card: {
76+
'TechDocs builder': 'local',
77+
'Authentication provider': 'Github',
78+
RBAC: 'disabled',
79+
},
80+
full: false,
81+
},
82+
},
83+
});
84+
const renderResult = await renderInTestApp(
85+
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
86+
<InfoCard />
87+
</TestApiProvider>,
88+
);
89+
expect(renderResult.queryByText(/TechDocs builder/)).toBeInTheDocument();
90+
expect(
91+
renderResult.queryByText(/Authentication provider/),
92+
).toBeInTheDocument();
93+
await userEvent.click(renderResult.getByText(/TechDocs builder/));
94+
expect(renderResult.getByText(/RBAC/)).toBeInTheDocument();
95+
expect(renderResult.queryByText(/Last Commit/)).toBeInTheDocument();
96+
expect(renderResult.queryByText(/RHDH Version/)).toBeInTheDocument();
97+
});
98+
99+
it('should display only the customized values when build info is configured with full set to true, without appending RHDH versions', async () => {
100+
const mockConfig = mockApis.config({
101+
data: {
102+
buildInfo: {
103+
title: 'RHDH Build info',
104+
card: {
105+
'TechDocs builder': 'local',
106+
'Authentication provider': 'Github',
107+
RBAC: 'disabled',
108+
},
109+
full: true,
110+
},
111+
},
112+
});
113+
const renderResult = await renderInTestApp(
114+
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
115+
<InfoCard />
116+
</TestApiProvider>,
117+
);
118+
expect(renderResult.queryByText(/TechDocs builder/)).toBeInTheDocument();
119+
expect(
120+
renderResult.queryByText(/Authentication provider/),
121+
).toBeInTheDocument();
122+
await userEvent.click(renderResult.getByText(/TechDocs builder/));
123+
expect(renderResult.getByText(/RBAC/)).toBeInTheDocument();
124+
expect(renderResult.queryByText(/Last Commit/)).not.toBeInTheDocument();
125+
});
126+
127+
it('should fallback to default json if the customized card value is empty', async () => {
128+
const mockConfig = mockApis.config({
129+
data: {
130+
buildInfo: {
131+
title: 'RHDH Build info',
132+
card: undefined,
133+
},
134+
},
135+
});
136+
const renderResult = await renderInTestApp(
137+
<TestApiProvider apis={[[configApiRef, mockConfig]]}>
138+
<InfoCard />
139+
</TestApiProvider>,
140+
);
141+
expect(
142+
renderResult.queryByText(/TechDocs builder/),
143+
).not.toBeInTheDocument();
144+
expect(renderResult.getByText(/RHDH Version/)).toBeInTheDocument();
145+
expect(renderResult.getByText(/Backstage Version/)).toBeInTheDocument();
146+
});
20147
});

packages/app/src/components/UserSettings/InfoCard.tsx

+28-12
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@ import {
44
InfoCard as BSInfoCard,
55
CopyTextButton,
66
} from '@backstage/core-components';
7+
import { configApiRef, useApi } from '@backstage/core-plugin-api';
78

89
import UnfoldLessIcon from '@mui/icons-material/UnfoldLess';
910
import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore';
1011
import IconButton from '@mui/material/IconButton';
1112
import Typography from '@mui/material/Typography';
1213

1314
import buildMetadata from '../../build-metadata.json';
15+
import { BuildInfo } from '../../types/types';
1416

1517
export const InfoCard = () => {
18+
const config = useApi(configApiRef);
19+
const buildInfo: BuildInfo | undefined = config.getOptional('buildInfo');
20+
1621
const [showBuildInformation, setShowBuildInformation] =
1722
React.useState<boolean>(
1823
() =>
@@ -32,24 +37,35 @@ export const InfoCard = () => {
3237
}
3338
};
3439

35-
let clipboardText = buildMetadata.title;
36-
if (buildMetadata.card?.length) {
40+
const title = buildInfo?.title ?? buildMetadata.title;
41+
42+
let clipboardText = title;
43+
const buildDetails = Object.entries(
44+
buildInfo?.full
45+
? (buildInfo?.card ?? buildMetadata?.card)
46+
: { ...buildInfo?.card, ...buildMetadata?.card },
47+
).map(([key, value]) => `${key}: ${value}`);
48+
if (buildDetails?.length) {
3749
clipboardText += '\n\n';
38-
buildMetadata.card.forEach(text => {
50+
buildDetails.forEach(text => {
3951
clipboardText += `${text}\n`;
4052
});
4153
}
4254

43-
const filteredCards = showBuildInformation
44-
? buildMetadata.card
45-
: buildMetadata.card.filter(
46-
text =>
47-
text.startsWith('RHDH Version') ||
48-
text.startsWith('Backstage Version'),
49-
);
55+
const filteredContent = () => {
56+
if (buildInfo?.card) {
57+
return buildDetails.slice(0, 2);
58+
}
59+
return buildDetails.filter(
60+
text =>
61+
text.startsWith('RHDH Version') || text.startsWith('Backstage Version'),
62+
);
63+
};
64+
65+
const filteredCards = showBuildInformation ? buildDetails : filteredContent();
5066
// Ensure that we show always some information
5167
const versionInfo =
52-
filteredCards.length > 0 ? filteredCards.join('\n') : buildMetadata.card[0];
68+
filteredCards.length > 0 ? filteredCards.join('\n') : buildDetails[0];
5369

5470
/**
5571
* Show all build information and automatically select them
@@ -80,7 +96,7 @@ export const InfoCard = () => {
8096

8197
return (
8298
<BSInfoCard
83-
title={buildMetadata.title}
99+
title={title}
84100
action={
85101
// This is a workaround to ensure that the buttons doesn't increase the header size.
86102
<div style={{ position: 'relative' }}>

packages/app/src/types/types.ts

+6
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,9 @@ export type LearningPathLink = {
66
minutes?: number;
77
paths?: number;
88
};
9+
10+
export type BuildInfo = {
11+
title: string;
12+
card: { [key: string]: string };
13+
full?: boolean;
14+
};

0 commit comments

Comments
 (0)