From 09eddd1676d03b5a9038d497c455cce4df710dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Thu, 14 Mar 2019 18:48:12 +0100 Subject: [PATCH 1/6] Refactoring the bar gauge and the orientation modes --- .../components/BarGauge/BarGauge.story.tsx | 15 +- .../src/components/BarGauge/BarGauge.test.tsx | 1 + .../src/components/BarGauge/BarGauge.tsx | 214 ++++++----- .../__snapshots__/BarGauge.test.tsx.snap | 347 +----------------- .../plugins/panel/bargauge/BarGaugePanel.tsx | 1 + .../panel/bargauge/BarGaugePanelEditor.tsx | 13 +- public/app/plugins/panel/bargauge/types.ts | 4 + 7 files changed, 149 insertions(+), 446 deletions(-) diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx index c7a53af5ccf..6754d43a5cc 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.story.tsx @@ -1,6 +1,7 @@ import { storiesOf } from '@storybook/react'; -import { number, text } from '@storybook/addon-knobs'; +import { number, text, boolean } from '@storybook/addon-knobs'; import { BarGauge } from './BarGauge'; +import { VizOrientation } from '../../types'; import { withCenteredStory } from '../../utils/storybook/withCenteredStory'; import { renderComponentWithTheme } from '../../utils/storybook/withTheme'; @@ -15,6 +16,8 @@ const getKnobs = () => { threshold2Color: text('threshold2Color', 'red'), unit: text('unit', 'ms'), decimals: number('decimals', 1), + horizontal: boolean('horizontal', false), + lcd: boolean('lcd', false), }; }; @@ -22,7 +25,7 @@ const BarGaugeStories = storiesOf('UI/BarGauge/BarGauge', module); BarGaugeStories.addDecorator(withCenteredStory); -BarGaugeStories.add('Vertical, with basic thresholds', () => { +BarGaugeStories.add('Simple with basic thresholds', () => { const { value, minValue, @@ -33,11 +36,13 @@ BarGaugeStories.add('Vertical, with basic thresholds', () => { threshold2Value, unit, decimals, + horizontal, + lcd, } = getKnobs(); return renderComponentWithTheme(BarGauge, { - width: 200, - height: 400, + width: 700, + height: 700, value: value, minValue: minValue, maxValue: maxValue, @@ -45,6 +50,8 @@ BarGaugeStories.add('Vertical, with basic thresholds', () => { prefix: '', postfix: '', decimals: decimals, + orientation: horizontal ? VizOrientation.Horizontal : VizOrientation.Vertical, + displayMode: lcd ? 'lcd' : 'simple', thresholds: [ { index: 0, value: -Infinity, color: 'green' }, { index: 1, value: threshold1Value, color: threshold1Color }, diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx index 8fa0b2846a5..07a640ee7f7 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.test.tsx @@ -15,6 +15,7 @@ const setup = (propOverrides?: object) => { minValue: 0, prefix: '', suffix: '', + displayMode: 'simple', thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }], unit: 'none', height: 300, diff --git a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx index 97cc4792785..0de46709c16 100644 --- a/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx +++ b/packages/grafana-ui/src/components/BarGauge/BarGauge.tsx @@ -1,5 +1,5 @@ // Library -import React, { PureComponent, CSSProperties } from 'react'; +import React, { PureComponent, CSSProperties, ReactNode } from 'react'; import tinycolor from 'tinycolor2'; // Utils @@ -23,22 +23,37 @@ export interface Props extends Themeable { prefix?: string; suffix?: string; decimals?: number; + displayMode: 'simple' | 'lcd'; } -/* - * This visualization is still in POC state, needed more tests & better structure - */ export class BarGauge extends PureComponent { static defaultProps: Partial = { maxValue: 100, minValue: 0, value: 100, unit: 'none', + displayMode: 'simple', orientation: VizOrientation.Horizontal, thresholds: [], valueMappings: [], }; + render() { + const { maxValue, minValue, unit, decimals, displayMode } = this.props; + + const numericValue = this.getNumericValue(); + const valuePercent = Math.min(numericValue / (maxValue - minValue), 1); + + const formatFunc = getValueFormat(unit); + const valueFormatted = formatFunc(numericValue, decimals); + + if (displayMode === 'lcd') { + return this.renderLcdMode(valueFormatted, valuePercent); + } else { + return this.renderSimpleMode(valueFormatted, valuePercent); + } + } + getNumericValue(): number { if (Number.isFinite(this.props.value as number)) { return this.props.value as number; @@ -70,6 +85,70 @@ export class BarGauge extends PureComponent { }; } + getValueStyles(value: string, color: string, width: number): CSSProperties { + const guess = width / (value.length * 1.1); + const fontSize = Math.min(Math.max(guess, 14), 40); + + return { + color: color, + fontSize: fontSize + 'px', + }; + } + + /* + * Return width or height depending on viz orientation + * */ + get size() { + const { height, width, orientation } = this.props; + return orientation === VizOrientation.Horizontal ? width : height; + } + + renderSimpleMode(valueFormatted: string, valuePercent: number): ReactNode { + const { height, width, orientation } = this.props; + + const maxSize = this.size * BAR_SIZE_RATIO; + const barSize = Math.max(valuePercent * maxSize, 0); + const colors = this.getValueColors(); + const valueStyles = this.getValueStyles(valueFormatted, colors.value, this.size - maxSize); + + const containerStyles: CSSProperties = { + width: `${width}px`, + height: `${height}px`, + display: 'flex', + }; + + const barStyles: CSSProperties = { + backgroundColor: colors.bar, + }; + + // Custom styles for vertical orientation + if (orientation === VizOrientation.Vertical) { + containerStyles.flexDirection = 'column'; + containerStyles.justifyContent = 'flex-end'; + barStyles.height = `${barSize}px`; + barStyles.width = `${width}px`; + barStyles.borderTop = `1px solid ${colors.border}`; + } else { + // Custom styles for horizontal orientation + containerStyles.flexDirection = 'row-reverse'; + containerStyles.justifyContent = 'flex-end'; + containerStyles.alignItems = 'center'; + barStyles.height = `${height}px`; + barStyles.width = `${barSize}px`; + barStyles.marginRight = '10px'; + barStyles.borderRight = `1px solid ${colors.border}`; + } + + return ( +
+
+ {valueFormatted} +
+
+
+ ); + } + getCellColor(positionValue: TimeSeriesValue): string { const { thresholds, theme, value } = this.props; const activeThreshold = getThresholdForValue(thresholds, positionValue); @@ -92,117 +171,51 @@ export class BarGauge extends PureComponent { 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); - - return { - color: color, - fontSize: fontSize + 'px', - }; - } - - renderVerticalBar(valueFormatted: string, valuePercent: number) { - const { height, width } = this.props; - - const maxHeight = height * BAR_SIZE_RATIO; - const barHeight = Math.max(valuePercent * maxHeight, 0); - const colors = this.getValueColors(); - const valueStyles = this.getValueStyles(valueFormatted, colors.value, width); - - const containerStyles: CSSProperties = { - width: `${width}px`, - height: `${height}px`, - display: 'flex', - flexDirection: 'column', - justifyContent: 'flex-end', - }; - - const barStyles: CSSProperties = { - height: `${barHeight}px`, - width: `${width}px`, - backgroundColor: colors.bar, - borderTop: `1px solid ${colors.border}`, - }; - - return ( -
-
- {valueFormatted} -
-
-
- ); - } - - renderHorizontalBar(valueFormatted: string, valuePercent: number) { - const { height, width } = this.props; - - const maxWidth = width * BAR_SIZE_RATIO; - const barWidth = Math.max(valuePercent * maxWidth, 0); - 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 barStyles = { - height: `${height}px`, - width: `${barWidth}px`, - backgroundColor: colors.bar, - borderRight: `1px solid ${colors.border}`, - }; - - return ( -
-
-
- {valueFormatted} -
-
- ); - } - - renderHorizontalLCD(valueFormatted: string, valuePercent: number) { - const { height, width, maxValue, minValue } = this.props; + renderLcdMode(valueFormatted: string, valuePercent: number): ReactNode { + const { height, width, maxValue, minValue, orientation } = this.props; const valueRange = maxValue - minValue; - const maxWidth = width * BAR_SIZE_RATIO; + const maxSize = this.size * BAR_SIZE_RATIO; const cellSpacing = 4; const cellCount = 30; - const cellWidth = (maxWidth - cellSpacing * cellCount) / cellCount; + const cellSize = (maxSize - cellSpacing * cellCount) / cellCount; const colors = this.getValueColors(); - const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO)); - valueStyles.marginLeft = '8px'; + const valueStyles = this.getValueStyles(valueFormatted, colors.value, this.size - maxSize); const containerStyles: CSSProperties = { width: `${width}px`, height: `${height}px`, display: 'flex', - flexDirection: 'row', - alignItems: 'center', }; + if (orientation === VizOrientation.Horizontal) { + containerStyles.flexDirection = 'row'; + containerStyles.alignItems = 'center'; + } else { + containerStyles.flexDirection = 'column-reverse'; + containerStyles.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`, borderRadius: '2px', }; + if (orientation === VizOrientation.Horizontal) { + cellStyles.width = `${cellSize}px`; + cellStyles.height = `${height}px`; + cellStyles.marginRight = '4px'; + } else { + cellStyles.height = `${cellSize}px`; + cellStyles.width = `${width}px`; + cellStyles.marginTop = '4px'; + } + cells.push(
); } @@ -215,21 +228,6 @@ export class BarGauge extends PureComponent {
); } - - render() { - const { maxValue, minValue, orientation, unit, decimals } = this.props; - - const numericValue = this.getNumericValue(); - const valuePercent = Math.min(numericValue / (maxValue - minValue), 1); - - const formatFunc = getValueFormat(unit); - const valueFormatted = formatFunc(numericValue, decimals); - const vertical = orientation === 'vertical'; - - return vertical - ? this.renderVerticalBar(valueFormatted, valuePercent) - : this.renderHorizontalLCD(valueFormatted, valuePercent); - } } interface BarColors { diff --git a/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap b/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap index 65c647bd90c..077389be418 100644 --- a/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap +++ b/packages/grafana-ui/src/components/BarGauge/__snapshots__/BarGauge.test.tsx.snap @@ -6,353 +6,34 @@ exports[`Render BarGauge with basic options should render 1`] = ` Object { "alignItems": "center", "display": "flex", - "flexDirection": "row", + "flexDirection": "row-reverse", "height": "300px", + "justifyContent": "flex-end", "width": "300px", } } > -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
25
+
`; diff --git a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 9f60c3440eb..708b472ec2e 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -35,6 +35,7 @@ export class BarGaugePanel extends PureComponent { thresholds={options.thresholds} valueMappings={options.valueMappings} theme={config.theme} + displayMode={options.displayMode} /> ); } diff --git a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx index 4232155228b..87e5defd277 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanelEditor.tsx @@ -7,7 +7,7 @@ import { ThresholdsEditor, ValueMappingsEditor, PanelOptionsGrid, PanelOptionsGr // Types import { FormLabel, PanelEditorProps, Threshold, Select, ValueMapping } from '@grafana/ui'; -import { BarGaugeOptions, orientationOptions } from './types'; +import { BarGaugeOptions, orientationOptions, displayModes } from './types'; import { SingleStatValueOptions } from '../gauge/types'; export class BarGaugePanelEditor extends PureComponent> { @@ -32,6 +32,7 @@ export class BarGaugePanelEditor extends PureComponent this.props.onOptionsChange({ ...this.props.options, minValue: target.value }); onMaxValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, maxValue: target.value }); onOrientationChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, orientation: value }); + onDisplayModeChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, displayMode: value }); render() { const { options } = this.props; @@ -53,6 +54,16 @@ export class BarGaugePanelEditor extends PureComponent item.value === options.orientation)} />
+
+ Display Mode +