Table Panel: Add ability to use text color for value or hide value in gauge cell (#61477)
* BarGauge: New value options * Fix typings for cell options, add new value mode option for bar gauge cells * Add BarGauge panel option, tests, and update test dashboard * Updated * Added default * Goodbye trusty console.log * Update * Merge changes from main * Update docs * Add valuemode doc changes * Update gdev dashboard * Update valueMode symbol name to valueDisplayMode * Use Enums as Opposed to literals, don't calculate values when hidden * Remove double import * Fix tests * One more test fix * Remove erroneous targets field, fix type of maxDataPoints * Strip nulls and add index field to Thresholds * Gen cue * remove bad targets again * Fixes --------- Co-authored-by: Kyle Cunningham <kyle@codeincarnate.com> Co-authored-by: sam boyer <sdboyer@grafana.com>
This commit is contained in:
co-authored by
Kyle Cunningham
sam boyer
parent
0a4d9f01e8
commit
73ce20ab48
@@ -593,6 +593,15 @@ export enum BarGaugeDisplayMode {
|
||||
Lcd = 'lcd',
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows for the table cell gauge display type to set the gauge mode.
|
||||
*/
|
||||
export enum BarGaugeValueMode {
|
||||
Color = 'color',
|
||||
Hidden = 'hidden',
|
||||
Text = 'text',
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO docs
|
||||
*/
|
||||
@@ -697,6 +706,7 @@ export interface TableImageCellOptions {
|
||||
export interface TableBarGaugeCellOptions {
|
||||
mode?: BarGaugeDisplayMode;
|
||||
type: TableCellDisplayMode.Gauge;
|
||||
valueDisplayMode?: BarGaugeValueMode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -242,6 +242,9 @@ VizLegendOptions: {
|
||||
// for the bar gauge component of Grafana UI
|
||||
BarGaugeDisplayMode: "basic" | "lcd" | "gradient" @cuetsy(kind="enum")
|
||||
|
||||
// Allows for the table cell gauge display type to set the gauge mode.
|
||||
BarGaugeValueMode: "color" | "text" | "hidden" @cuetsy(kind="enum")
|
||||
|
||||
// TODO docs
|
||||
VizTooltipOptions: {
|
||||
mode: TooltipDisplayMode
|
||||
|
||||
@@ -52,6 +52,7 @@ TableImageCellOptions: {
|
||||
TableBarGaugeCellOptions: {
|
||||
type: TableCellDisplayMode & "gauge"
|
||||
mode?: BarGaugeDisplayMode
|
||||
valueDisplayMode?: BarGaugeValueMode
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
// Sparkline cell options
|
||||
|
||||
@@ -238,6 +238,10 @@ export interface Threshold {
|
||||
* TODO docs
|
||||
*/
|
||||
color: string;
|
||||
/**
|
||||
* Threshold index, an old property that is not needed an should only appear in older dashboards
|
||||
*/
|
||||
index?: number;
|
||||
/**
|
||||
* TODO docs
|
||||
* TODO are the values here enumerable into a disjunction?
|
||||
|
||||
@@ -11,18 +11,18 @@ import {
|
||||
getDisplayProcessor,
|
||||
createTheme,
|
||||
} from '@grafana/data';
|
||||
import { BarGaugeDisplayMode } from '@grafana/schema';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode } from '@grafana/schema';
|
||||
|
||||
import {
|
||||
BarGauge,
|
||||
Props,
|
||||
getCellColor,
|
||||
getValueColor,
|
||||
getTextValueColor,
|
||||
getBasicAndGradientStyles,
|
||||
getBarGradient,
|
||||
getTitleStyles,
|
||||
getValuePercent,
|
||||
calculateBarAndValueDimensions,
|
||||
getCellColor,
|
||||
} from './BarGauge';
|
||||
|
||||
const green = '#73BF69';
|
||||
@@ -63,7 +63,7 @@ function getProps(propOverrides?: Partial<Props>): Props {
|
||||
}
|
||||
|
||||
function getValue(value: number, title?: string): DisplayValue {
|
||||
return { numeric: value, text: value.toString(), title: title };
|
||||
return { numeric: value, text: value.toString(), title: title, color: '#FF0000' };
|
||||
}
|
||||
|
||||
describe('BarGauge', () => {
|
||||
@@ -134,12 +134,12 @@ describe('BarGauge', () => {
|
||||
it('should get the threshold color if value is same as a threshold', () => {
|
||||
const props = getProps();
|
||||
props.value = props.display!(70);
|
||||
expect(getValueColor(props)).toEqual(orange);
|
||||
expect(getTextValueColor(props)).toEqual(orange);
|
||||
});
|
||||
it('should get the base threshold', () => {
|
||||
const props = getProps();
|
||||
props.value = props.display!(-10);
|
||||
expect(getValueColor(props)).toEqual(green);
|
||||
expect(getTextValueColor(props)).toEqual(green);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -325,5 +325,46 @@ describe('BarGauge', () => {
|
||||
);
|
||||
expect(result.valueWidth).toBe(21);
|
||||
});
|
||||
|
||||
it('valueWidth be zero if valueMode is hideen', () => {
|
||||
const result = calculateBarAndValueDimensions(
|
||||
getProps({
|
||||
height: 30,
|
||||
width: 100,
|
||||
value: getValue(1, 'AA'),
|
||||
orientation: VizOrientation.Horizontal,
|
||||
valueDisplayMode: BarGaugeValueMode.Hidden,
|
||||
})
|
||||
);
|
||||
expect(result.valueWidth).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('With valueMode set to text color', () => {
|
||||
it('should color value using text color', () => {
|
||||
const props = getProps({
|
||||
width: 150,
|
||||
value: getValue(100),
|
||||
orientation: VizOrientation.Vertical,
|
||||
valueDisplayMode: BarGaugeValueMode.Text,
|
||||
});
|
||||
const styles = getBasicAndGradientStyles(props);
|
||||
expect(styles.bar.background).toBe('rgba(255, 0, 0, 0.35)');
|
||||
expect(styles.value.color).toBe('rgb(204, 204, 220)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('With valueMode set to text value', () => {
|
||||
it('should color value value color', () => {
|
||||
const props = getProps({
|
||||
width: 150,
|
||||
value: getValue(100),
|
||||
orientation: VizOrientation.Vertical,
|
||||
valueDisplayMode: BarGaugeValueMode.Color,
|
||||
});
|
||||
const styles = getBasicAndGradientStyles(props);
|
||||
expect(styles.bar.background).toBe('rgba(255, 0, 0, 0.35)');
|
||||
expect(styles.value.color).toBe('#FF0000');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
VizOrientation,
|
||||
} from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { BarGaugeDisplayMode, VizTextDisplayOptions } from '@grafana/schema';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode, VizTextDisplayOptions } from '@grafana/schema';
|
||||
|
||||
import { Themeable2 } from '../../types';
|
||||
import { calculateFontSize, measureText } from '../../utils/measureText';
|
||||
@@ -49,6 +49,7 @@ export interface Props extends Themeable2 {
|
||||
className?: string;
|
||||
showUnfilled?: boolean;
|
||||
alignmentFactors?: DisplayValueAlignmentFactors;
|
||||
valueDisplayMode?: BarGaugeValueMode;
|
||||
}
|
||||
|
||||
export class BarGauge extends PureComponent<Props> {
|
||||
@@ -111,17 +112,19 @@ export class BarGauge extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
renderBasicAndGradientBars(): ReactNode {
|
||||
const { value, showUnfilled } = this.props;
|
||||
const { value, showUnfilled, valueDisplayMode } = this.props;
|
||||
|
||||
const styles = getBasicAndGradientStyles(this.props);
|
||||
|
||||
return (
|
||||
<div style={styles.wrapper}>
|
||||
<FormattedValueDisplay
|
||||
data-testid={selectors.components.Panels.Visualization.BarGauge.valueV2}
|
||||
value={value}
|
||||
style={styles.value}
|
||||
/>
|
||||
{valueDisplayMode !== BarGaugeValueMode.Hidden && (
|
||||
<FormattedValueDisplay
|
||||
data-testid={selectors.components.Panels.Visualization.BarGauge.valueV2}
|
||||
value={value}
|
||||
style={styles.value}
|
||||
/>
|
||||
)}
|
||||
{showUnfilled && <div style={styles.emptyBar} />}
|
||||
<div style={styles.bar} />
|
||||
</div>
|
||||
@@ -129,7 +132,8 @@ export class BarGauge extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
renderRetroBars(): ReactNode {
|
||||
const { display, field, value, itemSpacing, alignmentFactors, orientation, lcdCellWidth, text } = this.props;
|
||||
const { display, field, value, itemSpacing, alignmentFactors, orientation, lcdCellWidth, text, valueDisplayMode } =
|
||||
this.props;
|
||||
const { valueHeight, valueWidth, maxBarHeight, maxBarWidth, wrapperWidth, wrapperHeight } =
|
||||
calculateBarAndValueDimensions(this.props);
|
||||
const minValue = field.min ?? GAUGE_DEFAULT_MINIMUM;
|
||||
@@ -141,7 +145,7 @@ export class BarGauge extends PureComponent<Props> {
|
||||
const cellSpacing = itemSpacing!;
|
||||
const cellCount = Math.floor(maxSize / lcdCellWidth!);
|
||||
const cellSize = Math.floor((maxSize - cellSpacing * cellCount) / cellCount);
|
||||
const valueColor = getValueColor(this.props);
|
||||
const valueColor = getTextValueColor(this.props);
|
||||
|
||||
const valueToBaseSizeOn = alignmentFactors ? alignmentFactors : value;
|
||||
const valueStyles = getValueStyles(valueToBaseSizeOn, valueColor, valueWidth, valueHeight, orientation, text);
|
||||
@@ -192,11 +196,13 @@ export class BarGauge extends PureComponent<Props> {
|
||||
return (
|
||||
<div style={containerStyles}>
|
||||
{cells}
|
||||
<FormattedValueDisplay
|
||||
data-testid={selectors.components.Panels.Visualization.BarGauge.valueV2}
|
||||
value={value}
|
||||
style={valueStyles}
|
||||
/>
|
||||
{valueDisplayMode !== BarGaugeValueMode.Hidden && (
|
||||
<FormattedValueDisplay
|
||||
data-testid={selectors.components.Panels.Visualization.BarGauge.valueV2}
|
||||
value={value}
|
||||
style={valueStyles}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -338,7 +344,7 @@ interface BarAndValueDimensions {
|
||||
* Only exported for unit tests
|
||||
**/
|
||||
export function calculateBarAndValueDimensions(props: Props): BarAndValueDimensions {
|
||||
const { height, width, orientation, text, alignmentFactors } = props;
|
||||
const { height, width, orientation, text, alignmentFactors, valueDisplayMode } = props;
|
||||
const titleDim = calculateTitleDimensions(props);
|
||||
const value = alignmentFactors ?? props.value;
|
||||
const valueString = formattedValueToString(value);
|
||||
@@ -363,13 +369,25 @@ export function calculateBarAndValueDimensions(props: Props): BarAndValueDimensi
|
||||
}
|
||||
|
||||
valueWidth = width;
|
||||
|
||||
if (valueDisplayMode === BarGaugeValueMode.Hidden) {
|
||||
valueHeight = 0;
|
||||
valueWidth = 0;
|
||||
}
|
||||
|
||||
maxBarHeight = height - (titleDim.height + valueHeight);
|
||||
maxBarWidth = width;
|
||||
wrapperWidth = width;
|
||||
wrapperHeight = height - titleDim.height;
|
||||
} else {
|
||||
valueHeight = height - titleDim.height;
|
||||
valueWidth = Math.max(Math.min(width * 0.2, MAX_VALUE_WIDTH), realValueWidth);
|
||||
// Calculate the width and the height of the given values
|
||||
if (valueDisplayMode === BarGaugeValueMode.Hidden) {
|
||||
valueHeight = 0;
|
||||
valueWidth = 0;
|
||||
} else {
|
||||
valueHeight = height - titleDim.height;
|
||||
valueWidth = Math.max(Math.min(width * 0.2, MAX_VALUE_WIDTH), realValueWidth);
|
||||
}
|
||||
|
||||
maxBarHeight = height - titleDim.height;
|
||||
maxBarWidth = width - valueWidth - titleDim.width;
|
||||
@@ -447,10 +465,11 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
|
||||
const minValue = field.min ?? GAUGE_DEFAULT_MINIMUM;
|
||||
const maxValue = field.max ?? GAUGE_DEFAULT_MAXIMUM;
|
||||
const valuePercent = getValuePercent(value.numeric, minValue, maxValue);
|
||||
const valueColor = getValueColor(props);
|
||||
const textColor = getTextValueColor(props);
|
||||
const barColor = value.color ?? FALLBACK_COLOR;
|
||||
|
||||
const valueToBaseSizeOn = alignmentFactors ? alignmentFactors : value;
|
||||
const valueStyles = getValueStyles(valueToBaseSizeOn, valueColor, valueWidth, valueHeight, orientation, text);
|
||||
const valueStyles = getValueStyles(valueToBaseSizeOn, textColor, valueWidth, valueHeight, orientation, text);
|
||||
|
||||
const isBasic = displayMode === 'basic';
|
||||
const wrapperStyles: CSSProperties = {
|
||||
@@ -491,8 +510,8 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
|
||||
|
||||
if (isBasic) {
|
||||
// Basic styles
|
||||
barStyles.background = `${tinycolor(valueColor).setAlpha(0.35).toRgbString()}`;
|
||||
barStyles.borderTop = `2px solid ${valueColor}`;
|
||||
barStyles.background = `${tinycolor(barColor).setAlpha(0.35).toRgbString()}`;
|
||||
barStyles.borderTop = `2px solid ${barColor}`;
|
||||
} else {
|
||||
// Gradient styles
|
||||
barStyles.background = getBarGradient(props, maxBarHeight);
|
||||
@@ -517,8 +536,8 @@ export function getBasicAndGradientStyles(props: Props): BasicAndGradientStyles
|
||||
|
||||
if (isBasic) {
|
||||
// Basic styles
|
||||
barStyles.background = `${tinycolor(valueColor).setAlpha(0.35).toRgbString()}`;
|
||||
barStyles.borderRight = `2px solid ${valueColor}`;
|
||||
barStyles.background = `${tinycolor(barColor).setAlpha(0.35).toRgbString()}`;
|
||||
barStyles.borderRight = `2px solid ${barColor}`;
|
||||
} else {
|
||||
// Gradient styles
|
||||
barStyles.background = getBarGradient(props, maxBarWidth);
|
||||
@@ -598,7 +617,11 @@ export function getBarGradient(props: Props, maxSize: number): string {
|
||||
/**
|
||||
* Only exported to for unit test
|
||||
*/
|
||||
export function getValueColor(props: Props): string {
|
||||
export function getTextValueColor(props: Props): string {
|
||||
if (props.valueDisplayMode === 'text') {
|
||||
return props.theme.colors.text.primary;
|
||||
}
|
||||
|
||||
const { value } = props;
|
||||
if (value.color) {
|
||||
return value.color;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { isFunction } from 'lodash';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { ThresholdsConfig, ThresholdsMode, VizOrientation, getFieldConfigWithMinMax } from '@grafana/data';
|
||||
import { BarGaugeDisplayMode } from '@grafana/schema';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode } from '@grafana/schema';
|
||||
|
||||
import { BarGauge } from '../BarGauge/BarGauge';
|
||||
import { DataLinksContextMenu, DataLinksContextMenuApi } from '../DataLinks/DataLinksContextMenu';
|
||||
@@ -26,6 +26,8 @@ const defaultScale: ThresholdsConfig = {
|
||||
|
||||
export const BarGaugeCell: FC<TableCellProps> = (props) => {
|
||||
const { field, innerWidth, tableStyles, cell, cellProps, row } = props;
|
||||
const displayValue = field.display!(cell.value);
|
||||
const cellOptions = getCellOptions(field);
|
||||
|
||||
let config = getFieldConfigWithMinMax(field, false);
|
||||
if (!config.thresholds) {
|
||||
@@ -35,14 +37,15 @@ export const BarGaugeCell: FC<TableCellProps> = (props) => {
|
||||
};
|
||||
}
|
||||
|
||||
const displayValue = field.display!(cell.value);
|
||||
|
||||
// Set default display mode
|
||||
// Set default display mode and update if defined
|
||||
// and update the valueMode if defined
|
||||
let barGaugeMode: BarGaugeDisplayMode = BarGaugeDisplayMode.Gradient;
|
||||
let valueDisplayMode: BarGaugeValueMode | undefined = undefined;
|
||||
|
||||
const cellOptions = getCellOptions(field);
|
||||
if (cellOptions.type === TableCellDisplayMode.Gauge) {
|
||||
barGaugeMode = cellOptions.mode ?? BarGaugeDisplayMode.Gradient;
|
||||
valueDisplayMode =
|
||||
cellOptions.valueDisplayMode !== undefined ? cellOptions.valueDisplayMode : BarGaugeValueMode.Text;
|
||||
}
|
||||
|
||||
const getLinks = () => {
|
||||
@@ -73,6 +76,7 @@ export const BarGaugeCell: FC<TableCellProps> = (props) => {
|
||||
itemSpacing={1}
|
||||
lcdCellWidth={8}
|
||||
displayMode={barGaugeMode}
|
||||
valueDisplayMode={valueDisplayMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -84,21 +88,7 @@ export const BarGaugeCell: FC<TableCellProps> = (props) => {
|
||||
{(api) => renderComponent(api)}
|
||||
</DataLinksContextMenu>
|
||||
)}
|
||||
{!hasLinks && (
|
||||
<BarGauge
|
||||
width={innerWidth}
|
||||
height={tableStyles.cellHeightInner}
|
||||
field={config}
|
||||
display={field.display}
|
||||
text={{ valueSize: 14 }}
|
||||
value={displayValue}
|
||||
orientation={VizOrientation.Horizontal}
|
||||
theme={tableStyles.theme}
|
||||
itemSpacing={1}
|
||||
lcdCellWidth={8}
|
||||
displayMode={barGaugeMode}
|
||||
/>
|
||||
)}
|
||||
{!hasLinks && renderComponent({})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user