Commit b34f2665 by Torkel Ödegaard

bar-gauge storybook

parent 843f8b04
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 },
],
});
});
// Library // Library
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import tinycolor from 'tinycolor2';
// Utils // Utils
import { getValueFormat } from '../../utils'; import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils';
// Types // Types
import { Themeable, TimeSeriesValue } from '../../types'; import { Themeable, TimeSeriesValue, Threshold, ValueMapping } from '../../types';
export interface Props extends Themeable { export interface Props extends Themeable {
height: number; height: number;
unit: string; unit: string;
width: number; width: number;
thresholds: Threshold[];
valueMappings: ValueMapping[];
value: TimeSeriesValue; value: TimeSeriesValue;
prefix: string;
suffix: string;
maxValue: number; maxValue: number;
minValue: number; minValue: number;
prefix?: string;
suffix?: string;
decimals?: number;
} }
export class BarGauge extends PureComponent<Props> { export class BarGauge extends PureComponent<Props> {
static defaultProps = { static defaultProps: Partial<Props> = {
maxValue: 100, maxValue: 100,
minValue: 0, minValue: 0,
value: 100,
unit: 'none', unit: 'none',
thresholds: [],
valueMappings: [],
}; };
getNumericValue(): number { getNumericValue(): number {
...@@ -32,8 +39,45 @@ export class BarGauge extends PureComponent<Props> { ...@@ -32,8 +39,45 @@ export class BarGauge extends PureComponent<Props> {
return 0; 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() { render() {
const { height, width, maxValue, minValue, unit } = this.props; const { height, width, maxValue, minValue, unit, decimals } = this.props;
const numericValue = this.getNumericValue(); const numericValue = this.getNumericValue();
const barMaxHeight = height * 0.8; // 20% for value & name const barMaxHeight = height * 0.8; // 20% for value & name
...@@ -41,19 +85,31 @@ export class BarGauge extends PureComponent<Props> { ...@@ -41,19 +85,31 @@ export class BarGauge extends PureComponent<Props> {
const barHeight = valuePercent * barMaxHeight; const barHeight = valuePercent * barMaxHeight;
const formatFunc = getValueFormat(unit); 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 ( return (
<div className="bar-gauge" style={{ width: `${width}px`, height: `${height}px` }}> <div className="bar-gauge" style={containerStyles}>
<div className="bar-gauge__value">{valueFormatted}</div> <div className="bar-gauge__value" style={valueStyles}>
<div {valueFormatted}
style={{ </div>
height: `${barHeight}px`, <div style={barStyles} />
width: `${width}px`,
backgroundColor: 'rgba(200,0,0,0.3)',
}}
/>
</div> </div>
); );
} }
} }
interface BarColors {
value: string;
bar: string;
border: string;
}
import React, { PureComponent } from 'react'; import React, { PureComponent } from 'react';
import $ from 'jquery'; import $ from 'jquery';
import { ValueMapping, Threshold, BasicGaugeColor, GrafanaThemeType } from '../../types'; import { ValueMapping, Threshold, GrafanaThemeType } from '../../types';
import { getMappedValue } from '../../utils/valueMappings'; import { getMappedValue } from '../../utils/valueMappings';
import { getColorFromHexRgbOrName, getValueFormat } from '../../utils'; import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils';
import { Themeable } from '../../index'; import { Themeable } from '../../index';
type TimeSeriesValue = string | number | null; type GaugeValue = string | number | null;
export interface Props extends Themeable { export interface Props extends Themeable {
decimals: number; decimals: number;
...@@ -30,7 +30,7 @@ const FONT_SCALE = 1; ...@@ -30,7 +30,7 @@ const FONT_SCALE = 1;
export class Gauge extends PureComponent<Props> { export class Gauge extends PureComponent<Props> {
canvasElement: any; canvasElement: any;
static defaultProps = { static defaultProps: Partial<Props> = {
maxValue: 100, maxValue: 100,
valueMappings: [], valueMappings: [],
minValue: 0, minValue: 0,
...@@ -41,7 +41,6 @@ export class Gauge extends PureComponent<Props> { ...@@ -41,7 +41,6 @@ export class Gauge extends PureComponent<Props> {
thresholds: [], thresholds: [],
unit: 'none', unit: 'none',
stat: 'avg', stat: 'avg',
theme: GrafanaThemeType.Dark,
}; };
componentDidMount() { componentDidMount() {
...@@ -52,7 +51,7 @@ export class Gauge extends PureComponent<Props> { ...@@ -52,7 +51,7 @@ export class Gauge extends PureComponent<Props> {
this.draw(); this.draw();
} }
formatValue(value: TimeSeriesValue) { formatValue(value: GaugeValue) {
const { decimals, valueMappings, prefix, suffix, unit } = this.props; const { decimals, valueMappings, prefix, suffix, unit } = this.props;
if (isNaN(value as number)) { if (isNaN(value as number)) {
...@@ -73,26 +72,16 @@ export class Gauge extends PureComponent<Props> { ...@@ -73,26 +72,16 @@ export class Gauge extends PureComponent<Props> {
return `${prefix && prefix + ' '}${handleNoValueValue}${suffix && ' ' + suffix}`; return `${prefix && prefix + ' '}${handleNoValueValue}${suffix && ' ' + suffix}`;
} }
getFontColor(value: TimeSeriesValue) { getFontColor(value: GaugeValue): string {
const { thresholds, theme } = this.props; const { thresholds, theme } = this.props;
if (thresholds.length === 1) { const activeThreshold = getThresholdForValue(thresholds, value);
return getColorFromHexRgbOrName(thresholds[0].color, theme.type);
}
const atThreshold = thresholds.filter(threshold => (value as number) === threshold.value)[0]; if (activeThreshold !== null) {
if (atThreshold) { return getColorFromHexRgbOrName(activeThreshold.color, theme.type);
return getColorFromHexRgbOrName(atThreshold.color, theme.type);
} }
const belowThreshold = thresholds.filter(threshold => (value as number) > threshold.value); return '';
if (belowThreshold.length > 0) {
const nearestThreshold = belowThreshold.sort((t1, t2) => t2.value - t1.value)[0];
return getColorFromHexRgbOrName(nearestThreshold.color, theme.type);
}
return BasicGaugeColor.Red;
} }
getFormattedThresholds() { getFormattedThresholds() {
...@@ -199,5 +188,3 @@ export class Gauge extends PureComponent<Props> { ...@@ -199,5 +188,3 @@ export class Gauge extends PureComponent<Props> {
); );
} }
} }
export default Gauge;
...@@ -4,3 +4,4 @@ export * from './panel'; ...@@ -4,3 +4,4 @@ export * from './panel';
export * from './plugin'; export * from './plugin';
export * from './datasource'; export * from './datasource';
export * from './theme'; export * from './theme';
export * from './threshold';
...@@ -38,17 +38,6 @@ export interface PanelMenuItem { ...@@ -38,17 +38,6 @@ export interface PanelMenuItem {
subMenu?: PanelMenuItem[]; subMenu?: PanelMenuItem[];
} }
export interface Threshold {
index: number;
value: number;
color: string;
}
export enum BasicGaugeColor {
Green = '#299c46',
Red = '#d44a3a',
}
export enum MappingType { export enum MappingType {
ValueToText = 1, ValueToText = 1,
RangeToText = 2, RangeToText = 2,
......
export interface Threshold {
index: number;
value: number;
color: string;
}
...@@ -2,3 +2,4 @@ export * from './processTimeSeries'; ...@@ -2,3 +2,4 @@ export * from './processTimeSeries';
export * from './valueFormats/valueFormats'; export * from './valueFormats/valueFormats';
export * from './colors'; export * from './colors';
export * from './namedColorsPalette'; export * from './namedColorsPalette';
export * from './thresholds';
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;
}
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
"type": "panel", "type": "panel",
"name": "Bar Gauge", "name": "Bar Gauge",
"id": "bargauge", "id": "bargauge",
"state": "alpha",
"dataFormats": ["time_series"], "dataFormats": ["time_series"],
......
import { Threshold, ValueMapping } from '@grafana/ui';
export interface BarGaugeOptions { export interface BarGaugeOptions {
minValue: number; minValue: number;
maxValue: number; maxValue: number;
...@@ -5,6 +7,8 @@ export interface BarGaugeOptions { ...@@ -5,6 +7,8 @@ export interface BarGaugeOptions {
stat: string; stat: string;
suffix: string; suffix: string;
unit: string; unit: string;
valueMappings: ValueMapping[];
thresholds: Threshold[];
} }
export const PanelDefaults: BarGaugeOptions = { export const PanelDefaults: BarGaugeOptions = {
...@@ -14,4 +18,6 @@ export const PanelDefaults: BarGaugeOptions = { ...@@ -14,4 +18,6 @@ export const PanelDefaults: BarGaugeOptions = {
suffix: '', suffix: '',
stat: 'avg', stat: 'avg',
unit: 'none', unit: 'none',
thresholds: [],
valueMappings: [],
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment