Explorar el Código

Refactoring the bar gauge and the orientation modes

Torkel Ödegaard hace 6 años
padre
commit
09eddd1676

+ 11 - 4
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 },

+ 1 - 0
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,

+ 90 - 92
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<Props> {
   static defaultProps: Partial<Props> = {
     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,28 +85,6 @@ export class BarGauge extends PureComponent<Props> {
     };
   }
 
-  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.7)
-          .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);
@@ -102,29 +95,50 @@ export class BarGauge extends PureComponent<Props> {
     };
   }
 
-  renderVerticalBar(valueFormatted: string, valuePercent: number) {
-    const { height, width } = this.props;
+  /*
+   * 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 maxHeight = height * BAR_SIZE_RATIO;
-    const barHeight = Math.max(valuePercent * maxHeight, 0);
+    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, width);
+    const valueStyles = this.getValueStyles(valueFormatted, colors.value, this.size - maxSize);
 
     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}`,
     };
 
+    // 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 (
       <div style={containerStyles}>
         <div className="bar-gauge__value" style={valueStyles}>
@@ -135,74 +149,73 @@ export class BarGauge extends PureComponent<Props> {
     );
   }
 
-  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';
+  getCellColor(positionValue: TimeSeriesValue): string {
+    const { thresholds, theme, value } = this.props;
+    const activeThreshold = getThresholdForValue(thresholds, positionValue);
 
-    const containerStyles: CSSProperties = {
-      width: `${width}px`,
-      height: `${height}px`,
-      display: 'flex',
-      flexDirection: 'row',
-      alignItems: 'center',
-    };
+    if (activeThreshold !== null) {
+      const color = getColorFromHexRgbOrName(activeThreshold.color, theme.type);
 
-    const barStyles = {
-      height: `${height}px`,
-      width: `${barWidth}px`,
-      backgroundColor: colors.bar,
-      borderRight: `1px solid ${colors.border}`,
-    };
+      // 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.7)
+          .toRgbString();
+      }
+    }
 
-    return (
-      <div style={containerStyles}>
-        <div style={barStyles} />
-        <div className="bar-gauge__value" style={valueStyles}>
-          {valueFormatted}
-        </div>
-      </div>
-    );
+    return 'gray';
   }
 
-  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(<div style={cellStyles} />);
     }
 
@@ -215,21 +228,6 @@ export class BarGauge extends PureComponent<Props> {
       </div>
     );
   }
-
-  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 {

+ 12 - 331
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",
     }
   }
 >
   <div
+    className="bar-gauge__value"
     style={
       Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.7)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
-      }
-    }
-  />
-  <div
-    style={
-      Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
-        "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
+        "color": "#7EB26D",
+        "fontSize": "27.27272727272727px",
       }
     }
-  />
+  >
+    25
+  </div>
   <div
     style={
       Object {
-        "backgroundColor": "rgba(126, 178, 109, 0.15)",
-        "borderRadius": "2px",
+        "backgroundColor": "rgba(126, 178, 109, 0.3)",
+        "borderRight": "1px solid #7EB26D",
         "height": "300px",
-        "marginRight": "4px",
-        "width": "4px",
+        "marginRight": "10px",
+        "width": "60px",
       }
     }
   />
-  <div
-    className="bar-gauge__value"
-    style={
-      Object {
-        "color": "#7EB26D",
-        "fontSize": "27.272727272727263px",
-        "marginLeft": "8px",
-      }
-    }
-  >
-    25
-  </div>
 </div>
 `;

+ 1 - 0
public/app/plugins/panel/bargauge/BarGaugePanel.tsx

@@ -35,6 +35,7 @@ export class BarGaugePanel extends PureComponent<Props> {
         thresholds={options.thresholds}
         valueMappings={options.valueMappings}
         theme={config.theme}
+        displayMode={options.displayMode}
       />
     );
   }

+ 12 - 1
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<PanelEditorProps<BarGaugeOptions>> {
@@ -32,6 +32,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
   onMinValueChange = ({ target }) => 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<PanelEditorProps<BarGauge
                 value={orientationOptions.find(item => item.value === options.orientation)}
               />
             </div>
+            <div className="form-field">
+              <FormLabel width={8}>Display Mode</FormLabel>
+              <Select
+                width={12}
+                options={displayModes}
+                defaultValue={displayModes[0]}
+                onChange={this.onDisplayModeChange}
+                value={displayModes.find(item => item.value === options.displayMode)}
+              />
+            </div>
           </PanelOptionsGroup>
           <ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
         </PanelOptionsGrid>

+ 4 - 0
public/app/plugins/panel/bargauge/types.ts

@@ -8,6 +8,7 @@ export interface BarGaugeOptions {
   valueOptions: SingleStatValueOptions;
   valueMappings: ValueMapping[];
   thresholds: Threshold[];
+  displayMode: 'simple' | 'lcd';
 }
 
 export const orientationOptions: SelectOptionItem[] = [
@@ -15,9 +16,12 @@ export const orientationOptions: SelectOptionItem[] = [
   { value: VizOrientation.Vertical, label: 'Vertical' },
 ];
 
+export const displayModes: SelectOptionItem[] = [{ value: 'simple', label: 'Simple' }, { value: 'lcd', label: 'LCD' }];
+
 export const defaults: BarGaugeOptions = {
   minValue: 0,
   maxValue: 100,
+  displayMode: 'simple',
   orientation: VizOrientation.Horizontal,
   valueOptions: {
     unit: 'none',