diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json b/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json new file mode 100644 index 00000000000..8e071d22fab --- /dev/null +++ b/devenv/dev-dashboards/panel-graph/graph-ng-by-value-color-schemes.json @@ -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 + } \ No newline at end of file diff --git a/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap b/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap index e9e66fb7b02..10283981b35 100644 --- a/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap +++ b/packages/grafana-ui/src/components/GraphNG/__snapshots__/utils.test.ts.snap @@ -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", diff --git a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx index cfb321c3bf8..a8bd93707b0 100755 --- a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx +++ b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx @@ -164,7 +164,6 @@ export class Sparkline extends PureComponent { lineInterpolation: customConfig.lineInterpolation, showPoints: pointsMode, pointSize: customConfig.pointSize, - pointColor: customConfig.pointColor ?? seriesColor, fillOpacity: customConfig.fillOpacity, fillColor: customConfig.fillColor ?? seriesColor, }); diff --git a/packages/grafana-ui/src/components/TimeSeries/utils.ts b/packages/grafana-ui/src/components/TimeSeries/utils.ts index ff977febb8f..8d5de8a672c 100644 --- a/packages/grafana-ui/src/components/TimeSeries/utils.ts +++ b/packages/grafana-ui/src/components/TimeSeries/utils.ts @@ -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, diff --git a/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx b/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx index 1144de00a0e..bc109700961 100644 --- a/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx +++ b/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx @@ -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 = ({ displayMode, ...vizLayoutLegendProps }) => { + const theme = useTheme2(); const legendItems = config .getSeries() .map((s) => { @@ -40,10 +49,13 @@ export const PlotLegend: React.FC = ({ } 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: () => { diff --git a/packages/grafana-ui/src/components/uPlot/config.ts b/packages/grafana-ui/src/components/uPlot/config.ts index ed62e779a2a..2837c310142 100644 --- a/packages/grafana-ui/src/components/uPlot/config.ts +++ b/packages/grafana-ui/src/components/uPlot/config.ts @@ -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>, stacking: [ diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts index aa70917d939..d169e42c036 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -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", diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts index 65b235a9962..c86d5ba53e1 100755 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts @@ -46,7 +46,6 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder { barWidthFactor, barMaxWidth, showPoints, - pointColor, pointSize, scaleKey, pxAlign, @@ -55,15 +54,16 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder { } = this.props; let lineConfig: Partial = {}; + 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 { const pointsConfig: Partial = { points: { - stroke: pointColor, - fill: pointColor, + stroke: lineColor, + fill: lineColor, size: pointSize, filter: pointsFilter, }, @@ -123,10 +123,10 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder { } 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 { 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(); diff --git a/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts b/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts index 0a2fc39c02d..09e8b078dfa 100644 --- a/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts +++ b/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts @@ -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]); } } diff --git a/public/app/features/dashboard/state/getPanelOptionsWithDefaults.test.ts b/public/app/features/dashboard/state/getPanelOptionsWithDefaults.test.ts index fdbdf90ed3c..6b61bdcaf60 100644 --- a/public/app/features/dashboard/state/getPanelOptionsWithDefaults.test.ts +++ b/public/app/features/dashboard/state/getPanelOptionsWithDefaults.test.ts @@ -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', () => { diff --git a/public/app/features/dashboard/state/getPanelOptionsWithDefaults.ts b/public/app/features/dashboard/state/getPanelOptionsWithDefaults.ts index 779e3a45ea5..2eb30b6d951 100644 --- a/public/app/features/dashboard/state/getPanelOptionsWithDefaults.ts +++ b/public/app/features/dashboard/state/getPanelOptionsWithDefaults.ts @@ -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; } diff --git a/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap b/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap index 5d69d0de21d..8d4bfe31dcd 100644 --- a/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap +++ b/public/app/plugins/panel/barchart/__snapshots__/utils.test.ts.snap @@ -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", diff --git a/public/app/plugins/panel/timeseries/config.ts b/public/app/plugins/panel/timeseries/config.ts index d461ae49f2d..be629fa2e06 100644 --- a/public/app/plugins/panel/timeseries/config.ts +++ b/public/app/plugins/panel/timeseries/config.ts @@ -44,7 +44,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption standardOptions: { [FieldConfigProperty.Color]: { settings: { - byValueSupport: false, + byValueSupport: true, bySeriesSupport: true, preferThresholdsMode: false, },