From b34f2665fd1bf53a7196680895b94d03bb808bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 15 Feb 2019 15:23:16 +0100 Subject: [PATCH] bar-gauge storybook --- .../components/BarGauge/BarGauge.story.tsx | 54 ++++++++++++ .../src/components/BarGauge/BarGauge.tsx | 88 +++++++++++++++---- .../grafana-ui/src/components/Gauge/Gauge.tsx | 35 +++----- packages/grafana-ui/src/types/index.ts | 1 + packages/grafana-ui/src/types/panel.ts | 11 --- packages/grafana-ui/src/types/threshold.ts | 5 ++ packages/grafana-ui/src/utils/index.ts | 1 + packages/grafana-ui/src/utils/thresholds.ts | 23 +++++ public/app/plugins/panel/bargauge/plugin.json | 1 + public/app/plugins/panel/bargauge/types.ts | 6 ++ 10 files changed, 174 insertions(+), 51 deletions(-) create mode 100644 packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx create mode 100644 packages/grafana-ui/src/types/threshold.ts create mode 100644 packages/grafana-ui/src/utils/thresholds.ts diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx new file mode 100644 index 00000000000..fa1ab4eaac1 --- /dev/null +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx @@ -0,0 +1,54 @@ +import { storiesOf } from '@storybook/react'; +import { number, text } from '@storybook/addon-knobs'; +import { BarGauge } from './BarGauge'; +import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; +import { renderComponentWithTheme } from '../../utils/storybook/withTheme'; + +const getKnobs = () => { + return { + value: number('value', 70), + minValue: number('minValue', 0), + maxValue: number('maxValue', 100), + threshold1Value: number('threshold1Value', 40), + threshold1Color: text('threshold1Color', 'orange'), + threshold2Value: number('threshold2Value', 60), + threshold2Color: text('threshold2Color', 'red'), + unit: text('unit', 'ms'), + decimals: number('decimals', 1), + }; +}; + +const BarGaugeStories = storiesOf('UI/BarGauge/BarGauge', module); + +BarGaugeStories.addDecorator(withCenteredStory); + +BarGaugeStories.add('Vertical, with basic thresholds', () => { + const { + value, + minValue, + maxValue, + threshold1Color, + threshold2Color, + threshold1Value, + threshold2Value, + unit, + decimals, + } = getKnobs(); + + return renderComponentWithTheme(BarGauge, { + width: 300, + height: 600, + value: value, + minValue: minValue, + maxValue: maxValue, + unit: unit, + prefix: '', + postfix: '', + decimals: decimals, + thresholds: [ + { index: 0, value: null, color: 'green' }, + { index: 1, value: threshold1Value, color: threshold1Color }, + { index: 1, value: threshold2Value, color: threshold2Color }, + ], + }); +}); diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx index 6b704148ee2..8f251c612f4 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx @@ -1,28 +1,35 @@ // Library import React, { PureComponent } from 'react'; +import tinycolor from 'tinycolor2'; // Utils -import { getValueFormat } from '../../utils'; +import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils'; // Types -import { Themeable, TimeSeriesValue } from '../../types'; +import { Themeable, TimeSeriesValue, Threshold, ValueMapping } from '../../types'; export interface Props extends Themeable { height: number; unit: string; width: number; + thresholds: Threshold[]; + valueMappings: ValueMapping[]; value: TimeSeriesValue; - prefix: string; - suffix: string; maxValue: number; minValue: number; + prefix?: string; + suffix?: string; + decimals?: number; } export class BarGauge extends PureComponent { - static defaultProps = { + static defaultProps: Partial = { maxValue: 100, minValue: 0, + value: 100, unit: 'none', + thresholds: [], + valueMappings: [], }; getNumericValue(): number { @@ -32,8 +39,45 @@ export class BarGauge extends PureComponent { return 0; } + getColors(): BarColors { + const { thresholds, theme, value } = this.props; + + const activeThreshold = getThresholdForValue(thresholds, value); + console.log(thresholds, value); + + if (activeThreshold !== null) { + const color = getColorFromHexRgbOrName(activeThreshold.color, theme.type); + + return { + value: color, + border: color, + bar: tinycolor(color) + .setAlpha(0.3) + .toRgbString(), + }; + } + + return { + value: getColorFromHexRgbOrName('gray', theme.type), + bar: getColorFromHexRgbOrName('gray', theme.type), + border: getColorFromHexRgbOrName('gray', theme.type), + }; + } + + getValueStyles(value: string, color: string) { + const { width } = this.props; + + const guess = width / value.length; + const fontSize = Math.max(guess, 14); + + return { + color: color, + fontSize: fontSize + 'px', + }; + } + render() { - const { height, width, maxValue, minValue, unit } = this.props; + const { height, width, maxValue, minValue, unit, decimals } = this.props; const numericValue = this.getNumericValue(); const barMaxHeight = height * 0.8; // 20% for value & name @@ -41,19 +85,31 @@ export class BarGauge extends PureComponent { const barHeight = valuePercent * barMaxHeight; const formatFunc = getValueFormat(unit); - const valueFormatted = formatFunc(numericValue); + const valueFormatted = formatFunc(numericValue, decimals); + const colors = this.getColors(); + + const containerStyles = { width: `${width}px`, height: `${height}px` }; + const valueStyles = this.getValueStyles(valueFormatted, colors.value); + const barStyles = { + height: `${barHeight}px`, + width: `${width}px`, + backgroundColor: colors.bar, + borderTop: `1px solid ${colors.border}`, + }; return ( -
-
{valueFormatted}
-
+
+
+ {valueFormatted} +
+
); } } + +interface BarColors { + value: string; + bar: string; + border: string; +} diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index a7435a56b3c..9f96ef74333 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -1,12 +1,12 @@ import React, { PureComponent } from 'react'; import $ from 'jquery'; -import { ValueMapping, Threshold, BasicGaugeColor, GrafanaThemeType } from '../../types'; +import { ValueMapping, Threshold, GrafanaThemeType } from '../../types'; import { getMappedValue } from '../../utils/valueMappings'; -import { getColorFromHexRgbOrName, getValueFormat } from '../../utils'; +import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils'; import { Themeable } from '../../index'; -type TimeSeriesValue = string | number | null; +type GaugeValue = string | number | null; export interface Props extends Themeable { decimals: number; @@ -30,7 +30,7 @@ const FONT_SCALE = 1; export class Gauge extends PureComponent { canvasElement: any; - static defaultProps = { + static defaultProps: Partial = { maxValue: 100, valueMappings: [], minValue: 0, @@ -41,7 +41,6 @@ export class Gauge extends PureComponent { thresholds: [], unit: 'none', stat: 'avg', - theme: GrafanaThemeType.Dark, }; componentDidMount() { @@ -52,7 +51,7 @@ export class Gauge extends PureComponent { this.draw(); } - formatValue(value: TimeSeriesValue) { + formatValue(value: GaugeValue) { const { decimals, valueMappings, prefix, suffix, unit } = this.props; if (isNaN(value as number)) { @@ -73,26 +72,16 @@ export class Gauge extends PureComponent { return `${prefix && prefix + ' '}${handleNoValueValue}${suffix && ' ' + suffix}`; } - getFontColor(value: TimeSeriesValue) { + getFontColor(value: GaugeValue): string { const { thresholds, theme } = this.props; - if (thresholds.length === 1) { - return getColorFromHexRgbOrName(thresholds[0].color, theme.type); + const activeThreshold = getThresholdForValue(thresholds, value); + + if (activeThreshold !== null) { + return getColorFromHexRgbOrName(activeThreshold.color, theme.type); } - const atThreshold = thresholds.filter(threshold => (value as number) === threshold.value)[0]; - if (atThreshold) { - return getColorFromHexRgbOrName(atThreshold.color, theme.type); - } - - const belowThreshold = thresholds.filter(threshold => (value as number) > threshold.value); - - if (belowThreshold.length > 0) { - const nearestThreshold = belowThreshold.sort((t1, t2) => t2.value - t1.value)[0]; - return getColorFromHexRgbOrName(nearestThreshold.color, theme.type); - } - - return BasicGaugeColor.Red; + return ''; } getFormattedThresholds() { @@ -199,5 +188,3 @@ export class Gauge extends PureComponent { ); } } - -export default Gauge; diff --git a/packages/grafana-ui/src/types/index.ts b/packages/grafana-ui/src/types/index.ts index 263b1de4287..b09d88bab4d 100644 --- a/packages/grafana-ui/src/types/index.ts +++ b/packages/grafana-ui/src/types/index.ts @@ -4,3 +4,4 @@ export * from './panel'; export * from './plugin'; export * from './datasource'; export * from './theme'; +export * from './threshold'; diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 4eda85f9a28..0cb3818cda5 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -38,17 +38,6 @@ export interface PanelMenuItem { subMenu?: PanelMenuItem[]; } -export interface Threshold { - index: number; - value: number; - color: string; -} - -export enum BasicGaugeColor { - Green = '#299c46', - Red = '#d44a3a', -} - export enum MappingType { ValueToText = 1, RangeToText = 2, diff --git a/packages/grafana-ui/src/types/threshold.ts b/packages/grafana-ui/src/types/threshold.ts new file mode 100644 index 00000000000..741ecd940bf --- /dev/null +++ b/packages/grafana-ui/src/types/threshold.ts @@ -0,0 +1,5 @@ +export interface Threshold { + index: number; + value: number; + color: string; +} diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index 52a8535f9e1..f467f2520dd 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -2,3 +2,4 @@ export * from './processTimeSeries'; export * from './valueFormats/valueFormats'; export * from './colors'; export * from './namedColorsPalette'; +export * from './thresholds'; diff --git a/packages/grafana-ui/src/utils/thresholds.ts b/packages/grafana-ui/src/utils/thresholds.ts new file mode 100644 index 00000000000..2fdca67194a --- /dev/null +++ b/packages/grafana-ui/src/utils/thresholds.ts @@ -0,0 +1,23 @@ +import { Threshold } from '../types'; + +export function getThresholdForValue( + thresholds: Threshold[], + value: number | null | string | undefined +): Threshold | null { + if (thresholds.length === 1) { + return thresholds[0]; + } + + const atThreshold = thresholds.filter(threshold => (value as number) === threshold.value)[0]; + if (atThreshold) { + return atThreshold; + } + + const belowThreshold = thresholds.filter(threshold => (value as number) > threshold.value); + if (belowThreshold.length > 0) { + const nearestThreshold = belowThreshold.sort((t1: Threshold, t2: Threshold) => t2.value - t1.value)[0]; + return nearestThreshold; + } + + return null; +} diff --git a/public/app/plugins/panel/bargauge/plugin.json b/public/app/plugins/panel/bargauge/plugin.json index 2c5cdc08a0b..4e77e046dcd 100644 --- a/public/app/plugins/panel/bargauge/plugin.json +++ b/public/app/plugins/panel/bargauge/plugin.json @@ -2,6 +2,7 @@ "type": "panel", "name": "Bar Gauge", "id": "bargauge", + "state": "alpha", "dataFormats": ["time_series"], diff --git a/public/app/plugins/panel/bargauge/types.ts b/public/app/plugins/panel/bargauge/types.ts index 0e3ee4c25cd..e0c7e1e8799 100644 --- a/public/app/plugins/panel/bargauge/types.ts +++ b/public/app/plugins/panel/bargauge/types.ts @@ -1,3 +1,5 @@ +import { Threshold, ValueMapping } from '@grafana/ui'; + export interface BarGaugeOptions { minValue: number; maxValue: number; @@ -5,6 +7,8 @@ export interface BarGaugeOptions { stat: string; suffix: string; unit: string; + valueMappings: ValueMapping[]; + thresholds: Threshold[]; } export const PanelDefaults: BarGaugeOptions = { @@ -14,4 +18,6 @@ export const PanelDefaults: BarGaugeOptions = { suffix: '', stat: 'avg', unit: 'none', + thresholds: [], + valueMappings: [], };