mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
333d520528
commit
a241f03167
@ -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;
|
||||
|
||||
console.log('scale', scale);
|
||||
console.log('series.min', series.min);
|
||||
console.log('series.max', series.max);
|
||||
|
||||
const getColorWithAlpha = (color: string) => {
|
||||
return 'rgb(255, 0, 0)';
|
||||
};
|
||||
const scaleMin = scale.min ?? 0;
|
||||
const scaleMax = scale.max ?? 100;
|
||||
const scaleRange = scaleMax - scaleMin;
|
||||
|
||||
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));
|
||||
const pos = plot.valToPos(value, series.scale!, true);
|
||||
// when above range we get negative values here
|
||||
if (pos < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const percent = Math.max(pos / canvasHeight, 0);
|
||||
const realColor = tinycolor(theme.visualization.getColorByName(color)).setAlpha(opacity).toString();
|
||||
const colorStopPos = Math.min(percent, 1);
|
||||
|
||||
gradient.addColorStop(colorStopPos, realColor);
|
||||
};
|
||||
|
||||
if (colorMode.id === FieldColorModeId.Thresholds) {
|
||||
for (let idx = 0; idx < thresholds.steps.length; idx++) {
|
||||
const step = thresholds.steps[idx];
|
||||
const value = step.value === -Infinity ? 0 : step.value;
|
||||
|
||||
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) {
|
||||
addColorStop(thresholds.steps[idx + 1].value - 0.0000001, step.color);
|
||||
// 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;
|
||||
|
||||
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…
Reference in New Issue
Block a user