Merge pull request #16008 from grafana/bar-gauge-refactoring
Refactoring the bar gauge and the orientation modes
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { storiesOf } from '@storybook/react';
|
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 { BarGauge } from './BarGauge';
|
||||||
|
import { VizOrientation } from '../../types';
|
||||||
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
import { withCenteredStory } from '../../utils/storybook/withCenteredStory';
|
||||||
import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
|
import { renderComponentWithTheme } from '../../utils/storybook/withTheme';
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ const getKnobs = () => {
|
|||||||
threshold2Color: text('threshold2Color', 'red'),
|
threshold2Color: text('threshold2Color', 'red'),
|
||||||
unit: text('unit', 'ms'),
|
unit: text('unit', 'ms'),
|
||||||
decimals: number('decimals', 1),
|
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.addDecorator(withCenteredStory);
|
||||||
|
|
||||||
BarGaugeStories.add('Vertical, with basic thresholds', () => {
|
BarGaugeStories.add('Simple with basic thresholds', () => {
|
||||||
const {
|
const {
|
||||||
value,
|
value,
|
||||||
minValue,
|
minValue,
|
||||||
@@ -33,11 +36,13 @@ BarGaugeStories.add('Vertical, with basic thresholds', () => {
|
|||||||
threshold2Value,
|
threshold2Value,
|
||||||
unit,
|
unit,
|
||||||
decimals,
|
decimals,
|
||||||
|
horizontal,
|
||||||
|
lcd,
|
||||||
} = getKnobs();
|
} = getKnobs();
|
||||||
|
|
||||||
return renderComponentWithTheme(BarGauge, {
|
return renderComponentWithTheme(BarGauge, {
|
||||||
width: 200,
|
width: 700,
|
||||||
height: 400,
|
height: 700,
|
||||||
value: value,
|
value: value,
|
||||||
minValue: minValue,
|
minValue: minValue,
|
||||||
maxValue: maxValue,
|
maxValue: maxValue,
|
||||||
@@ -45,6 +50,8 @@ BarGaugeStories.add('Vertical, with basic thresholds', () => {
|
|||||||
prefix: '',
|
prefix: '',
|
||||||
postfix: '',
|
postfix: '',
|
||||||
decimals: decimals,
|
decimals: decimals,
|
||||||
|
orientation: horizontal ? VizOrientation.Horizontal : VizOrientation.Vertical,
|
||||||
|
displayMode: lcd ? 'lcd' : 'simple',
|
||||||
thresholds: [
|
thresholds: [
|
||||||
{ index: 0, value: -Infinity, color: 'green' },
|
{ index: 0, value: -Infinity, color: 'green' },
|
||||||
{ index: 1, value: threshold1Value, color: threshold1Color },
|
{ index: 1, value: threshold1Value, color: threshold1Color },
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ const setup = (propOverrides?: object) => {
|
|||||||
const props: Props = {
|
const props: Props = {
|
||||||
maxValue: 100,
|
maxValue: 100,
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
|
displayMode: 'basic',
|
||||||
thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }],
|
thresholds: [{ index: 0, value: -Infinity, color: '#7EB26D' }],
|
||||||
height: 300,
|
height: 300,
|
||||||
width: 300,
|
width: 300,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Library
|
// Library
|
||||||
import React, { PureComponent, CSSProperties } from 'react';
|
import React, { PureComponent, CSSProperties, ReactNode } from 'react';
|
||||||
import tinycolor from 'tinycolor2';
|
import tinycolor from 'tinycolor2';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
@@ -18,11 +18,9 @@ export interface Props extends Themeable {
|
|||||||
maxValue: number;
|
maxValue: number;
|
||||||
minValue: number;
|
minValue: number;
|
||||||
orientation: VizOrientation;
|
orientation: VizOrientation;
|
||||||
|
displayMode: 'basic' | 'lcd' | 'gradient';
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This visualization is still in POC state, needed more tests & better structure
|
|
||||||
*/
|
|
||||||
export class BarGauge extends PureComponent<Props> {
|
export class BarGauge extends PureComponent<Props> {
|
||||||
static defaultProps: Partial<Props> = {
|
static defaultProps: Partial<Props> = {
|
||||||
maxValue: 100,
|
maxValue: 100,
|
||||||
@@ -31,10 +29,22 @@ export class BarGauge extends PureComponent<Props> {
|
|||||||
text: '100',
|
text: '100',
|
||||||
numeric: 100,
|
numeric: 100,
|
||||||
},
|
},
|
||||||
|
displayMode: 'lcd',
|
||||||
orientation: VizOrientation.Horizontal,
|
orientation: VizOrientation.Horizontal,
|
||||||
thresholds: [],
|
thresholds: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
switch (this.props.displayMode) {
|
||||||
|
case 'lcd':
|
||||||
|
return this.renderRetroBars();
|
||||||
|
case 'basic':
|
||||||
|
case 'gradient':
|
||||||
|
default:
|
||||||
|
return this.renderBasicAndGradientBars();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getValueColors(): BarColors {
|
getValueColors(): BarColors {
|
||||||
const { thresholds, theme, value } = this.props;
|
const { thresholds, theme, value } = this.props;
|
||||||
|
|
||||||
@@ -46,41 +56,19 @@ export class BarGauge extends PureComponent<Props> {
|
|||||||
return {
|
return {
|
||||||
value: color,
|
value: color,
|
||||||
border: color,
|
border: color,
|
||||||
bar: tinycolor(color)
|
background: tinycolor(color)
|
||||||
.setAlpha(0.3)
|
.setAlpha(0.15)
|
||||||
.toRgbString(),
|
.toRgbString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: getColorFromHexRgbOrName('gray', theme.type),
|
value: getColorFromHexRgbOrName('gray', theme.type),
|
||||||
bar: getColorFromHexRgbOrName('gray', theme.type),
|
background: getColorFromHexRgbOrName('gray', theme.type),
|
||||||
border: getColorFromHexRgbOrName('gray', theme.type),
|
border: getColorFromHexRgbOrName('gray', theme.type),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
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.numeric)) {
|
|
||||||
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 {
|
getValueStyles(value: string, color: string, width: number): CSSProperties {
|
||||||
const guess = width / (value.length * 1.1);
|
const guess = width / (value.length * 1.1);
|
||||||
const fontSize = Math.min(Math.max(guess, 14), 40);
|
const fontSize = Math.min(Math.max(guess, 14), 40);
|
||||||
@@ -91,107 +79,205 @@ export class BarGauge extends PureComponent<Props> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
renderVerticalBar(valueFormatted: string, valuePercent: number) {
|
/*
|
||||||
|
* Return width or height depending on viz orientation
|
||||||
|
* */
|
||||||
|
get size() {
|
||||||
const { height, width } = this.props;
|
const { height, width } = this.props;
|
||||||
|
return this.isVertical ? height : width;
|
||||||
|
}
|
||||||
|
|
||||||
const maxHeight = height * BAR_SIZE_RATIO;
|
get isVertical() {
|
||||||
const barHeight = Math.max(valuePercent * maxHeight, 0);
|
return this.props.orientation === VizOrientation.Vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
getBarGradient(maxSize: number): string {
|
||||||
|
const { minValue, maxValue, thresholds, value } = this.props;
|
||||||
|
const cssDirection = this.isVertical ? '0deg' : '90deg';
|
||||||
|
|
||||||
|
let gradient = '';
|
||||||
|
let lastpos = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < thresholds.length; i++) {
|
||||||
|
const threshold = thresholds[i];
|
||||||
|
const color = getColorFromHexRgbOrName(threshold.color);
|
||||||
|
const valuePercent = Math.min(threshold.value / (maxValue - minValue), 1);
|
||||||
|
const pos = valuePercent * maxSize;
|
||||||
|
const offset = Math.round(pos - (pos - lastpos) / 2);
|
||||||
|
|
||||||
|
if (gradient === '') {
|
||||||
|
gradient = `linear-gradient(${cssDirection}, ${color}, ${color}`;
|
||||||
|
} else if (value.numeric < threshold.value) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
lastpos = pos;
|
||||||
|
gradient += ` ${offset}px, ${color}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gradient + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
renderBasicAndGradientBars(): ReactNode {
|
||||||
|
const { height, width, displayMode, maxValue, minValue, value } = this.props;
|
||||||
|
|
||||||
|
const valuePercent = Math.min(value.numeric / (maxValue - minValue), 1);
|
||||||
|
const maxSize = this.size * BAR_SIZE_RATIO;
|
||||||
|
const barSize = Math.max(valuePercent * maxSize, 0);
|
||||||
const colors = this.getValueColors();
|
const colors = this.getValueColors();
|
||||||
const valueStyles = this.getValueStyles(valueFormatted, colors.value, width);
|
const spaceForText = this.isVertical ? width : Math.min(this.size - maxSize, height);
|
||||||
|
const valueStyles = this.getValueStyles(value.text, colors.value, spaceForText);
|
||||||
|
const isBasic = displayMode === 'basic';
|
||||||
|
|
||||||
const containerStyles: CSSProperties = {
|
const containerStyles: CSSProperties = {
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'flex-end',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const barStyles: CSSProperties = {
|
const barStyles: CSSProperties = {
|
||||||
height: `${barHeight}px`,
|
borderRadius: '3px',
|
||||||
width: `${width}px`,
|
|
||||||
backgroundColor: colors.bar,
|
|
||||||
borderTop: `1px solid ${colors.border}`,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.isVertical) {
|
||||||
|
// Custom styles for vertical orientation
|
||||||
|
containerStyles.flexDirection = 'column';
|
||||||
|
containerStyles.justifyContent = 'flex-end';
|
||||||
|
barStyles.transition = 'height 1s';
|
||||||
|
barStyles.height = `${barSize}px`;
|
||||||
|
barStyles.width = `${width}px`;
|
||||||
|
if (isBasic) {
|
||||||
|
// Basic styles
|
||||||
|
barStyles.background = `${colors.background}`;
|
||||||
|
barStyles.border = `1px solid ${colors.border}`;
|
||||||
|
barStyles.boxShadow = `0 0 4px ${colors.border}`;
|
||||||
|
} else {
|
||||||
|
// Gradient styles
|
||||||
|
barStyles.background = this.getBarGradient(maxSize);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Custom styles for horizontal orientation
|
||||||
|
containerStyles.flexDirection = 'row-reverse';
|
||||||
|
containerStyles.justifyContent = 'flex-end';
|
||||||
|
containerStyles.alignItems = 'center';
|
||||||
|
barStyles.transition = 'width 1s';
|
||||||
|
barStyles.height = `${height}px`;
|
||||||
|
barStyles.width = `${barSize}px`;
|
||||||
|
barStyles.marginRight = '10px';
|
||||||
|
|
||||||
|
if (isBasic) {
|
||||||
|
// Basic styles
|
||||||
|
barStyles.background = `${colors.background}`;
|
||||||
|
barStyles.border = `1px solid ${colors.border}`;
|
||||||
|
barStyles.boxShadow = `0 0 4px ${colors.border}`;
|
||||||
|
} else {
|
||||||
|
// Gradient styles
|
||||||
|
barStyles.background = this.getBarGradient(maxSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={containerStyles}>
|
<div style={containerStyles}>
|
||||||
<div className="bar-gauge__value" style={valueStyles}>
|
<div className="bar-gauge__value" style={valueStyles}>
|
||||||
{valueFormatted}
|
{value.text}
|
||||||
</div>
|
</div>
|
||||||
<div style={barStyles} />
|
<div style={barStyles} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHorizontalBar(valueFormatted: string, valuePercent: number) {
|
getCellColor(positionValue: TimeSeriesValue): CellColors {
|
||||||
const { height, width } = this.props;
|
const { thresholds, theme, value } = this.props;
|
||||||
|
const activeThreshold = getThresholdForValue(thresholds, positionValue);
|
||||||
|
|
||||||
const maxWidth = width * BAR_SIZE_RATIO;
|
if (activeThreshold !== null) {
|
||||||
const barWidth = Math.max(valuePercent * maxWidth, 0);
|
const color = getColorFromHexRgbOrName(activeThreshold.color, theme.type);
|
||||||
const colors = this.getValueColors();
|
|
||||||
const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO));
|
|
||||||
|
|
||||||
valueStyles.marginLeft = '8px';
|
// if we are past real value the cell is not "on"
|
||||||
|
if (value === null || (positionValue !== null && positionValue > value.numeric)) {
|
||||||
|
return {
|
||||||
|
background: tinycolor(color)
|
||||||
|
.setAlpha(0.15)
|
||||||
|
.toRgbString(),
|
||||||
|
border: 'transparent',
|
||||||
|
isLit: false,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
background: tinycolor(color)
|
||||||
|
.setAlpha(0.85)
|
||||||
|
.toRgbString(),
|
||||||
|
backgroundShade: tinycolor(color)
|
||||||
|
.setAlpha(0.55)
|
||||||
|
.toRgbString(),
|
||||||
|
border: tinycolor(color)
|
||||||
|
.setAlpha(0.9)
|
||||||
|
.toRgbString(),
|
||||||
|
isLit: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const containerStyles: CSSProperties = {
|
return {
|
||||||
width: `${width}px`,
|
background: 'gray',
|
||||||
height: `${height}px`,
|
border: 'gray',
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const barStyles = {
|
|
||||||
height: `${height}px`,
|
|
||||||
width: `${barWidth}px`,
|
|
||||||
backgroundColor: colors.bar,
|
|
||||||
borderRight: `1px solid ${colors.border}`,
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={containerStyles}>
|
|
||||||
<div style={barStyles} />
|
|
||||||
<div className="bar-gauge__value" style={valueStyles}>
|
|
||||||
{valueFormatted}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderHorizontalLCD(valueFormatted: string, valuePercent: number) {
|
renderRetroBars(): ReactNode {
|
||||||
const { height, width, maxValue, minValue } = this.props;
|
const { height, width, maxValue, minValue, value } = this.props;
|
||||||
|
|
||||||
const valueRange = maxValue - minValue;
|
const valueRange = maxValue - minValue;
|
||||||
const maxWidth = width * BAR_SIZE_RATIO;
|
const maxSize = this.size * BAR_SIZE_RATIO;
|
||||||
const cellSpacing = 4;
|
const cellSpacing = 5;
|
||||||
const cellCount = 30;
|
const cellCount = maxSize / 20;
|
||||||
const cellWidth = (maxWidth - cellSpacing * cellCount) / cellCount;
|
const cellSize = (maxSize - cellSpacing * cellCount) / cellCount;
|
||||||
const colors = this.getValueColors();
|
const colors = this.getValueColors();
|
||||||
const valueStyles = this.getValueStyles(valueFormatted, colors.value, width * (1 - BAR_SIZE_RATIO));
|
const spaceForText = this.isVertical ? width : Math.min(this.size - maxSize, height);
|
||||||
valueStyles.marginLeft = '8px';
|
const valueStyles = this.getValueStyles(value.text, colors.value, spaceForText);
|
||||||
|
|
||||||
const containerStyles: CSSProperties = {
|
const containerStyles: CSSProperties = {
|
||||||
width: `${width}px`,
|
width: `${width}px`,
|
||||||
height: `${height}px`,
|
height: `${height}px`,
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'row',
|
|
||||||
alignItems: 'center',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (this.isVertical) {
|
||||||
|
containerStyles.flexDirection = 'column-reverse';
|
||||||
|
containerStyles.alignItems = 'center';
|
||||||
|
valueStyles.marginBottom = '20px';
|
||||||
|
} else {
|
||||||
|
containerStyles.flexDirection = 'row';
|
||||||
|
containerStyles.alignItems = 'center';
|
||||||
|
valueStyles.marginLeft = '20px';
|
||||||
|
}
|
||||||
|
|
||||||
const cells: JSX.Element[] = [];
|
const cells: JSX.Element[] = [];
|
||||||
|
|
||||||
for (let i = 0; i < cellCount; i++) {
|
for (let i = 0; i < cellCount; i++) {
|
||||||
const currentValue = (valueRange / cellCount) * i;
|
const currentValue = (valueRange / cellCount) * i;
|
||||||
const cellColor = this.getCellColor(currentValue);
|
const cellColor = this.getCellColor(currentValue);
|
||||||
const cellStyles: CSSProperties = {
|
const cellStyles: CSSProperties = {
|
||||||
width: `${cellWidth}px`,
|
|
||||||
backgroundColor: cellColor,
|
|
||||||
marginRight: '4px',
|
|
||||||
height: `${height}px`,
|
|
||||||
borderRadius: '2px',
|
borderRadius: '2px',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (cellColor.isLit) {
|
||||||
|
cellStyles.boxShadow = `0 0 4px ${cellColor.border}`;
|
||||||
|
cellStyles.backgroundImage = `radial-gradient(${cellColor.background} 10%, ${cellColor.backgroundShade})`;
|
||||||
|
} else {
|
||||||
|
cellStyles.backgroundColor = cellColor.background;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isVertical) {
|
||||||
|
cellStyles.height = `${cellSize}px`;
|
||||||
|
cellStyles.width = `${width}px`;
|
||||||
|
cellStyles.marginTop = `${cellSpacing}px`;
|
||||||
|
} else {
|
||||||
|
cellStyles.width = `${cellSize}px`;
|
||||||
|
cellStyles.height = `${height}px`;
|
||||||
|
cellStyles.marginRight = `${cellSpacing}px`;
|
||||||
|
}
|
||||||
|
|
||||||
cells.push(<div style={cellStyles} />);
|
cells.push(<div style={cellStyles} />);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,26 +285,22 @@ export class BarGauge extends PureComponent<Props> {
|
|||||||
<div style={containerStyles}>
|
<div style={containerStyles}>
|
||||||
{cells}
|
{cells}
|
||||||
<div className="bar-gauge__value" style={valueStyles}>
|
<div className="bar-gauge__value" style={valueStyles}>
|
||||||
{valueFormatted}
|
{value.text}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
|
||||||
const { maxValue, minValue, orientation, value } = this.props;
|
|
||||||
|
|
||||||
const valuePercent = Math.min(value.numeric / (maxValue - minValue), 1);
|
|
||||||
const vertical = orientation === 'vertical';
|
|
||||||
|
|
||||||
return vertical
|
|
||||||
? this.renderVerticalBar(value.text, valuePercent)
|
|
||||||
: this.renderHorizontalLCD(value.text, valuePercent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BarColors {
|
interface BarColors {
|
||||||
value: string;
|
value: string;
|
||||||
bar: string;
|
background: string;
|
||||||
border: string;
|
border: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CellColors {
|
||||||
|
background: string;
|
||||||
|
backgroundShade?: string;
|
||||||
|
border: string;
|
||||||
|
isLit?: boolean;
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,353 +6,37 @@ exports[`Render BarGauge with basic options should render 1`] = `
|
|||||||
Object {
|
Object {
|
||||||
"alignItems": "center",
|
"alignItems": "center",
|
||||||
"display": "flex",
|
"display": "flex",
|
||||||
"flexDirection": "row",
|
"flexDirection": "row-reverse",
|
||||||
"height": "300px",
|
"height": "300px",
|
||||||
|
"justifyContent": "flex-end",
|
||||||
"width": "300px",
|
"width": "300px",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<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.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",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
style={
|
|
||||||
Object {
|
|
||||||
"backgroundColor": "rgba(126, 178, 109, 0.15)",
|
|
||||||
"borderRadius": "2px",
|
|
||||||
"height": "300px",
|
|
||||||
"marginRight": "4px",
|
|
||||||
"width": "4px",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
className="bar-gauge__value"
|
className="bar-gauge__value"
|
||||||
style={
|
style={
|
||||||
Object {
|
Object {
|
||||||
"color": "#7EB26D",
|
"color": "#7EB26D",
|
||||||
"fontSize": "27.272727272727263px",
|
"fontSize": "27.27272727272727px",
|
||||||
"marginLeft": "8px",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
25
|
25
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
style={
|
||||||
|
Object {
|
||||||
|
"background": "rgba(126, 178, 109, 0.15)",
|
||||||
|
"border": "1px solid #7EB26D",
|
||||||
|
"borderRadius": "3px",
|
||||||
|
"boxShadow": "0 0 4px #7EB26D",
|
||||||
|
"height": "300px",
|
||||||
|
"marginRight": "10px",
|
||||||
|
"transition": "width 1s",
|
||||||
|
"width": "60px",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -166,7 +166,11 @@ export class ThresholdsEditor extends PureComponent<Props, State> {
|
|||||||
<div className="thresholds-row-input-inner-color">
|
<div className="thresholds-row-input-inner-color">
|
||||||
{threshold.color && (
|
{threshold.color && (
|
||||||
<div className="thresholds-row-input-inner-color-colorpicker">
|
<div className="thresholds-row-input-inner-color-colorpicker">
|
||||||
<ColorPicker color={threshold.color} onChange={color => this.onChangeThresholdColor(threshold, color)} />
|
<ColorPicker
|
||||||
|
color={threshold.color}
|
||||||
|
onChange={color => this.onChangeThresholdColor(threshold, color)}
|
||||||
|
enableNamedColors={true}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+3
@@ -83,10 +83,12 @@ exports[`Render should render with base threshold 1`] = `
|
|||||||
>
|
>
|
||||||
<WithTheme(ColorPicker)
|
<WithTheme(ColorPicker)
|
||||||
color="#7EB26D"
|
color="#7EB26D"
|
||||||
|
enableNamedColors={true}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
>
|
>
|
||||||
<ColorPicker
|
<ColorPicker
|
||||||
color="#7EB26D"
|
color="#7EB26D"
|
||||||
|
enableNamedColors={true}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
Object {
|
||||||
@@ -230,6 +232,7 @@ exports[`Render should render with base threshold 1`] = `
|
|||||||
content={
|
content={
|
||||||
<ColorPickerPopover
|
<ColorPickerPopover
|
||||||
color="#7EB26D"
|
color="#7EB26D"
|
||||||
|
enableNamedColors={true}
|
||||||
onChange={[Function]}
|
onChange={[Function]}
|
||||||
theme={
|
theme={
|
||||||
Object {
|
Object {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
|||||||
orientation={options.orientation}
|
orientation={options.orientation}
|
||||||
thresholds={options.thresholds}
|
thresholds={options.thresholds}
|
||||||
theme={config.theme}
|
theme={config.theme}
|
||||||
|
displayMode={options.displayMode}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { ThresholdsEditor, ValueMappingsEditor, PanelOptionsGrid, PanelOptionsGr
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { FormLabel, PanelEditorProps, Threshold, Select, ValueMapping } from '@grafana/ui';
|
import { FormLabel, PanelEditorProps, Threshold, Select, ValueMapping } from '@grafana/ui';
|
||||||
import { BarGaugeOptions, orientationOptions } from './types';
|
import { BarGaugeOptions, orientationOptions, displayModes } from './types';
|
||||||
import { SingleStatValueEditor } from '../singlestat2/SingleStatValueEditor';
|
import { SingleStatValueEditor } from '../singlestat2/SingleStatValueEditor';
|
||||||
import { SingleStatValueOptions } from '../singlestat2/types';
|
import { SingleStatValueOptions } from '../singlestat2/types';
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
|||||||
onMinValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, minValue: target.value });
|
onMinValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, minValue: target.value });
|
||||||
onMaxValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, maxValue: target.value });
|
onMaxValueChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, maxValue: target.value });
|
||||||
onOrientationChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
onOrientationChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
|
||||||
|
onDisplayModeChange = ({ value }) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
@@ -53,6 +54,16 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
|
|||||||
value={orientationOptions.find(item => item.value === options.orientation)}
|
value={orientationOptions.find(item => item.value === options.orientation)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</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>
|
</PanelOptionsGroup>
|
||||||
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
|
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
|
||||||
</PanelOptionsGrid>
|
</PanelOptionsGrid>
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
import { VizOrientation, SelectOptionItem } from '@grafana/ui';
|
import { VizOrientation, SelectOptionItem } from '@grafana/ui';
|
||||||
|
|
||||||
import { SingleStatBaseOptions } from '../singlestat2/types';
|
import { SingleStatBaseOptions } from '../singlestat2/types';
|
||||||
|
|
||||||
|
export interface BarGaugeOptions extends SingleStatBaseOptions {
|
||||||
|
minValue: number;
|
||||||
|
maxValue: number;
|
||||||
|
displayMode: 'basic' | 'lcd' | 'gradient';
|
||||||
|
}
|
||||||
|
|
||||||
|
export const displayModes: SelectOptionItem[] = [
|
||||||
|
{ value: 'gradient', label: 'Gradient' },
|
||||||
|
{ value: 'lcd', label: 'Retro LCD' },
|
||||||
|
{ value: 'basic', label: 'Basic' },
|
||||||
|
];
|
||||||
|
|
||||||
export const orientationOptions: SelectOptionItem[] = [
|
export const orientationOptions: SelectOptionItem[] = [
|
||||||
{ value: VizOrientation.Horizontal, label: 'Horizontal' },
|
{ value: VizOrientation.Horizontal, label: 'Horizontal' },
|
||||||
{ value: VizOrientation.Vertical, label: 'Vertical' },
|
{ value: VizOrientation.Vertical, label: 'Vertical' },
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface BarGaugeOptions extends SingleStatBaseOptions {
|
|
||||||
minValue: number;
|
|
||||||
maxValue: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaults: BarGaugeOptions = {
|
export const defaults: BarGaugeOptions = {
|
||||||
minValue: 0,
|
minValue: 0,
|
||||||
maxValue: 100,
|
maxValue: 100,
|
||||||
|
displayMode: 'lcd',
|
||||||
orientation: VizOrientation.Horizontal,
|
orientation: VizOrientation.Horizontal,
|
||||||
valueOptions: {
|
valueOptions: {
|
||||||
unit: 'none',
|
unit: 'none',
|
||||||
|
|||||||
Reference in New Issue
Block a user