mirror of https://github.com/grafana/grafana.git
Stat: Add percent change color modes (#88205)
* Stat: Add percent change color modes * Apply color mode to percent change component * Remove todo * Create percent change color function and add tests * drew08t/stat-percent-change-color-modes/ maybe fix import error? --------- Co-authored-by: jev forsberg <jev.forsberg@grafana.com>
This commit is contained in:
parent
ed42119907
commit
14957953db
|
|
@ -552,6 +552,15 @@ export enum BigValueTextMode {
|
|||
ValueAndName = 'value_and_name',
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO docs
|
||||
*/
|
||||
export enum PercentChangeColorMode {
|
||||
Inverted = 'inverted',
|
||||
SameAsValue = 'same_as_value',
|
||||
Standard = 'standard',
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO -- should not be table specific!
|
||||
* TODO docs
|
||||
|
|
|
|||
|
|
@ -190,6 +190,9 @@ BigValueJustifyMode: "auto" | "center" @cuetsy(kind="enum")
|
|||
// TODO docs
|
||||
BigValueTextMode: "auto" | "value" | "value_and_name" | "name" | "none" @cuetsy(kind="enum",memberNames="Auto|Value|ValueAndName|Name|None")
|
||||
|
||||
// TODO docs
|
||||
PercentChangeColorMode: "standard" | "inverted" | "same_as_value" @cuetsy(kind="enum",memberNames="Standard|Inverted|SameAsValue")
|
||||
|
||||
// TODO -- should not be table specific!
|
||||
// TODO docs
|
||||
FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(kind="type")
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ export interface Options extends common.SingleStatBaseOptions {
|
|||
colorMode: common.BigValueColorMode;
|
||||
graphMode: common.BigValueGraphMode;
|
||||
justifyMode: common.BigValueJustifyMode;
|
||||
percentChangeColorMode: common.PercentChangeColorMode;
|
||||
showPercentChange: boolean;
|
||||
textMode: common.BigValueTextMode;
|
||||
wideLayout: boolean;
|
||||
|
|
@ -25,6 +26,7 @@ export const defaultOptions: Partial<Options> = {
|
|||
colorMode: common.BigValueColorMode.Value,
|
||||
graphMode: common.BigValueGraphMode.Area,
|
||||
justifyMode: common.BigValueJustifyMode.Auto,
|
||||
percentChangeColorMode: common.PercentChangeColorMode.Standard,
|
||||
showPercentChange: false,
|
||||
textMode: common.BigValueTextMode.Auto,
|
||||
wideLayout: true,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { cx } from '@emotion/css';
|
|||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { DisplayValue, DisplayValueAlignmentFactors, FieldSparkline } from '@grafana/data';
|
||||
import { VizTextDisplayOptions } from '@grafana/schema';
|
||||
import { PercentChangeColorMode, VizTextDisplayOptions } from '@grafana/schema';
|
||||
|
||||
import { Themeable2 } from '../../types';
|
||||
import { clearButtonStyles } from '../Button';
|
||||
|
|
@ -67,6 +67,8 @@ export interface Props extends Themeable2 {
|
|||
textMode?: BigValueTextMode;
|
||||
/** If true disables the tooltip */
|
||||
hasLinks?: boolean;
|
||||
/** Percent change color mode */
|
||||
percentChangeColorMode?: PercentChangeColorMode;
|
||||
|
||||
/**
|
||||
* If part of a series of stat panes, this is the total number.
|
||||
|
|
@ -94,6 +96,7 @@ export class BigValue extends PureComponent<Props> {
|
|||
const titleStyles = layout.getTitleStyles();
|
||||
const textValues = layout.textValues;
|
||||
const percentChange = this.props.value.percentChange;
|
||||
const percentChangeColorMode = this.props.percentChangeColorMode;
|
||||
const showPercentChange = percentChange != null && !Number.isNaN(percentChange);
|
||||
|
||||
// When there is an outer data link this tooltip will override the outer native tooltip
|
||||
|
|
@ -106,7 +109,10 @@ export class BigValue extends PureComponent<Props> {
|
|||
{textValues.title && <div style={titleStyles}>{textValues.title}</div>}
|
||||
<FormattedValueDisplay value={textValues} style={valueStyles} />
|
||||
{showPercentChange && (
|
||||
<PercentChange percentChange={percentChange} styles={layout.getPercentChangeStyles(percentChange)} />
|
||||
<PercentChange
|
||||
percentChange={percentChange}
|
||||
styles={layout.getPercentChangeStyles(percentChange, percentChangeColorMode, valueStyles)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{layout.renderChart()}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,17 @@
|
|||
import { CSSProperties } from 'react';
|
||||
|
||||
import { createTheme, FieldType } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { PercentChangeColorMode } from '@grafana/schema';
|
||||
|
||||
import { Props, BigValueColorMode, BigValueGraphMode, BigValueTextMode } from './BigValue';
|
||||
import { buildLayout, StackedWithChartLayout, StackedWithNoChartLayout, WideWithChartLayout } from './BigValueLayout';
|
||||
import {
|
||||
buildLayout,
|
||||
getPercentChangeColor,
|
||||
StackedWithChartLayout,
|
||||
StackedWithNoChartLayout,
|
||||
WideWithChartLayout,
|
||||
} from './BigValueLayout';
|
||||
|
||||
function getProps(propOverrides?: Partial<Props>): Props {
|
||||
const props: Props = {
|
||||
|
|
@ -29,6 +39,10 @@ function getProps(propOverrides?: Partial<Props>): Props {
|
|||
return props;
|
||||
}
|
||||
|
||||
const valueStyles: CSSProperties = {
|
||||
color: 'purple',
|
||||
};
|
||||
|
||||
describe('BigValueLayout', () => {
|
||||
describe('buildLayout', () => {
|
||||
it('should auto select to stacked layout', () => {
|
||||
|
|
@ -88,4 +102,70 @@ describe('BigValueLayout', () => {
|
|||
expect(layout).toBeInstanceOf(WideWithChartLayout);
|
||||
});
|
||||
});
|
||||
|
||||
describe('percentChangeColor', () => {
|
||||
const themeVisualizationColors = config.theme2.visualization;
|
||||
const red = themeVisualizationColors.getColorByName('red');
|
||||
const green = themeVisualizationColors.getColorByName('green');
|
||||
it('standard negative should be red', () => {
|
||||
const percentChange = -10;
|
||||
const color = getPercentChangeColor(
|
||||
percentChange,
|
||||
PercentChangeColorMode.Standard,
|
||||
valueStyles,
|
||||
themeVisualizationColors
|
||||
);
|
||||
expect(color).toBe(red);
|
||||
});
|
||||
it('standard positive should be green', () => {
|
||||
const percentChange = 10;
|
||||
const color = getPercentChangeColor(
|
||||
percentChange,
|
||||
PercentChangeColorMode.Standard,
|
||||
valueStyles,
|
||||
themeVisualizationColors
|
||||
);
|
||||
expect(color).toBe(green);
|
||||
});
|
||||
it('inverted negative should be green', () => {
|
||||
const percentChange = -10;
|
||||
const color = getPercentChangeColor(
|
||||
percentChange,
|
||||
PercentChangeColorMode.Inverted,
|
||||
valueStyles,
|
||||
themeVisualizationColors
|
||||
);
|
||||
expect(color).toBe(green);
|
||||
});
|
||||
it('inverted positive should be red', () => {
|
||||
const percentChange = 10;
|
||||
const color = getPercentChangeColor(
|
||||
percentChange,
|
||||
PercentChangeColorMode.Inverted,
|
||||
valueStyles,
|
||||
themeVisualizationColors
|
||||
);
|
||||
expect(color).toBe(red);
|
||||
});
|
||||
it('same as value negative should be purple', () => {
|
||||
const percentChange = -10;
|
||||
const color = getPercentChangeColor(
|
||||
percentChange,
|
||||
PercentChangeColorMode.SameAsValue,
|
||||
valueStyles,
|
||||
themeVisualizationColors
|
||||
);
|
||||
expect(color).toBe('purple');
|
||||
});
|
||||
it('same as value positive should be purple', () => {
|
||||
const percentChange = 10;
|
||||
const color = getPercentChangeColor(
|
||||
percentChange,
|
||||
PercentChangeColorMode.SameAsValue,
|
||||
valueStyles,
|
||||
themeVisualizationColors
|
||||
);
|
||||
expect(color).toBe('purple');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import React, { CSSProperties } from 'react';
|
||||
import tinycolor from 'tinycolor2';
|
||||
|
||||
import { formattedValueToString, DisplayValue, FieldConfig, FieldType } from '@grafana/data';
|
||||
import { GraphDrawStyle, GraphFieldConfig } from '@grafana/schema';
|
||||
import { formattedValueToString, DisplayValue, FieldConfig, FieldType, ThemeVisualizationColors } from '@grafana/data';
|
||||
import { GraphDrawStyle, GraphFieldConfig, PercentChangeColorMode } from '@grafana/schema';
|
||||
|
||||
import { getTextColorForAlphaBackground } from '../../utils';
|
||||
import { calculateFontSize } from '../../utils/measureText';
|
||||
|
|
@ -103,16 +103,17 @@ export abstract class BigValueLayout {
|
|||
return styles;
|
||||
}
|
||||
|
||||
getPercentChangeStyles(percentChange: number): PercentChangeStyles {
|
||||
getPercentChangeStyles(
|
||||
percentChange: number,
|
||||
percentChangeColorMode: PercentChangeColorMode | undefined,
|
||||
valueStyles: React.CSSProperties
|
||||
): PercentChangeStyles {
|
||||
const VALUE_TO_PERCENT_CHANGE_RATIO = 2.5;
|
||||
const valueContainerStyles = this.getValueAndTitleContainerStyles();
|
||||
const percentFontSize = Math.max(this.valueFontSize / VALUE_TO_PERCENT_CHANGE_RATIO, 12);
|
||||
let iconSize = Math.max(this.valueFontSize / 3, 10);
|
||||
|
||||
const color =
|
||||
percentChange > 0
|
||||
? this.props.theme.visualization.getColorByName('green')
|
||||
: this.props.theme.visualization.getColorByName('red');
|
||||
const themeVisualizationColors = this.props.theme.visualization;
|
||||
const color = getPercentChangeColor(percentChange, percentChangeColorMode, valueStyles, themeVisualizationColors);
|
||||
|
||||
const containerStyles: CSSProperties = {
|
||||
fontSize: percentFontSize,
|
||||
|
|
@ -597,3 +598,18 @@ export interface PercentChangeStyles {
|
|||
containerStyles: CSSProperties;
|
||||
iconSize: number;
|
||||
}
|
||||
|
||||
export function getPercentChangeColor(
|
||||
percentChange: number,
|
||||
percentChangeColorMode: PercentChangeColorMode | undefined,
|
||||
valueStyles: CSSProperties,
|
||||
themeVisualizationColors: ThemeVisualizationColors
|
||||
): string | undefined {
|
||||
if (percentChangeColorMode === PercentChangeColorMode.SameAsValue) {
|
||||
return valueStyles.color;
|
||||
} else {
|
||||
return percentChange * (percentChangeColorMode === PercentChangeColorMode.Inverted ? -1 : 1) > 0
|
||||
? themeVisualizationColors.getColorByName('green')
|
||||
: themeVisualizationColors.getColorByName('red');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ export class StatPanel extends PureComponent<PanelProps<Options>> {
|
|||
onClick={openMenu}
|
||||
className={targetClassName}
|
||||
disableWideLayout={!options.wideLayout}
|
||||
percentChangeColorMode={options.percentChangeColorMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
import { PanelPlugin } from '@grafana/data';
|
||||
import { BigValueColorMode, BigValueGraphMode, BigValueJustifyMode, BigValueTextMode } from '@grafana/schema';
|
||||
import {
|
||||
BigValueColorMode,
|
||||
BigValueGraphMode,
|
||||
BigValueJustifyMode,
|
||||
BigValueTextMode,
|
||||
PercentChangeColorMode,
|
||||
} from '@grafana/schema';
|
||||
import { commonOptionsBuilder, sharedSingleStatMigrationHandler } from '@grafana/ui';
|
||||
|
||||
import { statPanelChangedHandler } from './StatMigrations';
|
||||
|
|
@ -94,6 +100,20 @@ export const plugin = new PanelPlugin<Options>(StatPanel)
|
|||
defaultValue: defaultOptions.showPercentChange,
|
||||
category: mainCategory,
|
||||
showIf: (config) => !config.reduceOptions.values,
|
||||
})
|
||||
.addSelect({
|
||||
path: 'percentChangeColorMode',
|
||||
name: 'Percent change color mode',
|
||||
defaultValue: defaultOptions.percentChangeColorMode,
|
||||
category: mainCategory,
|
||||
settings: {
|
||||
options: [
|
||||
{ value: PercentChangeColorMode.Standard, label: 'Standard' },
|
||||
{ value: PercentChangeColorMode.Inverted, label: 'Inverted' },
|
||||
{ value: PercentChangeColorMode.SameAsValue, label: 'Same as Value' },
|
||||
],
|
||||
},
|
||||
showIf: (config) => config.showPercentChange,
|
||||
});
|
||||
})
|
||||
.setNoPadding()
|
||||
|
|
|
|||
|
|
@ -27,12 +27,13 @@ composableKinds: PanelCfg: {
|
|||
schema: {
|
||||
Options: {
|
||||
common.SingleStatBaseOptions
|
||||
graphMode: common.BigValueGraphMode & (*"area" | _)
|
||||
colorMode: common.BigValueColorMode & (*"value" | _)
|
||||
justifyMode: common.BigValueJustifyMode & (*"auto" | _)
|
||||
textMode: common.BigValueTextMode & (*"auto" | _)
|
||||
wideLayout: bool | *true
|
||||
showPercentChange: bool | *false
|
||||
graphMode: common.BigValueGraphMode & (*"area" | _)
|
||||
colorMode: common.BigValueColorMode & (*"value" | _)
|
||||
justifyMode: common.BigValueJustifyMode & (*"auto" | _)
|
||||
textMode: common.BigValueTextMode & (*"auto" | _)
|
||||
wideLayout: bool | *true
|
||||
showPercentChange: bool | *false
|
||||
percentChangeColorMode: common.PercentChangeColorMode & (*"standard" | _)
|
||||
} @cuetsy(kind="interface")
|
||||
}
|
||||
}]
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ export interface Options extends common.SingleStatBaseOptions {
|
|||
colorMode: common.BigValueColorMode;
|
||||
graphMode: common.BigValueGraphMode;
|
||||
justifyMode: common.BigValueJustifyMode;
|
||||
percentChangeColorMode: common.PercentChangeColorMode;
|
||||
showPercentChange: boolean;
|
||||
textMode: common.BigValueTextMode;
|
||||
wideLayout: boolean;
|
||||
|
|
@ -23,6 +24,7 @@ export const defaultOptions: Partial<Options> = {
|
|||
colorMode: common.BigValueColorMode.Value,
|
||||
graphMode: common.BigValueGraphMode.Area,
|
||||
justifyMode: common.BigValueJustifyMode.Auto,
|
||||
percentChangeColorMode: common.PercentChangeColorMode.Standard,
|
||||
showPercentChange: false,
|
||||
textMode: common.BigValueTextMode.Auto,
|
||||
wideLayout: true,
|
||||
|
|
|
|||
Loading…
Reference in New Issue