TimeSeries: Support coloring series and line by thresholds or gradient color scales (#35910)

* TimeSeries: Adds support for color scheme series and line colors

* Updates

* fixed device issue

* Evaluate series color in legend

* two fixes

* It works with points

* Added test dashboard

* Minor fix

* Reset color mode to palette when switching to panel that supports by series mode

* Add support for relative thresholds

* Updated snapshots
pull/36440/head
Torkel Ödegaard 4 years ago committed by GitHub
parent 333d520528
commit a241f03167
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 593
      devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json
  2. 20
      packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap
  3. 1
      packages/grafana-ui/src/components/Sparkline/Sparkline.tsx
  4. 1
      packages/grafana-ui/src/components/TimeSeries/utils.ts
  5. 16
      packages/grafana-ui/src/components/uPlot/PlotLegend.tsx
  6. 11
      packages/grafana-ui/src/components/uPlot/config.ts
  7. 17
      packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts
  8. 16
      packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts
  9. 75
      packages/grafana-ui/src/components/uPlot/config/gradientFills.ts
  10. 18
      public/app/features/dashboard/state/getPanelOptionsWithDefaults.test.ts
  11. 9
      public/app/features/dashboard/state/getPanelOptionsWithDefaults.ts
  12. 32
      public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap
  13. 2
      public/app/plugins/panel/timeseries/config.ts

@ -0,0 +1,593 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"links": [],
"panels": [
{
"datasource": "-- Dashboard --",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 5,
"gradientMode": "scheme",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "smooth",
"lineWidth": 3,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 50,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "orange",
"value": 20
},
{
"color": "red",
"value": 30
}
]
},
"unit": "degree"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 11,
"x": 0,
"y": 0
},
"id": 9,
"maxDataPoints": 45,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"panelId": 4,
"refId": "A"
}
],
"title": "Color line by discrete tresholds",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 84,
"gradientMode": "scheme",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "smooth",
"lineWidth": 0,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 50,
"min": 0,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "orange",
"value": 20
},
{
"color": "red",
"value": 30
}
]
},
"unit": "degree"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 13,
"x": 11,
"y": 0
},
"id": 4,
"interval": "80s",
"maxDataPoints": 42,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"max": 40,
"min": 0,
"noise": 1,
"refId": "A",
"scenarioId": "random_walk",
"spread": 20,
"startValue": 1
}
],
"timeFrom": null,
"title": "Color bars by discrete thresholds",
"type": "timeseries"
},
{
"datasource": "-- Dashboard --",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "scheme",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "smooth",
"lineWidth": 3,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 50,
"min": 1,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "green",
"value": 0
},
{
"color": "orange",
"value": 20
},
{
"color": "red",
"value": 30
}
]
},
"unit": "degree"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 11,
"x": 0,
"y": 7
},
"id": 6,
"maxDataPoints": 50,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"panelId": 4,
"refId": "A"
}
],
"title": "Color line by color scale",
"type": "timeseries"
},
{
"datasource": "-- Dashboard --",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 64,
"gradientMode": "scheme",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "smooth",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 50,
"min": 1,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "green",
"value": 0
},
{
"color": "orange",
"value": 20
},
{
"color": "red",
"value": 30
}
]
},
"unit": "degree"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 13,
"x": 11,
"y": 7
},
"id": 10,
"maxDataPoints": 45,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"panelId": 4,
"refId": "A"
}
],
"title": "Color bars by color scale",
"type": "timeseries"
},
{
"datasource": "-- Dashboard --",
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 64,
"gradientMode": "scheme",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "smooth",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 50,
"min": 1,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "green",
"value": 0
},
{
"color": "orange",
"value": 20
},
{
"color": "red",
"value": 30
}
]
},
"unit": "degree"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 15
},
"id": 7,
"maxDataPoints": 50,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"panelId": 4,
"refId": "A"
}
],
"title": "Color line by color scale",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-GrYlRd"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "points",
"fillOpacity": 10,
"gradientMode": "scheme",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "smooth",
"lineWidth": 3,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"max": 50,
"min": 1,
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "blue",
"value": null
},
{
"color": "green",
"value": 0
},
{
"color": "orange",
"value": 20
},
{
"color": "red",
"value": 30
}
]
},
"unit": "degree"
},
"overrides": []
},
"gridPos": {
"h": 13,
"w": 24,
"x": 0,
"y": 21
},
"id": 8,
"maxDataPoints": 250,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltip": {
"mode": "single"
}
},
"targets": [
{
"max": 45,
"min": 20,
"noise": 0,
"refId": "A",
"scenarioId": "random_walk",
"spread": 12,
"startValue": 40
},
{
"hide": false,
"max": 20,
"min": 1,
"noise": 0,
"refId": "B",
"scenarioId": "random_walk",
"spread": 10
}
],
"title": "Color line by color scale",
"type": "timeseries"
}
],
"refresh": false,
"schemaVersion": 30,
"style": "dark",
"tags": [
"gdev",
"panel-tests"
],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Panel Tests - Graph NG - By value color schemes",
"uid": "aBXrJ0R7z",
"version": 20
}

