mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,7 @@ It extends [SingleStatBaseOptions](#singlestatbaseoptions).
|
||||
| `minVizHeight` | uint32 | **Yes** | Default: `10`. |
|
||||
| `minVizWidth` | uint32 | **Yes** | Default: `0`. |
|
||||
| `showUnfilled` | boolean | **Yes** | Default: `true`. |
|
||||
| `valueMode` | string | **Yes** | Allows for the table cell gauge display type to set the gauge mode.<br/>Possible values are: `color`, `text`, `hidden`. |
|
||||
| `orientation` | string | No | *(Inherited from [SingleStatBaseOptions](#singlestatbaseoptions))*<br/>TODO docs<br/>Possible values are: `auto`, `vertical`, `horizontal`. |
|
||||
| `reduceOptions` | [ReduceDataOptions](#reducedataoptions) | No | *(Inherited from [SingleStatBaseOptions](#singlestatbaseoptions))*<br/>TODO docs |
|
||||
| `text` | [VizTextDisplayOptions](#viztextdisplayoptions) | No | *(Inherited from [SingleStatBaseOptions](#singlestatbaseoptions))*<br/>TODO docs |
|
||||
|
@@ -195,11 +195,12 @@ TODO docs
|
||||
|
||||
TODO docs
|
||||
|
||||
| Property | Type | Required | Description |
|
||||
|----------|--------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `color` | string | **Yes** | TODO docs |
|
||||
| `state` | string | No | TODO docs<br/>TODO are the values here enumerable into a disjunction?<br/>Some seem to be listed in typescript comment |
|
||||
| `value` | number | No | TODO docs<br/>FIXME the corresponding typescript field is required/non-optional, but nulls currently appear here when serializing -Infinity to JSON |
|
||||
| Property | Type | Required | Description |
|
||||
|----------|---------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| `color` | string | **Yes** | TODO docs |
|
||||
| `index` | integer | No | Threshold index, an old property that is not needed an should only appear in older dashboards |
|
||||
| `state` | string | No | TODO docs<br/>TODO are the values here enumerable into a disjunction?<br/>Some seem to be listed in typescript comment |
|
||||
| `value` | number | No | TODO docs<br/>FIXME the corresponding typescript field is required/non-optional, but nulls currently appear here when serializing -Infinity to JSON |
|
||||
|
||||
### ValueMapping
|
||||
|
||||
|
@@ -108,6 +108,22 @@ The gauge is split up in small cells that are lit or unlit.
|
||||
|
||||
{{< figure src="/static/img/docs/tables/lcd-gauge.png" max-width="500px" caption="LCD gauge" class="docs-image--no-shadow" >}}
|
||||
|
||||
#### Label Options
|
||||
|
||||
Additionally, labels displayed alongside of the gauges can be set to be colored by value, match the theme text color, or be hidden.
|
||||
|
||||
**Value Color**
|
||||
|
||||
{{< figure src="/static/img/docs/tables/value-color-mode.png" max-width="500px" caption="Color Label by Value" class="docs-image--no-shadow" >}}
|
||||
|
||||
**Text Color**
|
||||
|
||||
{{< figure src="/static/img/docs/tables/text-color-mode.png" max-width="500px" caption="Color Label by theme color" class="docs-image--no-shadow" >}}
|
||||
|
||||
**Hidden**
|
||||
|
||||
{{< figure src="/static/img/docs/tables/hidden-mode.png" max-width="500px" caption="Hide Label" class="docs-image--no-shadow" >}}
|
||||
|
||||
### JSON view
|
||||
|
||||
Shows value formatted as code. If a value is an object the JSON view allowing browsing the JSON object will appear on hover.
|
||||
|
@@ -25,8 +25,8 @@ lineage: seqs: [
|
||||
// Description of dashboard.
|
||||
description?: string
|
||||
// This property should only be used in dashboards defined by plugins. It is a quick check
|
||||
// to see if the version has changed since the last time. Unclear why using the version property
|
||||
// is insufficient.
|
||||
// to see if the version has changed since the last time. Unclear why using the version property
|
||||
// is insufficient.
|
||||
revision?: int64 @grafanamaturity(NeedsExpertReview)
|
||||
// For dashboards imported from the https://grafana.com/grafana/dashboards/ portal
|
||||
gnetId?: string @grafanamaturity(NeedsExpertReview)
|
||||
@@ -220,6 +220,8 @@ lineage: seqs: [
|
||||
value?: number @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
color: string @grafanamaturity(NeedsExpertReview)
|
||||
// Threshold index, an old property that is not needed an should only appear in older dashboards
|
||||
index?: int32 @grafanamaturity(NeedsExpertReview)
|
||||
// TODO docs
|
||||
// TODO are the values here enumerable into a disjunction?
|
||||
// Some seem to be listed in typescript comment
|
||||
|
@@ -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>
|
||||
);
|
||||
};
|
||||
|
@@ -701,6 +701,9 @@ type Threshold struct {
|
||||
// TODO docs
|
||||
Color string `json:"color"`
|
||||
|
||||
// Threshold index, an old property that is not needed an should only appear in older dashboards
|
||||
Index *int32 `json:"index,omitempty"`
|
||||
|
||||
// TODO docs
|
||||
// TODO are the values here enumerable into a disjunction?
|
||||
// Some seem to be listed in typescript comment
|
||||
|
@@ -296,7 +296,7 @@
|
||||
0
|
||||
],
|
||||
"description": "A Grafana dashboard.",
|
||||
"grafanaMaturityCount": 139,
|
||||
"grafanaMaturityCount": 140,
|
||||
"lineageIsGroup": false,
|
||||
"links": {
|
||||
"docs": "https://grafana.com/docs/grafana/next/developers/kinds/core/dashboard/schema-reference",
|
||||
|
@@ -12,12 +12,11 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/expr"
|
||||
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
|
@@ -4,7 +4,7 @@ import React from 'react';
|
||||
|
||||
import { dateMath, dateTime, EventBus, LoadingState, TimeRange, toDataFrame, VizOrientation } from '@grafana/data';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { BarGaugeDisplayMode } from '@grafana/schema';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode } from '@grafana/schema';
|
||||
|
||||
import { BarGaugePanel, BarGaugePanelProps } from './BarGaugePanel';
|
||||
|
||||
@@ -101,6 +101,7 @@ function buildPanelData(overrideValues?: Partial<BarGaugePanelProps>): BarGaugeP
|
||||
showUnfilled: true,
|
||||
minVizHeight: 10,
|
||||
minVizWidth: 0,
|
||||
valueMode: BarGaugeValueMode.Color,
|
||||
},
|
||||
transparent: false,
|
||||
timeRange,
|
||||
|
@@ -49,6 +49,7 @@ export class BarGaugePanel extends PureComponent<BarGaugePanelProps> {
|
||||
className={targetClassName}
|
||||
alignmentFactors={count > 1 ? alignmentFactors : undefined}
|
||||
showUnfilled={options.showUnfilled}
|
||||
valueDisplayMode={options.valueMode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { PanelPlugin, VizOrientation } from '@grafana/data';
|
||||
import { BarGaugeDisplayMode } from '@grafana/schema';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode } from '@grafana/schema';
|
||||
import { commonOptionsBuilder, sharedSingleStatPanelChangedHandler } from '@grafana/ui';
|
||||
|
||||
import { addOrientationOption, addStandardDataReduceOptions } from '../stat/common';
|
||||
@@ -29,6 +29,18 @@ export const plugin = new PanelPlugin<PanelOptions>(BarGaugePanel)
|
||||
},
|
||||
defaultValue: defaultPanelOptions.displayMode,
|
||||
})
|
||||
.addRadio({
|
||||
path: 'valueMode',
|
||||
name: 'Value display',
|
||||
settings: {
|
||||
options: [
|
||||
{ value: BarGaugeValueMode.Color, label: 'Value color' },
|
||||
{ value: BarGaugeValueMode.Text, label: 'Text color' },
|
||||
{ value: BarGaugeValueMode.Hidden, label: 'Hidden' },
|
||||
],
|
||||
},
|
||||
defaultValue: defaultPanelOptions.valueMode,
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
path: 'showUnfilled',
|
||||
name: 'Show unfilled area',
|
||||
|
@@ -29,6 +29,7 @@ composableKinds: PanelCfg: {
|
||||
PanelOptions: {
|
||||
common.SingleStatBaseOptions
|
||||
displayMode: common.BarGaugeDisplayMode | *"gradient"
|
||||
valueMode: common.BarGaugeValueMode | *"color"
|
||||
showUnfilled: bool | *true
|
||||
minVizWidth: uint32 | *0
|
||||
minVizHeight: uint32 | *10
|
||||
|
@@ -17,6 +17,7 @@ export interface PanelOptions extends common.SingleStatBaseOptions {
|
||||
minVizHeight: number;
|
||||
minVizWidth: number;
|
||||
showUnfilled: boolean;
|
||||
valueMode: common.BarGaugeValueMode;
|
||||
}
|
||||
|
||||
export const defaultPanelOptions: Partial<PanelOptions> = {
|
||||
@@ -24,4 +25,5 @@ export const defaultPanelOptions: Partial<PanelOptions> = {
|
||||
minVizHeight: 10,
|
||||
minVizWidth: 0,
|
||||
showUnfilled: true,
|
||||
valueMode: common.BarGaugeValueMode.Color,
|
||||
};
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { merge } from 'lodash';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
import { TableCellOptions } from '@grafana/schema';
|
||||
import { Field, Select, TableCellDisplayMode } from '@grafana/ui';
|
||||
import { Field, Select, TableCellDisplayMode, useStyles2 } from '@grafana/ui';
|
||||
|
||||
import { BarGaugeCellOptionsEditor } from './cells/BarGaugeCellOptionsEditor';
|
||||
import { ColorBackgroundCellOptionsEditor } from './cells/ColorBackgroundCellOptionsEditor';
|
||||
@@ -25,8 +26,8 @@ interface Props {
|
||||
|
||||
export const TableCellOptionEditor = ({ value, onChange }: Props) => {
|
||||
const cellType = value.type;
|
||||
const styles = useStyles2(getStyles);
|
||||
const currentMode = cellDisplayModeOptions.find((o) => o.value!.type === cellType)!;
|
||||
|
||||
let [settingCache, setSettingCache] = useState<Record<string, TableCellOptions>>({});
|
||||
|
||||
// Update display mode on change
|
||||
@@ -56,7 +57,7 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => {
|
||||
|
||||
// Setup and inject editor
|
||||
return (
|
||||
<>
|
||||
<div className={styles.fixBottomMargin}>
|
||||
<Field>
|
||||
<Select options={cellDisplayModeOptions} value={currentMode} onChange={onCellTypeChange} />
|
||||
</Field>
|
||||
@@ -69,7 +70,7 @@ export const TableCellOptionEditor = ({ value, onChange }: Props) => {
|
||||
{cellType === TableCellDisplayMode.Sparkline && (
|
||||
<SparklineCellOptionsEditor cellOptions={value} onChange={onCellOptionsChange} />
|
||||
)}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -87,3 +88,9 @@ const cellDisplayModeOptions: Array<SelectableValue<TableCellOptions>> = [
|
||||
{ value: { type: TableCellDisplayMode.JSONView }, label: 'JSON View' },
|
||||
{ value: { type: TableCellDisplayMode.Image }, label: 'Image' },
|
||||
];
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
fixBottomMargin: css({
|
||||
marginBottom: theme.spacing(-2),
|
||||
}),
|
||||
});
|
||||
|
@@ -1,32 +1,46 @@
|
||||
import React from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { BarGaugeDisplayMode, TableBarGaugeCellOptions } from '@grafana/schema';
|
||||
import { Field, HorizontalGroup, Select } from '@grafana/ui';
|
||||
import { Stack } from '@grafana/experimental';
|
||||
import { BarGaugeDisplayMode, BarGaugeValueMode, TableBarGaugeCellOptions } from '@grafana/schema';
|
||||
import { Field, RadioButtonGroup, Select } from '@grafana/ui';
|
||||
|
||||
import { TableCellEditorProps } from '../TableCellOptionEditor';
|
||||
|
||||
type Props = TableCellEditorProps<TableBarGaugeCellOptions>;
|
||||
|
||||
export function BarGaugeCellOptionsEditor({ cellOptions, onChange }: Props) {
|
||||
// Set the display mode on change
|
||||
const onCellOptionsChange = (v: SelectableValue) => {
|
||||
cellOptions.mode = v.value;
|
||||
onChange(cellOptions);
|
||||
};
|
||||
|
||||
const onValueModeChange = (v: BarGaugeValueMode) => {
|
||||
cellOptions.valueDisplayMode = v;
|
||||
onChange(cellOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack direction="column" gap={0}>
|
||||
<Field label="Gauge display mode">
|
||||
<Select value={cellOptions?.mode} onChange={onCellOptionsChange} options={barGaugeOpts} />
|
||||
</Field>
|
||||
<Field label="Value display">
|
||||
<RadioButtonGroup value={cellOptions?.valueDisplayMode} onChange={onValueModeChange} options={valueModes} />
|
||||
</Field>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const barGaugeOpts: SelectableValue[] = [
|
||||
{ value: BarGaugeDisplayMode.Basic, label: 'Basic' },
|
||||
{ value: BarGaugeDisplayMode.Gradient, label: 'Gradient' },
|
||||
{ value: BarGaugeDisplayMode.Lcd, label: 'Retro LCD' },
|
||||
];
|
||||
|
||||
export const BarGaugeCellOptionsEditor = ({
|
||||
cellOptions,
|
||||
onChange,
|
||||
}: TableCellEditorProps<TableBarGaugeCellOptions>) => {
|
||||
// Set the display mode on change
|
||||
const onCellOptionsChange = (v: SelectableValue) => {
|
||||
cellOptions.mode = v.value;
|
||||
onChange(cellOptions);
|
||||
};
|
||||
|
||||
return (
|
||||
<HorizontalGroup>
|
||||
<Field label="Gauge Display Mode">
|
||||
<Select value={cellOptions?.mode} onChange={onCellOptionsChange} options={barGaugeOpts} />
|
||||
</Field>
|
||||
</HorizontalGroup>
|
||||
);
|
||||
};
|
||||
const valueModes: SelectableValue[] = [
|
||||
{ value: BarGaugeValueMode.Color, label: 'Value color' },
|
||||
{ value: BarGaugeValueMode.Text, label: 'Text color' },
|
||||
{ value: BarGaugeValueMode.Hidden, label: 'Hidden' },
|
||||
];
|
||||
|
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { TableCellBackgroundDisplayMode, TableColoredBackgroundCellOptions } from '@grafana/schema';
|
||||
import { HorizontalGroup, Select, Field } from '@grafana/ui';
|
||||
import { Select, Field } from '@grafana/ui';
|
||||
|
||||
import { TableCellEditorProps } from '../TableCellOptionEditor';
|
||||
|
||||
@@ -22,10 +22,8 @@ export const ColorBackgroundCellOptionsEditor = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<HorizontalGroup>
|
||||
<Field label="Background display mode">
|
||||
<Select value={cellOptions?.mode} onChange={onCellOptionsChange} options={colorBackgroundOpts} />
|
||||
</Field>
|
||||
</HorizontalGroup>
|
||||
<Field label="Background display mode">
|
||||
<Select value={cellOptions?.mode} onChange={onCellOptionsChange} options={colorBackgroundOpts} />
|
||||
</Field>
|
||||
);
|
||||
};
|
||||
|
@@ -23,7 +23,7 @@ import { PanelOptions, defaultPanelOptions } from './panelcfg.gen';
|
||||
import { TableSuggestionsSupplier } from './suggestions';
|
||||
|
||||
const footerCategory = 'Table footer';
|
||||
const cellCategory = ['Cell Options'];
|
||||
const cellCategory = ['Cell options'];
|
||||
|
||||
export const plugin = new PanelPlugin<PanelOptions, TableFieldOptions>(TablePanel)
|
||||
.setPanelChangeHandler(tablePanelChangedHandler)
|
||||
@@ -70,7 +70,7 @@ export const plugin = new PanelPlugin<PanelOptions, TableFieldOptions>(TablePane
|
||||
.addCustomEditor<void, TableCellOptions>({
|
||||
id: 'cellOptions',
|
||||
path: 'cellOptions',
|
||||
name: 'Cell Type',
|
||||
name: 'Cell type',
|
||||
editor: TableCellOptionEditor,
|
||||
override: TableCellOptionEditor,
|
||||
defaultValue: defaultTableFieldOptions.cellOptions,
|
||||
|
Reference in New Issue
Block a user