Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions workspaces/scorecard/.changeset/fancy-rice-rush.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@red-hat-developer-hub/backstage-plugin-scorecard-backend': minor
'@red-hat-developer-hub/backstage-plugin-scorecard-common': minor
'@red-hat-developer-hub/backstage-plugin-scorecard-node': minor
'@red-hat-developer-hub/backstage-plugin-scorecard': minor
---

Introduces custom threshold rule icons that can be configured in `app-config.yaml`.
8 changes: 8 additions & 0 deletions workspaces/scorecard/packages/app-legacy/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ import { getThemes } from '@red-hat-developer-hub/backstage-plugin-theme';
import {
ScorecardHomepageCard,
ScorecardPage,
ScorecardErrorStatusIcon,
ScorecardSuccessStatusIcon,
ScorecardWarningStatusIcon,
} from '@red-hat-developer-hub/backstage-plugin-scorecard';

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

const app = createApp({
apis,
icons: {
scorecardSuccessStatusIcon: ScorecardSuccessStatusIcon,
scorecardWarningStatusIcon: ScorecardWarningStatusIcon,
scorecardErrorStatusIcon: ScorecardErrorStatusIcon,
},
themes: getThemes(),
__experimentalTranslations: {
availableLanguages: ['en', 'de', 'es', 'fr', 'it', 'ja'],
Expand Down
2 changes: 2 additions & 0 deletions workspaces/scorecard/packages/app/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
} from '@red-hat-developer-hub/backstage-plugin-scorecard/alpha';
import { signInModule } from './modules/signIn';
import { navModule } from './modules/nav';
import { iconsModule } from './modules/icons';

/*
* app: Backstage app using the New Frontend System (NFS).
Expand All @@ -32,6 +33,7 @@ const app = createApp({
rhdhThemeModule,
scorecardCatalogModule,
scorecardTranslationsModule,
iconsModule,
signInModule,
navModule,
],
Expand Down
39 changes: 39 additions & 0 deletions workspaces/scorecard/packages/app/src/modules/icons/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { createFrontendModule } from '@backstage/frontend-plugin-api';
import { IconBundleBlueprint } from '@backstage/plugin-app-react';

import {
ScorecardErrorStatusIcon,
ScorecardSuccessStatusIcon,
ScorecardWarningStatusIcon,
} from '@red-hat-developer-hub/backstage-plugin-scorecard';

export const iconsModule = createFrontendModule({
pluginId: 'app',
extensions: [
IconBundleBlueprint.make({
params: {
icons: {
scorecardSuccessStatusIcon: ScorecardSuccessStatusIcon,
scorecardWarningStatusIcon: ScorecardWarningStatusIcon,
scorecardErrorStatusIcon: ScorecardErrorStatusIcon,
},
},
}),
],
});
47 changes: 44 additions & 3 deletions workspaces/scorecard/plugins/scorecard-backend/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,58 @@ export interface Config {
[metricName: string]: {
/** Threshold configuration for the metric */
thresholds?: {
/**
* Rules describe how metric values are categorized and how that category is presented in the UI.
* They are evaluated in order and the first matching rule is applied.
*/
rules?: Array<{
/**
* Threshold category key that a metric value is assigned to when this rule
* matches (for example `success`, `warning`, `error`, or a custom key).
*/
key: string;
/** Threshold expression - supports: >=, <=, >, <, ==, !=, - (range) */
/**
* Threshold expression that determines whether a metric value matches this
* rule. Supports:`>=`, `<=`, `>`, `<`, `==`, `!=`, `-` (range).
*
* @example `<= 10` - Metric value must be less than or equal to 10.
* @example `10-60` - Metric value must be between 10 and 60 (inclusive).
*/
expression: string;
/**
* Color for this threshold rule. Can be a theme palette path (e.g., 'error.main')
* or a direct color value (e.g., '#ADD8E6', 'blue', 'rgb(255,255,0)')
* Color configuration - supports multiple formats:
* - theme palette reference (`success.main` / `warning.main` / `error.main`)
* - HEX code (e.g. '#FFA500')
* - RGB/RGBA (e.g. 'rgb(255, 0, 0)')
*
* Threshold rules 'success', 'warning' and 'error' have default colors.
*/
color?: string;
/**
* Icon configuration - supports multiple formats:
* - Backstage system icons: 'kind:component', 'kind:api', etc.
* - Material Design icons: 'settings', 'home', 'build', etc.
* - SVG strings: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
* - URLs: 'https://example.com/icon.png', '/assets/icon.svg'
* - Data URIs: 'data:image/svg+xml;base64,...'
*
* Threshold rules 'success', 'warning' and 'error' have default icons.
*/
icon?: string;
}>;
};
/**
* Schedule for collecting this metric. If not set, the default hourly schedule is used.
*
* Default schedule:
* ```ts
* {
* frequency: { hours: 1 },
* timeout: { minutes: 15 },
* initialDelay: { minutes: 1 },
* }
* ```
*/
schedule?: SchedulerServiceTaskScheduleDefinitionConfig;
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Thresholds are evaluated in order and the **first matching** threshold rule is a
- **`key`**: The threshold category (e.g., `success`, `warning`, `error`, or custom keys)
- **`expression`**: The condition that determines if a metric value matches this threshold
- **`color`** (optional): The color to display for this threshold in the UI (see [Threshold Colors](#threshold-colors))
- **`icon`** (optional): The icon to display for this threshold in the UI (see [Threshold Icons](#threshold-icons))

## Threshold Configuration Options

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

**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.

## Threshold Icons

You can customize the icon displayed for each threshold rule in the scorecard UI by adding an `icon` property.

### Icon Configuration

Add an `icon` property to any threshold rule:

```yaml
scorecard:
plugins:
myDatasource:
myMetric:
thresholds:
rules:
- key: success
expression: '<10'
icon: scorecardSuccessStatusIcon
- key: warning
expression: '10-50'
icon: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
- key: error
expression: '>50'
icon: 'customIcon'
```

### Supported Icon Formats

The `icon` value is a string and can be one of:

- **Backstage System Icon**: `kind:component`, `kind:api`, your `customIcon` registered with Backstage
- **Material Design Icon**: `settings`, `home`, `build`
- **SVG String**: `<svg xmlns="http://www.w3.org/2000/svg">...</svg>`
- **Image URL**: `http(s)://...`, `/assets/icon.svg`
- **Data URI**: `data:image/svg+xml;base64,...`

> [!NOTE]
> 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.

For information on registering custom icons with Backstage, see [Adding Icons](https://backstage.io/docs/conf/user-interface/icons/#adding-icons).
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:

```yaml
dynamicPlugins:
rootDirectory: dynamic-plugins-root
frontend:
example.plugin-custom:
appIcons:
- name: customIcon
importName: CustomIcon
```

### Default icons

If no icon is specified for a threshold rule, the frontend uses default icons for standard threshold rule keys:

| Rule Key | Default icon constant | Material Design Icon |
| -------- | ---------------------------- | -------------------- |
| success | `scorecardSuccessStatusIcon` | CheckCircleOutline |
| warning | `scorecardWarningStatusIcon` | WarningAmber |
| error | `scorecardErrorStatusIcon` | DangerousOutlined |

**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.

## ThresholdEvaluator

The `ThresholdEvaluator` service processes threshold rules and determines which threshold a metric value matches.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,12 @@ describe('mergeEntityAndProviderThresholds', () => {
return {
rules: [
{ key: 'low', expression: '<10', color: 'success.main' },
{ key: 'high', expression: '10-20', color: '#FF0000' },
{
key: 'high',
expression: '10-20',
color: '#FF0000',
icon: 'scorecardWarningStatusIcon',
},
{ key: 'error', expression: '>20' },
],
};
Expand All @@ -237,7 +242,12 @@ describe('mergeEntityAndProviderThresholds', () => {
expect(result).toEqual({
rules: [
{ key: 'low', expression: '<10', color: 'success.main' },
{ key: 'high', expression: '10-60', color: '#FF0000' },
{
key: 'high',
expression: '10-60',
color: '#FF0000',
icon: 'scorecardWarningStatusIcon',
},
{ key: 'error', expression: '>60' },
],
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ export type ThresholdRule = {
key: string;
expression: string;
color?: string;
icon?: string;
};

// (No @packageDocumentation comment for this package)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,50 @@
* @public
*/
export type ThresholdRule = {
/**
* Threshold category key that a metric value is assigned to when this rule
* matches (for example `success`, `warning`, `error`, or a custom key).
*/
key: string;
/**
* Threshold expression that determines whether a metric value matches this
* rule. Supports:`>=`, `<=`, `>`, `<`, `==`, `!=`, `-` (range).
*
* @example `<= 10` - Metric value must be less than or equal to 10.
* @example `10-60` - Metric value must be between 10 and 60 (inclusive).
*/
expression: string;
/**
* Color configuration - supports multiple formats:
* - theme palette reference (`success.main` / `warning.main` / `error.main`)
* - HEX code (e.g. '#FFA500')
* - RGB/RGBA (e.g. 'rgb(255, 0, 0)')
*
* Threshold rules 'success', 'warning' and 'error' have default colors.
*/
color?: string;
/**
* Icon configuration - supports multiple formats:
* - Backstage system icons: 'kind:component', 'kind:api', etc.
* - Material Design icons: 'settings', 'home', 'build', etc.
* - SVG strings: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
* - URLs: 'https://example.com/icon.png', '/assets/icon.svg'
* - Data URIs: 'data:image/svg+xml;base64,...'
*
* Threshold rules 'success', 'warning' and 'error' have default icons.
*/
icon?: string;
};

/**
* Threshold configuration
* @public
*/
export type ThresholdConfig = {
/**
* Rules describe how metric values are categorized and how that category is presented in the UI.
* They are evaluated in order and the first matching rule is applied.
*/
rules: ThresholdRule[];
};

Expand Down
Loading
Loading