@ -126,11 +126,11 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": "#808080",
"fill": "#ff0000",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
"stroke": "#ff0000",
},
"pxAlign": undefined,
"scale": "__fixed",
@ -147,11 +147,11 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": "#808080",
"fill": "#ff0000",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
"stroke": "#ff0000",
},
"pxAlign": undefined,
"scale": "__fixed",
@ -168,11 +168,11 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": "#808080",
"fill": "#ff0000",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
"stroke": "#ff0000",
},
"pxAlign": undefined,
"scale": "__fixed",
@ -189,11 +189,11 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": "#808080",
"fill": "#ff0000",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
"stroke": "#ff0000",
},
"pxAlign": undefined,
"scale": "__fixed",
@ -210,11 +210,11 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": "#808080",
"fill": "#ff0000",
"filter": [Function],
"show": true,
"size": undefined,
"stroke": "#808080",
"stroke": "#ff0000",
},
"pxAlign": undefined,
"scale": "__fixed",

@ -164,7 +164,6 @@ export class Sparkline extends PureComponent<SparklineProps, State> {
lineInterpolation: customConfig.lineInterpolation,
showPoints: pointsMode,
pointSize: customConfig.pointSize,
pointColor: customConfig.pointColor ?? seriesColor,
fillOpacity: customConfig.fillOpacity,
fillColor: customConfig.fillColor ?? seriesColor,
});

