Commit 392819c5 by Torkel Ödegaard Committed by GitHub

New bar gauge style: Unfilled (#21201)

* BarGauge: Added unfilled option

* Fixed white theme and added 2 unit tests

* Added another demo dashboard

* Fixed dev env dashboards
parent 587e4009
......@@ -106,6 +106,20 @@ describe('BarGauge', () => {
});
const styles = getBasicAndGradientStyles(props);
expect(styles.bar.height).toBe('249px');
expect(styles.emptyBar.bottom).toBe('-3px');
});
});
describe('Horizontal bar', () => {
it('should stretch items', () => {
const props = getProps({
height: 300,
value: getValue(100, 'ServerA'),
orientation: VizOrientation.Horizontal,
});
const styles = getBasicAndGradientStyles(props);
expect(styles.wrapper.alignItems).toBe('stretch');
expect(styles.emptyBar.left).toBe('-3px');
});
});
......
......@@ -42,6 +42,7 @@ export interface Props extends Themeable {
displayMode: 'basic' | 'lcd' | 'gradient';
onClick?: React.MouseEventHandler<HTMLElement>;
className?: string;
showUnfilled?: boolean;
alignmentFactors?: DisplayValueAlignmentFactors;
}
......@@ -57,6 +58,7 @@ export class BarGauge extends PureComponent<Props> {
orientation: VizOrientation.Horizontal,
thresholds: [],
itemSpacing: 10,
showUnfilled: true,
};
render() {
......@@ -92,13 +94,14 @@ export class BarGauge extends PureComponent<Props> {
}
renderBasicAndGradientBars(): ReactNode {
const { value } = this.props;
const { value, showUnfilled } = this.props;
const styles = getBasicAndGradientStyles(this.props);
return (
<div style={styles.wrapper}>
<FormattedValueDisplay className="bar-gauge__value" value={value} style={styles.value} />
{showUnfilled && <div style={styles.emptyBar} />}
<div style={styles.bar} />
</div>
);
......@@ -260,7 +263,7 @@ function calculateTitleDimensions(props: Props): TitleDimensions {
// if height above 40 put text to above bar
if (height > 40) {
const maxTitleHeightRatio = 0.35;
const maxTitleHeightRatio = 0.45;
const titleHeight = Math.max(Math.min(height * maxTitleHeightRatio, MAX_VALUE_HEIGHT), 17);
return {
......@@ -289,6 +292,7 @@ export function getTitleStyles(props: Props): { wrapper: CSSProperties; title: C
const wrapperStyles: CSSProperties = {
display: 'flex',
overflow: 'hidden',
width: '100%',
};
const titleDim = calculateTitleDimensions(props);
......@@ -327,6 +331,7 @@ export function getTitleStyles(props: Props): { wrapper: CSSProperties; title: C
interface BasicAndGradientStyles {
wrapper: CSSProperties;
bar: CSSProperties;
emptyBar: CSSProperties;
value: CSSProperties;
}
......@@ -390,7 +395,7 @@ export function getValuePercent(value: number, minValue: number, maxValue: numbe
* Only exported to for unit test
*/
export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles {
const { displayMode, maxValue, minValue, value, alignmentFactors, orientation } = props;
const { displayMode, maxValue, minValue, value, alignmentFactors, orientation, theme } = props;
const { valueWidth, valueHeight, maxBarHeight, maxBarWidth } = calculateBarAndValueDimensions(props);
const valuePercent = getValuePercent(value.numeric, minValue, maxValue);
......@@ -402,10 +407,21 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
const isBasic = displayMode === 'basic';
const wrapperStyles: CSSProperties = {
display: 'flex',
flexGrow: 1,
};
const barStyles: CSSProperties = {
borderRadius: '3px',
position: 'relative',
zIndex: 1,
};
const emptyBar: CSSProperties = {
background: `rgba(${theme.isDark ? '255,255,255' : '0,0,0'}, 0.07)`,
flexGrow: 1,
display: 'flex',
borderRadius: '3px',
position: 'relative',
};
if (isVertical(orientation)) {
......@@ -419,11 +435,15 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
barStyles.height = `${barHeight}px`;
barStyles.width = `${maxBarWidth}px`;
// adjust so that filled in bar is at the bottom
emptyBar.bottom = '-3px';
if (isBasic) {
// Basic styles
barStyles.background = `${tinycolor(valueColor)
.setAlpha(0.25)
.setAlpha(0.35)
.toRgbString()}`;
barStyles.borderTop = `2px solid ${valueColor}`;
} else {
// Gradient styles
......@@ -435,16 +455,19 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
// Custom styles for horizontal orientation
wrapperStyles.flexDirection = 'row-reverse';
wrapperStyles.justifyContent = 'flex-end';
wrapperStyles.alignItems = 'center';
wrapperStyles.alignItems = 'stretch';
barStyles.transition = 'width 1s';
barStyles.height = `${maxBarHeight}px`;
barStyles.width = `${barWidth}px`;
// shift empty region back to fill gaps due to border radius
emptyBar.left = '-3px';
if (isBasic) {
// Basic styles
barStyles.background = `${tinycolor(valueColor)
.setAlpha(0.25)
.setAlpha(0.35)
.toRgbString()}`;
barStyles.borderRight = `2px solid ${valueColor}`;
} else {
......@@ -457,6 +480,7 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
wrapper: wrapperStyles,
bar: barStyles,
value: valueStyles,
emptyBar,
};
}
......@@ -523,18 +547,27 @@ function getValueStyles(
// how many pixels in wide can the text be?
let textWidth = width;
const formattedValueString = formattedValueToString(value);
if (isVertical(orientation)) {
styles.fontSize = calculateFontSize(formattedValueString, textWidth, height, VALUE_LINE_HEIGHT);
styles.justifyContent = `center`;
} else {
styles.justifyContent = `flex-start`;
styles.fontSize = calculateFontSize(
formattedValueString,
textWidth - VALUE_LEFT_PADDING * 2,
height,
VALUE_LINE_HEIGHT
);
styles.justifyContent = `flex-end`;
styles.paddingLeft = `${VALUE_LEFT_PADDING}px`;
styles.paddingRight = `${VALUE_LEFT_PADDING}px`;
// Need to remove the left padding from the text width constraints
textWidth -= VALUE_LEFT_PADDING;
}
const formattedValueString = formattedValueToString(value);
styles.fontSize = calculateFontSize(formattedValueString, textWidth, height, VALUE_LINE_HEIGHT);
// adjust width of title box
styles.width = measureText(formattedValueString, styles.fontSize).width + VALUE_LEFT_PADDING * 2;
}
return styles;
}
......@@ -7,15 +7,17 @@ exports[`BarGauge Render with basic options should render 1`] = `
"display": "flex",
"flexDirection": "column",
"overflow": "hidden",
"width": "100%",
}
}
>
<div
style={
Object {
"alignItems": "center",
"alignItems": "stretch",
"display": "flex",
"flexDirection": "row-reverse",
"flexGrow": 1,
"justifyContent": "flex-end",
}
}
......@@ -27,12 +29,13 @@ exports[`BarGauge Render with basic options should render 1`] = `
"alignItems": "center",
"color": "#73BF69",
"display": "flex",
"fontSize": 175,
"fontSize": 140,
"height": "300px",
"justifyContent": "flex-start",
"justifyContent": "flex-end",
"lineHeight": 1,
"paddingLeft": "10px",
"width": "60px",
"paddingRight": "10px",
"width": 22,
}
}
value={
......@@ -45,12 +48,26 @@ exports[`BarGauge Render with basic options should render 1`] = `
<div
style={
Object {
"background": "rgba(115, 191, 105, 0.25)",
"background": "rgba(255,255,255, 0.07)",
"borderRadius": "3px",
"display": "flex",
"flexGrow": 1,
"left": "-3px",
"position": "relative",
}
}
/>
<div
style={
Object {
"background": "rgba(115, 191, 105, 0.35)",
"borderRadius": "3px",
"borderRight": "2px solid #73BF69",
"height": "300px",
"position": "relative",
"transition": "width 1s",
"width": "60px",
"zIndex": 1,
}
}
/>
......
......@@ -43,6 +43,7 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
onClick={openMenu}
className={targetClassName}
alignmentFactors={alignmentFactors}
showUnfilled={options.showUnfilled}
/>
);
}}
......
......@@ -11,6 +11,7 @@ import {
FormLabel,
Select,
DataLinksEditor,
Switch,
} from '@grafana/ui';
import { FieldDisplayOptions, FieldConfig, DataLink, PanelEditorProps } from '@grafana/data';
......@@ -54,6 +55,9 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
onOrientationChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, orientation: value });
onDisplayModeChange = ({ value }: any) => this.props.onOptionsChange({ ...this.props.options, displayMode: value });
onToggleShowUnfilled = () => {
this.props.onOptionsChange({ ...this.props.options, showUnfilled: !this.props.options.showUnfilled });
};
onDataLinksChanged = (links: DataLink[]) => {
this.onDefaultsChange({
......@@ -96,6 +100,14 @@ export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGauge
value={displayModes.find(item => item.value === options.displayMode)}
/>
</div>
{options.displayMode !== 'lcd' && (
<Switch
label="Unfilled"
labelClass={`width-${labelWidth}`}
checked={options.showUnfilled}
onChange={this.onToggleShowUnfilled}
/>
)}
</PanelOptionsGroup>
<PanelOptionsGroup title="Field">
<FieldPropertiesEditor
......
......@@ -4,6 +4,7 @@ import { VizOrientation, SelectableValue } from '@grafana/data';
export interface BarGaugeOptions extends SingleStatBaseOptions {
displayMode: 'basic' | 'lcd' | 'gradient';
showUnfilled: boolean;
}
export const displayModes: Array<SelectableValue<string>> = [
......@@ -16,4 +17,5 @@ export const defaults: BarGaugeOptions = {
displayMode: 'lcd',
orientation: VizOrientation.Horizontal,
fieldOptions: standardGaugeFieldOptions,
showUnfilled: true,
};
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