diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx index 3165329c372..8fa0b2846a5 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { BarGauge, Props } from './BarGauge'; +import { VizOrientation } from '../../types'; import { getTheme } from '../../themes'; jest.mock('jquery', () => ({ @@ -21,7 +22,7 @@ const setup = (propOverrides?: object) => { value: 25, decimals: 0, theme: getTheme(), - orientation: 'horizontal', + orientation: VizOrientation.Horizontal, }; Object.assign(props, propOverrides); @@ -39,7 +40,7 @@ describe('Get font color', () => { it('should get first threshold color when only one threshold', () => { const { instance } = setup({ thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }] }); - expect(instance.getColors().value).toEqual('#7EB26D'); + expect(instance.getValueColors().value).toEqual('#7EB26D'); }); it('should get the threshold color if value is same as a threshold', () => { @@ -51,7 +52,7 @@ describe('Get font color', () => { ], }); - expect(instance.getColors().value).toEqual('#EAB839'); + expect(instance.getValueColors().value).toEqual('#EAB839'); }); }); diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx index 28d2597df10..adec77c3170 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx @@ -6,7 +6,7 @@ import tinycolor from 'tinycolor2'; import { getColorFromHexRgbOrName, getValueFormat, getThresholdForValue } from '../../utils'; // Types -import { Themeable, TimeSeriesValue, Threshold, ValueMapping } from '../../types'; +import { Themeable, TimeSeriesValue, Threshold, ValueMapping, VizOrientation } from '../../types'; const BAR_SIZE_RATIO = 0.8; @@ -19,7 +19,7 @@ export interface Props extends Themeable { value: TimeSeriesValue; maxValue: number; minValue: number; - orientation: string; + orientation: VizOrientation; prefix?: string; suffix?: string; decimals?: number; @@ -31,7 +31,7 @@ export class BarGauge extends PureComponent { minValue: 0, value: 100, unit: 'none', - orientation: 'horizontal', + orientation: VizOrientation.Horizontal, thresholds: [], valueMappings: [], }; @@ -43,7 +43,7 @@ export class BarGauge extends PureComponent { return 0; } - getColors(): BarColors { + getValueColors(): BarColors { const { thresholds, theme, value } = this.props; const activeThreshold = getThresholdForValue(thresholds, value); @@ -67,6 +67,28 @@ export class BarGauge extends PureComponent { }; } + getCellColor(positionValue: TimeSeriesValue): string { + const { thresholds, theme, value } = this.props; + const activeThreshold = getThresholdForValue(thresholds, positionValue); + + if (activeThreshold !== null) { + const color = getColorFromHexRgbOrName(activeThreshold.color, theme.type); + + // if we are past real value the cell is not "on" + if (value === null || (positionValue !== null && positionValue > value)) { + return tinycolor(color) + .setAlpha(0.15) + .toRgbString(); + } else { + return tinycolor(color) + .setAlpha(0.65) + .toRgbString(); + } + } + + return 'gray'; + } + getValueStyles(value: string, color: string, width: number): CSSProperties { const guess = width / (value.length * 1.1); const fontSize = Math.min(Math.max(guess, 14), 40); @@ -82,7 +104,7 @@ export class BarGauge extends PureComponent { const maxHeight = height * BAR_SIZE_RATIO; const barHeight = Math.max(valuePercent * maxHeight, 0); - const colors = this.getColors(); + const colors = this.getValueColors(); const valueStyles = this.getValueStyles(valueFormatted, colors.value, width); const containerStyles: CSSProperties = { @@ -115,7 +137,7 @@ export class BarGauge extends PureComponent { const maxWidth = width * BAR_SIZE_RATIO; const barWidth = Math.max(valuePercent * maxWidth, 0); - const colors = this.getColors(); + const colors = this.getValueColors(); const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO)); valueStyles.marginLeft = '8px'; @@ -145,6 +167,51 @@ export class BarGauge extends PureComponent { ); } + renderHorizontalLCD(valueFormatted: string, valuePercent: number) { + const { height, width, maxValue, minValue } = this.props; + + const valueRange = maxValue - minValue; + const maxWidth = width * BAR_SIZE_RATIO; + const cellSpacing = 4; + const cellCount = 30; + const cellWidth = (maxWidth - cellSpacing * cellCount) / cellCount; + const colors = this.getValueColors(); + const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO)); + valueStyles.marginLeft = '8px'; + + const containerStyles: CSSProperties = { + width: `${width}px`, + height: `${height}px`, + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }; + + const cells: JSX.Element[] = []; + + for (let i = 0; i < cellCount; i++) { + const currentValue = (valueRange / cellCount) * i; + const cellColor = this.getCellColor(currentValue); + const cellStyles: CSSProperties = { + width: `${cellWidth}px`, + backgroundColor: cellColor, + marginRight: '4px', + height: `${height}px`, + }; + + cells.push(
); + } + + return ( +
+ {cells} +
+ {valueFormatted} +
+
+ ); + } + render() { const { maxValue, minValue, orientation, unit, decimals } = this.props; @@ -157,7 +224,7 @@ export class BarGauge extends PureComponent { return vertical ? this.renderVerticalBar(valueFormatted, valuePercent) - : this.renderHorizontalBar(valueFormatted, valuePercent); + : this.renderHorizontalLCD(valueFormatted, valuePercent); } } diff --git a/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx b/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx index 276bfa3517e..767be6764ca 100644 --- a/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx +++ b/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx @@ -1,5 +1,5 @@ import React, { PureComponent } from 'react'; -import { SingleStatValueInfo } from '../../types'; +import { SingleStatValueInfo, VizOrientation } from '../../types'; interface RenderProps { vizWidth: number; @@ -12,20 +12,20 @@ interface Props { height: number; width: number; values: SingleStatValueInfo[]; - orientation?: string; + orientation: VizOrientation; } const SPACE_BETWEEN = 10; export class VizRepeater extends PureComponent { - getOrientation() { + getOrientation(): VizOrientation { const { orientation, width, height } = this.props; - if (!orientation) { + if (orientation === VizOrientation.Auto) { if (width > height) { - return 'horizontal'; + return VizOrientation.Vertical; } else { - return 'vertical'; + return VizOrientation.Horizontal; } } @@ -47,7 +47,7 @@ export class VizRepeater extends PureComponent { let vizHeight = height; let vizWidth = width; - if (orientation === 'horizontal') { + if (orientation === VizOrientation.Horizontal) { repeaterStyle.flexDirection = 'column'; itemStyles.margin = `${SPACE_BETWEEN / 2}px 0`; vizWidth = width; diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 553adb60ec6..0e5c1fd24ff 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -86,3 +86,9 @@ export interface RangeMap extends BaseMap { from: string; to: string; } + +export enum VizOrientation { + Auto = 'auto', + Vertical = 'vertical', + Horizontal = 'horizontal', +} diff --git a/public/app/plugins/panel/bargauge/types.ts b/public/app/plugins/panel/bargauge/types.ts index efc08d64aff..962d18d0e3d 100644 --- a/public/app/plugins/panel/bargauge/types.ts +++ b/public/app/plugins/panel/bargauge/types.ts @@ -1,29 +1,24 @@ -import { Threshold, SelectOptionItem, ValueMapping } from '@grafana/ui'; +import { Threshold, SelectOptionItem, ValueMapping, VizOrientation } from '@grafana/ui'; import { SingleStatValueOptions } from '../gauge/types'; export interface BarGaugeOptions { minValue: number; maxValue: number; - orientation: string; + orientation: VizOrientation; valueOptions: SingleStatValueOptions; valueMappings: ValueMapping[]; thresholds: Threshold[]; } -export enum OrientationModes { - Vertical = 'vertical', - Horizontal = 'horizontal', -} - export const orientationOptions: SelectOptionItem[] = [ - { value: OrientationModes.Horizontal, label: 'Horizontal' }, - { value: OrientationModes.Vertical, label: 'Vertical' }, + { value: VizOrientation.Horizontal, label: 'Horizontal' }, + { value: VizOrientation.Vertical, label: 'Vertical' }, ]; export const defaults: BarGaugeOptions = { minValue: 0, maxValue: 100, - orientation: OrientationModes.Horizontal, + orientation: VizOrientation.Horizontal, valueOptions: { unit: 'none', stat: 'avg', diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index c0e87a8be3b..1ca3e05b86a 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -10,7 +10,7 @@ import { Gauge, VizRepeater } from '@grafana/ui'; // Types import { GaugeOptions } from './types'; -import { PanelProps } from '@grafana/ui/src/types'; +import { PanelProps, VizOrientation } from '@grafana/ui/src/types'; interface Props extends PanelProps {} @@ -50,7 +50,7 @@ export class GaugePanel extends PureComponent { }); return ( - + {({ vizHeight, vizWidth, valueInfo }) => this.renderGauge(valueInfo.value, vizWidth, vizHeight)} );