@ -226,7 +226,6 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<{ sync: DashboardCursor
barWidthFactor: customConfig.barWidthFactor,
barMaxWidth: customConfig.barMaxWidth,
pointSize: customConfig.pointSize,
pointColor: customConfig.pointColor ?? seriesColor,
spanNulls: customConfig.spanNulls || false,
show: !customConfig.hideFrom?.viz,
gradientMode: customConfig.gradientMode,

@ -1,11 +1,19 @@
import React from 'react';
import { DataFrame, DisplayValue, fieldReducers, getFieldDisplayName, reduceField } from '@grafana/data';
import {
DataFrame,
DisplayValue,
fieldReducers,
getFieldDisplayName,
getFieldSeriesColor,
reduceField,
} from '@grafana/data';
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
import { VizLegendItem } from '../VizLegend/types';
import { VizLegendOptions } from '../VizLegend/models.gen';
import { AxisPlacement } from './config';
import { VizLayout, VizLayoutLegendProps } from '../VizLayout/VizLayout';
import { VizLegend } from '../VizLegend/VizLegend';
import { useTheme2 } from '../../themes';
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
@ -22,6 +30,7 @@ export const PlotLegend: React.FC<PlotLegendProps> = ({
displayMode,
...vizLayoutLegendProps
}) => {
const theme = useTheme2();
const legendItems = config
.getSeries()
.map<VizLegendItem | undefined>((s) => {
@ -40,10 +49,13 @@ export const PlotLegend: React.FC<PlotLegendProps> = ({
}
const label = getFieldDisplayName(field, data[fieldIndex.frameIndex]!, data);
const scaleColor = getFieldSeriesColor(field, theme);
const seriesColor = scaleColor.color;
return {
disabled: !seriesConfig.show ?? false,
fieldIndex,
color: seriesConfig.lineColor!,
color: seriesColor,
label,
yAxis: axisPlacement === AxisPlacement.Left ? 1 : 2,
getDisplayValues: () => {

@ -137,7 +137,6 @@ export enum GraphGradientMode {
export interface PointsConfig {
showPoints?: PointVisibility;
pointSize?: number;
pointColor?: string;
pointSymbol?: string; // eventually dot,star, etc
}
@ -274,9 +273,13 @@ export const graphFieldOptions = {
fillGradient: [
{ label: 'None', value: GraphGradientMode.None },
{ label: 'Opacity', value: GraphGradientMode.Opacity },
{ label: 'Hue', value: GraphGradientMode.Hue },
// { label: 'Color scheme', value: GraphGradientMode.Scheme },
{ label: 'Opacity', value: GraphGradientMode.Opacity, description: 'Enable fill opacity gradient' },
{ label: 'Hue', value: GraphGradientMode.Hue, description: 'Small color hue gradient' },
{
label: 'Scheme',
value: GraphGradientMode.Scheme,
description: 'Use color scheme to define gradient',
},
] as Array<SelectableValue<GraphGradientMode>>,
stacking: [

@ -464,7 +464,6 @@ describe('UPlotConfigBuilder', () => {
gradientMode: GraphGradientMode.Opacity,
showPoints: PointVisibility.Auto,
pointSize: 5,
pointColor: '#00ff00',
lineColor: '#0000ff',
lineWidth: 1,
spanNulls: false,
@ -497,10 +496,10 @@ describe('UPlotConfigBuilder', () => {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": "#00ff00",
"fill": "#0000ff",
"filter": undefined,
"size": 5,
"stroke": "#00ff00",
"stroke": "#0000ff",
},
"pxAlign": undefined,
"scale": "scale-x",
@ -607,10 +606,10 @@ describe('UPlotConfigBuilder', () => {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#0000ff",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#0000ff",
},
"pxAlign": undefined,
"scale": "scale-x",
@ -623,10 +622,10 @@ describe('UPlotConfigBuilder', () => {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#00ff00",
"filter": undefined,
"size": 5,
"stroke": undefined,
"stroke": "#00ff00",
},
"pxAlign": undefined,
"scale": "scale-x",
@ -639,10 +638,10 @@ describe('UPlotConfigBuilder', () => {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#ff0000",
"filter": undefined,
"size": 5,
"stroke": undefined,
"stroke": "#ff0000",
},
"pxAlign": undefined,
"scale": "scale-x",

@ -46,7 +46,6 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
barWidthFactor,
barMaxWidth,
showPoints,
pointColor,
pointSize,
scaleKey,
pxAlign,
@ -55,15 +54,16 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
} = this.props;
let lineConfig: Partial<Series> = {};
const lineColor = this.getLineColor();
if (pathBuilder != null) {
lineConfig.paths = pathBuilder;
lineConfig.stroke = this.getLineColor();
lineConfig.stroke = lineColor;
lineConfig.width = lineWidth;
} else if (drawStyle === DrawStyle.Points) {
lineConfig.paths = () => null;
} else if (drawStyle != null) {
lineConfig.stroke = this.getLineColor();
lineConfig.stroke = lineColor;
lineConfig.width = lineWidth;
if (lineStyle && lineStyle.fill !== 'solid') {
if (lineStyle.fill === 'dot') {
@ -85,8 +85,8 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
const pointsConfig: Partial<Series> = {
points: {
stroke: pointColor,
fill: pointColor,
stroke: lineColor,
fill: lineColor,
size: pointSize,
filter: pointsFilter,
},
@ -123,10 +123,10 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
}
private getLineColor(): Series.Stroke {
const { lineColor, gradientMode, colorMode, thresholds } = this.props;
const { lineColor, gradientMode, colorMode, thresholds, theme } = this.props;
if (gradientMode === GraphGradientMode.Scheme) {
return getScaleGradientFn(1, colorMode, thresholds);
return getScaleGradientFn(1, theme, colorMode, thresholds);
}
return lineColor ?? FALLBACK_COLOR;
@ -148,7 +148,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder<SeriesProps, Series> {
case GraphGradientMode.Hue:
return getHueGradientFn((fillColor ?? lineColor)!, opacityPercent, theme);
case GraphGradientMode.Scheme:
return getScaleGradientFn(opacityPercent, colorMode, thresholds);
return getScaleGradientFn(opacityPercent, theme, colorMode, thresholds);
default:
if (opacityPercent > 0) {
return tinycolor(lineColor).setAlpha(opacityPercent).toString();

@ -1,4 +1,4 @@
import { FieldColorMode, GrafanaTheme2, ThresholdsConfig } from '@grafana/data';
import { FieldColorMode, FieldColorModeId, GrafanaTheme2, ThresholdsConfig, ThresholdsMode } from '@grafana/data';
import tinycolor from 'tinycolor2';
import uPlot from 'uplot';
import { getCanvasContext } from '../../../utils/measureText';
@ -45,9 +45,10 @@ export function getHueGradientFn(
*/
export function getScaleGradientFn(
opacity: number,
theme: GrafanaTheme2,
colorMode?: FieldColorMode,
thresholds?: ThresholdsConfig
): (self: uPlot, seriesIdx: number) => CanvasGradient {
): (self: uPlot, seriesIdx: number) => CanvasGradient | string {
if (!colorMode) {
throw Error('Missing colorMode required for color scheme gradients');
}
@ -57,35 +58,67 @@ export function getScaleGradientFn(
}
return (plot: uPlot, seriesIdx: number) => {
// A uplot bug (I think) where this is called before there is bbox
// Color used for cursor highlight, not sure what to do here as this is called before we have bbox
// and only once so same color is used for all points
if (plot.bbox.top == null) {
return theme.colors.text.primary;
}
const ctx = getCanvasContext();
const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + plot.bbox.height);
const canvasHeight = plot.bbox.height;
const series = plot.series[seriesIdx];
const scale = plot.scales[series.scale!];
const range = plot.bbox.height;
const scaleMin = scale.min ?? 0;
const scaleMax = scale.max ?? 100;
const scaleRange = scaleMax - scaleMin;
console.log('scale', scale);
console.log('series.min', series.min);
console.log('series.max', series.max);
const addColorStop = (value: number, color: string) => {
const pos = plot.valToPos(value, series.scale!, true);
// when above range we get negative values here
if (pos < 0) {
return;
}
const getColorWithAlpha = (color: string) => {
return 'rgb(255, 0, 0)';
};
const percent = Math.max(pos / canvasHeight, 0);
const realColor = tinycolor(theme.visualization.getColorByName(color)).setAlpha(opacity).toString();
const colorStopPos = Math.min(percent, 1);
const addColorStop = (value: number, color: string) => {
const pos = plot.valToPos(value, series.scale!);
const percent = pos / range;
console.log(`addColorStop(value = ${value}, xPos=${pos})`);
gradient.addColorStop(Math.min(percent, 1), getColorWithAlpha(color));
gradient.addColorStop(colorStopPos, realColor);
};
for (let idx = 0; idx < thresholds.steps.length; idx++) {
const step = thresholds.steps[idx];
const value = step.value === -Infinity ? 0 : step.value;
addColorStop(value, step.color);
if (colorMode.id === FieldColorModeId.Thresholds) {
for (let idx = 0; idx < thresholds.steps.length; idx++) {
const step = thresholds.steps[idx];
if (thresholds.mode === ThresholdsMode.Absolute) {
const value = step.value === -Infinity ? scaleMin : step.value;
addColorStop(value, step.color);
if (thresholds.steps.length > idx + 1) {
// to make the gradient discrete
addColorStop(thresholds.steps[idx + 1].value - 0.00000001, step.color);
}
} else {
const percent = step.value === -Infinity ? 0 : step.value;
const realValue = (percent / 100) * scaleRange;
addColorStop(realValue, step.color);
// to make the gradient discrete
if (thresholds.steps.length > idx + 1) {
// to make the gradient discrete
const nextValue = (thresholds.steps[idx + 1].value / 100) * scaleRange - 0.0000001;
addColorStop(nextValue, step.color);
}
}
}
} else if (colorMode.getColors) {
const colors = colorMode.getColors(theme);
const stepValue = (scaleMax - scaleMin) / colors.length;
// to make the gradient discrete
if (thresholds.steps.length > idx + 1) {
addColorStop(thresholds.steps[idx + 1].value - 0.0000001, step.color);
for (let idx = 0; idx < colors.length; idx++) {
addColorStop(scaleMin + stepValue * idx, colors[idx]);
}
}

@ -229,6 +229,24 @@ describe('getPanelOptionsWithDefaults', () => {
});
expect(result.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.Thresholds);
});
it('should change to classic mode when panel supports bySeries', () => {
const result = runScenario({
defaults: {
color: { mode: FieldColorModeId.Thresholds },
},
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: true,
bySeriesSupport: true,
},
},
},
isAfterPluginChange: true,
});
expect(result.fieldConfig.defaults.color!.mode).toBe(FieldColorModeId.PaletteClassic);
});
});
describe('when changing panel type to one that does not use standard field config', () => {

@ -166,6 +166,15 @@ function adaptFieldColorMode(
return fieldConfig;
}
}
// If panel support bySeries then we should default to that when switching to this panel as that is most likely
// what users will expect. Example scenario a user who has a graph panel (time series) and switches to Gauge and
// then back to time series we want the graph panel color mode to reset to classic palette and not preserve the
// Gauge prefered thresholds mode.
if (colorSettings.bySeriesSupport && mode?.isByValue) {
fieldConfig.defaults.color = { mode: FieldColorModeId.PaletteClassic };
return fieldConfig;
}
}
return fieldConfig;
}

@ -108,10 +108,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -233,10 +233,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -358,10 +358,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -483,10 +483,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -608,10 +608,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -733,10 +733,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -858,10 +858,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",
@ -983,10 +983,10 @@ Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"fill": "#808080",
"filter": undefined,
"size": undefined,
"stroke": undefined,
"stroke": "#808080",
},
"pxAlign": false,
"scale": "m/s",

@ -44,7 +44,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
standardOptions: {
[FieldConfigProperty.Color]: {
settings: {
byValueSupport: false,
byValueSupport: true,
bySeriesSupport: true,
preferThresholdsMode: false,
},

Loading…
Cancel
Save