Skip to content

Commit 55226c2

Browse files
authored
feat(scorecard): Customize icons (#2641)
* Introduce icons Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com> * Fix data URI Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com> * Introduce IconImage Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com> * Fix safari Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com> * Revert "Fix safari" This reverts commit 3954388. * Revert "Introduce IconImage" This reverts commit 1ad89a1. * Add information about icon color Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com> * Fix tests after merge Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com> --------- Signed-off-by: Dominika Zemanovicova <dzemanov@redhat.com>
1 parent 8f2c7f2 commit 55226c2

26 files changed

Lines changed: 756 additions & 291 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-scorecard-backend': minor
3+
'@red-hat-developer-hub/backstage-plugin-scorecard-common': minor
4+
'@red-hat-developer-hub/backstage-plugin-scorecard-node': minor
5+
'@red-hat-developer-hub/backstage-plugin-scorecard': minor
6+
---
7+
8+
Introduces custom threshold rule icons that can be configured in `app-config.yaml`.

workspaces/scorecard/packages/app-legacy/src/App.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ import { getThemes } from '@red-hat-developer-hub/backstage-plugin-theme';
5757
import {
5858
ScorecardHomepageCard,
5959
ScorecardPage,
60+
ScorecardErrorStatusIcon,
61+
ScorecardSuccessStatusIcon,
62+
ScorecardWarningStatusIcon,
6063
} from '@red-hat-developer-hub/backstage-plugin-scorecard';
6164

6265
import { ScalprumContext, ScalprumState } from '@scalprum/react-core';
@@ -274,6 +277,11 @@ const scalprumState: ScalprumState = {
274277

275278
const app = createApp({
276279
apis,
280+
icons: {
281+
scorecardSuccessStatusIcon: ScorecardSuccessStatusIcon,
282+
scorecardWarningStatusIcon: ScorecardWarningStatusIcon,
283+
scorecardErrorStatusIcon: ScorecardErrorStatusIcon,
284+
},
277285
themes: getThemes(),
278286
__experimentalTranslations: {
279287
availableLanguages: ['en', 'de', 'es', 'fr', 'it', 'ja'],

workspaces/scorecard/packages/app/src/App.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
} from '@red-hat-developer-hub/backstage-plugin-scorecard/alpha';
2424
import { signInModule } from './modules/signIn';
2525
import { navModule } from './modules/nav';
26+
import { iconsModule } from './modules/icons';
2627

2728
/*
2829
* app: Backstage app using the New Frontend System (NFS).
@@ -32,6 +33,7 @@ const app = createApp({
3233
rhdhThemeModule,
3334
scorecardCatalogModule,
3435
scorecardTranslationsModule,
36+
iconsModule,
3537
signInModule,
3638
navModule,
3739
],
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright Red Hat, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { createFrontendModule } from '@backstage/frontend-plugin-api';
18+
import { IconBundleBlueprint } from '@backstage/plugin-app-react';
19+
20+
import {
21+
ScorecardErrorStatusIcon,
22+
ScorecardSuccessStatusIcon,
23+
ScorecardWarningStatusIcon,
24+
} from '@red-hat-developer-hub/backstage-plugin-scorecard';
25+
26+
export const iconsModule = createFrontendModule({
27+
pluginId: 'app',
28+
extensions: [
29+
IconBundleBlueprint.make({
30+
params: {
31+
icons: {
32+
scorecardSuccessStatusIcon: ScorecardSuccessStatusIcon,
33+
scorecardWarningStatusIcon: ScorecardWarningStatusIcon,
34+
scorecardErrorStatusIcon: ScorecardErrorStatusIcon,
35+
},
36+
},
37+
}),
38+
],
39+
});

workspaces/scorecard/plugins/scorecard-backend/config.d.ts

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,58 @@ export interface Config {
4343
[metricName: string]: {
4444
/** Threshold configuration for the metric */
4545
thresholds?: {
46+
/**
47+
* Rules describe how metric values are categorized and how that category is presented in the UI.
48+
* They are evaluated in order and the first matching rule is applied.
49+
*/
4650
rules?: Array<{
51+
/**
52+
* Threshold category key that a metric value is assigned to when this rule
53+
* matches (for example `success`, `warning`, `error`, or a custom key).
54+
*/
4755
key: string;
48-
/** Threshold expression - supports: >=, <=, >, <, ==, !=, - (range) */
56+
/**
57+
* Threshold expression that determines whether a metric value matches this
58+
* rule. Supports:`>=`, `<=`, `>`, `<`, `==`, `!=`, `-` (range).
59+
*
60+
* @example `<= 10` - Metric value must be less than or equal to 10.
61+
* @example `10-60` - Metric value must be between 10 and 60 (inclusive).
62+
*/
4963
expression: string;
5064
/**
51-
* Color for this threshold rule. Can be a theme palette path (e.g., 'error.main')
52-
* or a direct color value (e.g., '#ADD8E6', 'blue', 'rgb(255,255,0)')
65+
* Color configuration - supports multiple formats:
66+
* - theme palette reference (`success.main` / `warning.main` / `error.main`)
67+
* - HEX code (e.g. '#FFA500')
68+
* - RGB/RGBA (e.g. 'rgb(255, 0, 0)')
69+
*
70+
* Threshold rules 'success', 'warning' and 'error' have default colors.
5371
*/
5472
color?: string;
73+
/**
74+
* Icon configuration - supports multiple formats:
75+
* - Backstage system icons: 'kind:component', 'kind:api', etc.
76+
* - Material Design icons: 'settings', 'home', 'build', etc.
77+
* - SVG strings: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
78+
* - URLs: 'https://example.com/icon.png', '/assets/icon.svg'
79+
* - Data URIs: 'data:image/svg+xml;base64,...'
80+
*
81+
* Threshold rules 'success', 'warning' and 'error' have default icons.
82+
*/
83+
icon?: string;
5584
}>;
5685
};
86+
/**
87+
* Schedule for collecting this metric. If not set, the default hourly schedule is used.
88+
*
89+
* Default schedule:
90+
* ```ts
91+
* {
92+
* frequency: { hours: 1 },
93+
* timeout: { minutes: 15 },
94+
* initialDelay: { minutes: 1 },
95+
* }
96+
* ```
97+
*/
5798
schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
5899
};
59100
};

workspaces/scorecard/plugins/scorecard-backend/docs/thresholds.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Thresholds are evaluated in order and the **first matching** threshold rule is a
99
- **`key`**: The threshold category (e.g., `success`, `warning`, `error`, or custom keys)
1010
- **`expression`**: The condition that determines if a metric value matches this threshold
1111
- **`color`** (optional): The color to display for this threshold in the UI (see [Threshold Colors](#threshold-colors))
12+
- **`icon`** (optional): The icon to display for this threshold in the UI (see [Threshold Icons](#threshold-icons))
1213

1314
## Threshold Configuration Options
1415

@@ -287,6 +288,70 @@ If no color is specified for a threshold rule, frontend will use these default c
287288

288289
**Important:** Custom threshold keys (not `success`, `warning`, or `error`) **must** specify a `color` property. The configuration will fail validation if a custom key is used without a color. This requirement ensures that all thresholds can be properly visualized in the UI.
289290

291+
## Threshold Icons
292+
293+
You can customize the icon displayed for each threshold rule in the scorecard UI by adding an `icon` property.
294+
295+
### Icon Configuration
296+
297+
Add an `icon` property to any threshold rule:
298+
299+
```yaml
300+
scorecard:
301+
plugins:
302+
myDatasource:
303+
myMetric:
304+
thresholds:
305+
rules:
306+
- key: success
307+
expression: '<10'
308+
icon: scorecardSuccessStatusIcon
309+
- key: warning
310+
expression: '10-50'
311+
icon: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
312+
- key: error
313+
expression: '>50'
314+
icon: 'customIcon'
315+
```
316+
317+
### Supported Icon Formats
318+
319+
The `icon` value is a string and can be one of:
320+
321+
- **Backstage System Icon**: `kind:component`, `kind:api`, your `customIcon` registered with Backstage
322+
- **Material Design Icon**: `settings`, `home`, `build`
323+
- **SVG String**: `<svg xmlns="http://www.w3.org/2000/svg">...</svg>`
324+
- **Image URL**: `http(s)://...`, `/assets/icon.svg`
325+
- **Data URI**: `data:image/svg+xml;base64,...`
326+
327+
> [!NOTE]
328+
> SVG String, Image URL, and Data URI icons are treated as images and do not inherit the threshold status color. You must define the color within the icon itself.
329+
330+
For information on registering custom icons with Backstage, see [Adding Icons](https://backstage.io/docs/conf/user-interface/icons/#adding-icons).
331+
To use your custom Backstage System icons in RHDH for Scorecard, you will need to add `appIcons` key to the plugin that is exporting them:
332+
333+
```yaml
334+
dynamicPlugins:
335+
rootDirectory: dynamic-plugins-root
336+
frontend:
337+
example.plugin-custom:
338+
appIcons:
339+
- name: customIcon
340+
importName: CustomIcon
341+
```
342+
343+
### Default icons
344+
345+
If no icon is specified for a threshold rule, the frontend uses default icons for standard threshold rule keys:
346+
347+
| Rule Key | Default icon constant | Material Design Icon |
348+
| -------- | ---------------------------- | -------------------- |
349+
| success | `scorecardSuccessStatusIcon` | CheckCircleOutline |
350+
| warning | `scorecardWarningStatusIcon` | WarningAmber |
351+
| error | `scorecardErrorStatusIcon` | DangerousOutlined |
352+
353+
**Important:** Custom threshold keys (not `success`, `warning`, or `error`) **must** specify an `icon` property. The configuration will fail validation if a custom key is used without an icon. This requirement ensures that all thresholds can be properly visualized in the UI.
354+
290355
## ThresholdEvaluator
291356

292357
The `ThresholdEvaluator` service processes threshold rules and determines which threshold a metric value matches.

workspaces/scorecard/plugins/scorecard-backend/src/utils/mergeEntityAndProviderThresholds.test.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,12 @@ describe('mergeEntityAndProviderThresholds', () => {
215215
return {
216216
rules: [
217217
{ key: 'low', expression: '<10', color: 'success.main' },
218-
{ key: 'high', expression: '10-20', color: '#FF0000' },
218+
{
219+
key: 'high',
220+
expression: '10-20',
221+
color: '#FF0000',
222+
icon: 'scorecardWarningStatusIcon',
223+
},
219224
{ key: 'error', expression: '>20' },
220225
],
221226
};
@@ -237,7 +242,12 @@ describe('mergeEntityAndProviderThresholds', () => {
237242
expect(result).toEqual({
238243
rules: [
239244
{ key: 'low', expression: '<10', color: 'success.main' },
240-
{ key: 'high', expression: '10-60', color: '#FF0000' },
245+
{
246+
key: 'high',
247+
expression: '10-60',
248+
color: '#FF0000',
249+
icon: 'scorecardWarningStatusIcon',
250+
},
241251
{ key: 'error', expression: '>60' },
242252
],
243253
});

workspaces/scorecard/plugins/scorecard-common/report.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export type ThresholdRule = {
150150
key: string;
151151
expression: string;
152152
color?: string;
153+
icon?: string;
153154
};
154155

155156
// (No @packageDocumentation comment for this package)

workspaces/scorecard/plugins/scorecard-common/src/types/threshold.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,50 @@
1919
* @public
2020
*/
2121
export type ThresholdRule = {
22+
/**
23+
* Threshold category key that a metric value is assigned to when this rule
24+
* matches (for example `success`, `warning`, `error`, or a custom key).
25+
*/
2226
key: string;
27+
/**
28+
* Threshold expression that determines whether a metric value matches this
29+
* rule. Supports:`>=`, `<=`, `>`, `<`, `==`, `!=`, `-` (range).
30+
*
31+
* @example `<= 10` - Metric value must be less than or equal to 10.
32+
* @example `10-60` - Metric value must be between 10 and 60 (inclusive).
33+
*/
2334
expression: string;
35+
/**
36+
* Color configuration - supports multiple formats:
37+
* - theme palette reference (`success.main` / `warning.main` / `error.main`)
38+
* - HEX code (e.g. '#FFA500')
39+
* - RGB/RGBA (e.g. 'rgb(255, 0, 0)')
40+
*
41+
* Threshold rules 'success', 'warning' and 'error' have default colors.
42+
*/
2443
color?: string;
44+
/**
45+
* Icon configuration - supports multiple formats:
46+
* - Backstage system icons: 'kind:component', 'kind:api', etc.
47+
* - Material Design icons: 'settings', 'home', 'build', etc.
48+
* - SVG strings: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
49+
* - URLs: 'https://example.com/icon.png', '/assets/icon.svg'
50+
* - Data URIs: 'data:image/svg+xml;base64,...'
51+
*
52+
* Threshold rules 'success', 'warning' and 'error' have default icons.
53+
*/
54+
icon?: string;
2555
};
2656

2757
/**
2858
* Threshold configuration
2959
* @public
3060
*/
3161
export type ThresholdConfig = {
62+
/**
63+
* Rules describe how metric values are categorized and how that category is presented in the UI.
64+
* They are evaluated in order and the first matching rule is applied.
65+
*/
3266
rules: ThresholdRule[];
3367
};
3468

0 commit comments

Comments
 (0)