Merge pull request #7732 from tragiclifestories/gauge-color

Allow overriding colour in Gauge components
This commit is contained in:
Patrik Oldsberg
2021-11-10 19:07:41 +01:00
committed by GitHub
10 changed files with 162 additions and 43 deletions
+23
View File
@@ -0,0 +1,23 @@
---
'@backstage/core-components': patch
---
Add new way to override color selection to progress bar/gauge components.
`Gauge`, `LinearGauge` and `GaugeCard` all accept a `getColor` prop,
which is a function of the type:
```ts
export type GaugePropsGetColor = (args: {
palette: Palette;
value: number;
inverse?: boolean;
max?: number;
}) => string;
```
Return a standard CSS color string (e.g. "red", "#f02020"), and the gauge will
be set to that color.
If the prop is omitted, the default implementation is unchanged from previous
versions.
+33 -13
View File
@@ -7,6 +7,7 @@
import { ApiRef } from '@backstage/core-plugin-api';
import { BackstageIdentityApi } from '@backstage/core-plugin-api';
import { BackstagePalette } from '@backstage/theme';
import { BackstageTheme } from '@backstage/theme';
import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button';
import { CardHeaderProps } from '@material-ui/core/CardHeader';
@@ -100,7 +101,7 @@ export type BottomLinkProps = {
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function Breadcrumbs(props: Props_21): JSX.Element;
export function Breadcrumbs(props: Props_20): JSX.Element;
// @public (undocumented)
export type BreadcrumbsClickableTextClassKey = 'root';
@@ -157,7 +158,7 @@ export interface CodeSnippetProps {
// Warning: (ae-missing-release-tag) "Content" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function Content(props: PropsWithChildren<Props_14>): JSX.Element;
export function Content(props: PropsWithChildren<Props_13>): JSX.Element;
// Warning: (ae-forgotten-export) The symbol "ContentHeaderProps" needs to be exported by the entry point index.d.ts
//
@@ -368,10 +369,8 @@ export function FeatureCalloutCircular(
// @public (undocumented)
export type FiltersContainerClassKey = 'root' | 'title';
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function Gauge(props: Props_11): JSX.Element;
export function Gauge(props: GaugeProps): JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
//
@@ -384,6 +383,27 @@ export type GaugeCardClassKey = 'root';
// @public (undocumented)
export type GaugeClassKey = 'root' | 'overlay' | 'circle' | 'colorUnknown';
// @public (undocumented)
export type GaugeProps = {
value: number;
fractional?: boolean;
inverse?: boolean;
unit?: string;
max?: number;
getColor?: GaugePropsGetColor;
};
// @public (undocumented)
export type GaugePropsGetColor = (args: GaugePropsGetColorOptions) => string;
// @public (undocumented)
export type GaugePropsGetColorOptions = {
palette: BackstagePalette;
value: number;
inverse?: boolean;
max?: number;
};
// @public (undocumented)
export function GitHubIcon(props: IconComponentProps): JSX.Element;
@@ -393,7 +413,7 @@ export function GroupIcon(props: IconComponentProps): JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function Header(props: PropsWithChildren<Props_15>): JSX.Element;
export function Header(props: PropsWithChildren<Props_14>): JSX.Element;
// @public (undocumented)
export type HeaderClassKey =
@@ -484,7 +504,7 @@ export type IconLinkVerticalProps = {
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function InfoCard(props: Props_16): JSX.Element;
export function InfoCard(props: Props_15): JSX.Element;
// @public (undocumented)
export type InfoCardClassKey =
@@ -565,7 +585,7 @@ export type LifecycleClassKey = 'alpha' | 'beta';
// Warning: (ae-missing-release-tag) "LinearGauge" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function LinearGauge(props: Props_12): JSX.Element | null;
export function LinearGauge(props: Props_11): JSX.Element | null;
// Warning: (ae-missing-release-tag) "LinkType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -666,7 +686,7 @@ export type OverflowTooltipClassKey = 'container';
// Warning: (ae-missing-release-tag) "Page" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function Page(props: PropsWithChildren<Props_17>): JSX.Element;
export function Page(props: PropsWithChildren<Props_16>): JSX.Element;
// Warning: (ae-missing-release-tag) "PageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -771,7 +791,7 @@ export type SelectInputBaseClassKey = 'root' | 'input';
// Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function Sidebar(props: PropsWithChildren<Props_18>): JSX.Element;
export function Sidebar(props: PropsWithChildren<Props_17>): JSX.Element;
// Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -1961,7 +1981,7 @@ export const SidebarSpacer: React_2.ComponentType<
// Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function SignInPage(props: Props_19): JSX.Element;
export function SignInPage(props: Props_18): JSX.Element;
// Warning: (ae-missing-release-tag) "SignInPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -2050,7 +2070,7 @@ export function StatusWarning(props: PropsWithChildren<{}>): JSX.Element;
// Warning: (ae-missing-release-tag) "StructuredMetadataTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function StructuredMetadataTable(props: Props_13): JSX.Element;
export function StructuredMetadataTable(props: Props_12): JSX.Element;
// Warning: (ae-missing-release-tag) "StructuredMetadataTableListClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -2132,7 +2152,7 @@ export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root';
// Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function TabbedCard(props: PropsWithChildren<Props_20>): JSX.Element;
export function TabbedCard(props: PropsWithChildren<Props_19>): JSX.Element;
// Warning: (ae-missing-release-tag) "TabbedCardClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -53,3 +53,9 @@ export const AbsoluteProgress = () => (
<Gauge value={89.2} fractional={false} unit="m/s" />
</div>
);
export const StaticColor = () => (
<div style={containerStyle}>
<Gauge getColor={() => '#f0f'} value={0.5} />
</div>
);
@@ -56,16 +56,26 @@ describe('<Gauge />', () => {
};
it('colors the progress correctly', () => {
expect(getProgressColor(palette, 'Not a Number' as any)).toBe('#ddd');
expect(getProgressColor(palette, 10)).toBe(error);
expect(getProgressColor(palette, 50)).toBe(warning);
expect(getProgressColor(palette, 90)).toBe(ok);
expect(getProgressColor({ palette, value: 'Not a Number' as any })).toBe(
'#ddd',
);
expect(getProgressColor({ palette, value: 10 })).toBe(error);
expect(getProgressColor({ palette, value: 50 })).toBe(warning);
expect(getProgressColor({ palette, value: 90 })).toBe(ok);
});
it('colors the inverse progress correctly', () => {
expect(getProgressColor(palette, 'Not a Number' as any)).toBe('#ddd');
expect(getProgressColor(palette, 10, true)).toBe(ok);
expect(getProgressColor(palette, 50, true)).toBe(warning);
expect(getProgressColor(palette, 90, true)).toBe(error);
expect(
getProgressColor({
palette,
value: 'Not a Number' as any,
inverse: true,
}),
).toBe('#ddd');
expect(getProgressColor({ palette, value: 10, inverse: true })).toBe(ok);
expect(getProgressColor({ palette, value: 50, inverse: true })).toBe(
warning,
);
expect(getProgressColor({ palette, value: 90, inverse: true })).toBe(error);
});
});
@@ -15,7 +15,7 @@
*/
import { makeStyles, useTheme } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
import { BackstagePalette, BackstageTheme } from '@backstage/theme';
import { Circle } from 'rc-progress';
import React from 'react';
@@ -46,32 +46,45 @@ const useStyles = makeStyles<BackstageTheme>(
{ name: 'BackstageGauge' },
);
type Props = {
/** @public */
export type GaugeProps = {
value: number;
fractional?: boolean;
inverse?: boolean;
unit?: string;
max?: number;
getColor?: GaugePropsGetColor;
};
const defaultProps = {
/** @public */
export type GaugePropsGetColorOptions = {
palette: BackstagePalette;
value: number;
inverse?: boolean;
max?: number;
};
/** @public */
export type GaugePropsGetColor = (args: GaugePropsGetColorOptions) => string;
const defaultGaugeProps = {
fractional: true,
inverse: false,
unit: '%',
max: 100,
};
export function getProgressColor(
palette: BackstageTheme['palette'],
value: number,
inverse?: boolean,
max?: number,
) {
export const getProgressColor: GaugePropsGetColor = ({
palette,
value,
inverse,
max,
}) => {
if (isNaN(value)) {
return '#ddd';
}
const actualMax = max ? max : defaultProps.max;
const actualMax = max ? max : defaultGaugeProps.max;
const actualValue = inverse ? actualMax - value : value;
if (actualValue < actualMax / 3) {
@@ -81,14 +94,15 @@ export function getProgressColor(
}
return palette.status.ok;
}
};
/** @public */
export function Gauge(props: Props) {
export function Gauge(props: GaugeProps) {
const { getColor = getProgressColor } = props;
const classes = useStyles(props);
const theme = useTheme<BackstageTheme>();
const { palette } = useTheme<BackstageTheme>();
const { value, fractional, inverse, unit, max } = {
...defaultProps,
...defaultGaugeProps,
...props,
};
@@ -102,7 +116,7 @@ export function Gauge(props: Props) {
percent={asPercentage}
strokeWidth={12}
trailWidth={12}
strokeColor={getProgressColor(theme.palette, asActual, inverse, max)}
strokeColor={getColor({ palette, value: asActual, inverse, max })}
className={classes.circle}
/>
<div className={classes.overlay}>
@@ -97,3 +97,24 @@ export const LinkInFooter = () => (
</Grid>
</Wrapper>
);
export const StaticColor = () => (
<Wrapper>
<Grid item>
<GaugeCard getColor={() => '#f00'} title="Red" progress={0.5} />
</Grid>
<Grid item>
<GaugeCard getColor={() => '#0f0'} title="Green" progress={0.5} />
</Grid>
<Grid item>
<GaugeCard getColor={() => '#00f'} title="Blue" progress={0.5} />
</Grid>
<Grid item>
<GaugeCard
getColor={({ palette }) => palette.status.error}
title="palette.status.error"
progress={0.5}
/>
</Grid>
</Wrapper>
);
@@ -18,7 +18,7 @@ import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { InfoCard, InfoCardVariants } from '../../layout/InfoCard';
import { BottomLinkProps } from '../../layout/BottomLink';
import { Gauge } from './Gauge';
import { Gauge, GaugePropsGetColor } from './Gauge';
type Props = {
title: string;
@@ -28,6 +28,7 @@ type Props = {
progress: number;
inverse?: boolean;
deepLink?: BottomLinkProps;
getColor?: GaugePropsGetColor;
};
/** @public */
@@ -46,7 +47,14 @@ const useStyles = makeStyles(
/** @public */
export function GaugeCard(props: Props) {
const classes = useStyles(props);
const { title, subheader, progress, inverse, deepLink, variant } = props;
const { title, subheader, progress, inverse, deepLink, variant, getColor } =
props;
const gaugeProps = {
inverse,
getColor,
value: progress,
};
return (
<div className={classes.root}>
@@ -56,7 +64,7 @@ export function GaugeCard(props: Props) {
deepLink={deepLink}
variant={variant}
>
<Gauge value={progress} inverse={inverse} />
<Gauge {...gaugeProps} />
</InfoCard>
</div>
);
@@ -41,3 +41,9 @@ export const LowProgress = () => (
<LinearGauge value={0.2} />
</div>
);
export const StaticColor = () => (
<div style={containerStyle}>
<LinearGauge getColor={() => '#f0f'} value={0.5} />
</div>
);
@@ -20,18 +20,19 @@ import Tooltip from '@material-ui/core/Tooltip';
// @ts-ignore
import { Line } from 'rc-progress';
import { BackstageTheme } from '@backstage/theme';
import { getProgressColor } from './Gauge';
import { getProgressColor, GaugePropsGetColor } from './Gauge';
type Props = {
/**
* Progress value between 0.0 - 1.0.
*/
value: number;
getColor?: GaugePropsGetColor;
};
export function LinearGauge(props: Props) {
const { value } = props;
const theme = useTheme<BackstageTheme>();
const { value, getColor = getProgressColor } = props;
const { palette } = useTheme<BackstageTheme>();
if (isNaN(value)) {
return null;
}
@@ -39,7 +40,12 @@ export function LinearGauge(props: Props) {
if (percent > 100) {
percent = 100;
}
const strokeColor = getProgressColor(theme.palette, percent, false, 100);
const strokeColor = getColor({
palette,
value: percent,
inverse: false,
max: 100,
});
return (
<Tooltip title={`${percent}%`}>
<span>
@@ -17,5 +17,10 @@
export { GaugeCard } from './GaugeCard';
export type { GaugeCardClassKey } from './GaugeCard';
export { Gauge } from './Gauge';
export type {
GaugeProps,
GaugePropsGetColor,
GaugePropsGetColorOptions,
} from './Gauge';
export type { GaugeClassKey } from './Gauge';
export { LinearGauge } from './LinearGauge';