diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json b/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json index 75815d6c429..e5701b48e9c 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-gradient-area.json @@ -34,7 +34,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0.5, + "gradientMode": "opacity", "fillOpacity": 40, "lineInterpolation": "linear", "lineWidth": 2, @@ -122,7 +122,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "bars", - "fillGradient": 0.4, + "gradientNode": "opacity", "fillOpacity": 53, "lineInterpolation": "linear", "lineWidth": 1, @@ -211,7 +211,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "bars", - "fillGradient": "opacity", + "gradientMode": "opacity", "fillOpacity": 100, "lineInterpolation": "linear", "lineWidth": 0, @@ -300,7 +300,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0.5, + "gradientMode": "opacity", "fillOpacity": 20, "lineInterpolation": "linear", "lineWidth": 1, @@ -388,7 +388,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": "hue", + "gradientMode": "hue", "fillOpacity": 62, "lineInterpolation": "smooth", "lineWidth": 2, @@ -477,7 +477,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "bars", - "fillGradient": "hue", + "gradientMode": "hue", "fillOpacity": 100, "lineInterpolation": "smooth", "lineWidth": 0, diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json b/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json new file mode 100644 index 00000000000..7cf0f4a6f8a --- /dev/null +++ b/devenv/dev-dashboards/panel-graph/graph-ng-hue-gradients.json @@ -0,0 +1,892 @@ +{ + "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, + "id": 391, + "links": [], + "panels": [ + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 0, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 0 + }, + "id": 2, + "interval": "1m", + "links": [], + "maxDataPoints": 9, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "bars", + "fillOpacity": 72, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 1, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 0 + }, + "id": 3, + "interval": "1m", + "links": [], + "maxDataPoints": 9, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode (line + opacity)", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "bars", + "fillOpacity": 78, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 0 + }, + "id": 5, + "interval": "1m", + "links": [], + "maxDataPoints": 9, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode (line + opacity)", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 0, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 9 + }, + "id": 4, + "interval": "1m", + "links": [], + "maxDataPoints": 9, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 0, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 9 + }, + "id": 6, + "interval": "1m", + "links": [], + "maxDataPoints": 9, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "smooth", + "lineWidth": 0, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "rgb(12, 187, 242)", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 9 + }, + "id": 7, + "interval": "1m", + "links": [], + "maxDataPoints": 9, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "line", + "fillOpacity": 78, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "orange", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 0, + "y": 18 + }, + "id": 8, + "interval": "1m", + "links": [], + "maxDataPoints": 100, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode (line + opacity)", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "line", + "fillOpacity": 78, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 8, + "y": 18 + }, + "id": 9, + "interval": "1m", + "links": [], + "maxDataPoints": 100, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode (line + opacity)", + "type": "timeseries" + }, + { + "datasource": null, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisLabel": "", + "axisPlacement": "auto", + "drawStyle": "line", + "fillOpacity": 78, + "gradientMode": "hue", + "hideFrom": { + "graph": false, + "legend": false, + "tooltip": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 6, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": true + }, + "mappings": [], + "nullValueMode": "null", + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "A-series" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "green", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 8, + "x": 16, + "y": 18 + }, + "id": 10, + "interval": "1m", + "links": [], + "maxDataPoints": 100, + "options": { + "graph": {}, + "legend": { + "calcs": [], + "displayMode": "hidden", + "placement": "bottom" + }, + "tooltipOptions": { + "mode": "single" + } + }, + "pluginVersion": "7.4.0-pre", + "targets": [ + { + "refId": "A", + "scenarioId": "random_walk" + } + ], + "timeFrom": null, + "timeShift": null, + "title": "Hue gradient mode (line + opacity)", + "type": "timeseries" + } + ], + "schemaVersion": 27, + "style": "dark", + "tags": ["gdev", "panel-tests", "graph-ng"], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Panel Tests - GraphNG - Hue Gradients", + "uid": "k3XQFOBMk", + "version": 3 +} diff --git a/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json b/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json index 9bd94c8bd6b..c5dbb646638 100644 --- a/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json +++ b/devenv/dev-dashboards/panel-graph/graph-ng-nulls.json @@ -34,7 +34,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0, + "fillGradient": "none", "fillOpacity": 0, "hideFrom": { "graph": false, @@ -135,7 +135,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0, + "fillGradient": "none", "fillOpacity": 0, "hideFrom": { "graph": false, @@ -245,7 +245,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0, + "fillGradient": "none", "fillOpacity": 0, "hideFrom": { "graph": false, @@ -374,7 +374,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0, + "fillGradient": "none", "fillOpacity": 0, "hideFrom": { "graph": false, @@ -475,7 +475,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0, + "fillGradient": "none", "fillOpacity": 0, "hideFrom": { "graph": false, @@ -585,7 +585,7 @@ "axisLabel": "", "axisPlacement": "auto", "drawStyle": "line", - "fillGradient": 0, + "fillGradient": "none", "fillOpacity": 0, "hideFrom": { "graph": false, @@ -726,9 +726,7 @@ "fill": { "alpha": 0 }, - "fillGradient": { - "label": "None" - }, + "fillGradient": "none", "fillOpacity": 10, "hideFrom": { "graph": false, @@ -889,9 +887,7 @@ "fill": { "alpha": 0 }, - "fillGradient": { - "label": "None" - }, + "fillGradient": "none", "fillOpacity": 10, "hideFrom": { "graph": false, diff --git a/packages/grafana-data/src/field/fieldColor.test.ts b/packages/grafana-data/src/field/fieldColor.test.ts index 6949a25284b..295e71af681 100644 --- a/packages/grafana-data/src/field/fieldColor.test.ts +++ b/packages/grafana-data/src/field/fieldColor.test.ts @@ -1,18 +1,35 @@ -import { Field, FieldColorModeId } from '../types'; +import { Field, FieldColorModeId, FieldType } from '../types'; import { getTestTheme } from '../utils/testdata/testTheme'; -import { fieldColorModeRegistry, FieldValueColorCalculator } from './fieldColor'; +import { ArrayVector } from '../vector/ArrayVector'; +import { fieldColorModeRegistry, FieldValueColorCalculator, getFieldSeriesColor } from './fieldColor'; + +function getTestField(mode: string): Field { + return { + name: 'name', + type: FieldType.number, + values: new ArrayVector(), + config: { + color: { + mode: mode, + } as any, + }, + state: {}, + }; +} + +interface GetCalcOptions { + mode: string; + seriesIndex?: number; +} + +function getCalculator(options: GetCalcOptions): FieldValueColorCalculator { + const field = getTestField(options.mode); + const mode = fieldColorModeRegistry.get(options.mode); + field.state!.seriesIndex = options.seriesIndex; + return mode.getCalculator(field, getTestTheme()); +} describe('fieldColorModeRegistry', () => { - interface GetCalcOptions { - mode: string; - seriesIndex?: number; - } - - function getCalculator(options: GetCalcOptions): FieldValueColorCalculator { - const mode = fieldColorModeRegistry.get(options.mode); - return mode.getCalculator({ state: { seriesIndex: options.seriesIndex } } as Field, getTestTheme()); - } - it('Schemes should interpolate', () => { const calcFn = getCalculator({ mode: 'continuous-GrYlRd' }); expect(calcFn(70, 0.5, undefined)).toEqual('rgb(226, 192, 61)'); @@ -27,4 +44,48 @@ describe('fieldColorModeRegistry', () => { const calcFn = getCalculator({ mode: FieldColorModeId.PaletteClassic, seriesIndex: 1 }); expect(calcFn(70, 0, undefined)).toEqual('#EAB839'); }); + + it('When color.seriesBy is set to last use that instead of v', () => { + const field = getTestField('continuous-GrYlRd'); + + field.config.color!.seriesBy = 'last'; + // min = -10, max = 10, last = 5 + // last percent 75% + field.values = new ArrayVector([0, -10, 5, 10, 2, 5]); + + const color = getFieldSeriesColor(field, getTestTheme()); + const calcFn = getCalculator({ mode: 'continuous-GrYlRd' }); + + expect(color.color).toEqual(calcFn(4, 0.75)); + }); +}); + +describe('getFieldSeriesColor', () => { + const field = getTestField('continuous-GrYlRd'); + field.values = new ArrayVector([0, -10, 5, 10, 2, 5]); + + it('When color.seriesBy is last use that to calc series color', () => { + field.config.color!.seriesBy = 'last'; + const color = getFieldSeriesColor(field, getTestTheme()); + const calcFn = getCalculator({ mode: 'continuous-GrYlRd' }); + + // the 4 can be anything, 0.75 comes from 5 being 75% in the range -10 to 10 (see data above) + expect(color.color).toEqual(calcFn(4, 0.75)); + }); + + it('When color.seriesBy is max use that to calc series color', () => { + field.config.color!.seriesBy = 'max'; + const color = getFieldSeriesColor(field, getTestTheme()); + const calcFn = getCalculator({ mode: 'continuous-GrYlRd' }); + + expect(color.color).toEqual(calcFn(10, 1)); + }); + + it('When color.seriesBy is min use that to calc series color', () => { + field.config.color!.seriesBy = 'min'; + const color = getFieldSeriesColor(field, getTestTheme()); + const calcFn = getCalculator({ mode: 'continuous-GrYlRd' }); + + expect(color.color).toEqual(calcFn(-10, 0)); + }); }); diff --git a/packages/grafana-data/src/field/fieldColor.ts b/packages/grafana-data/src/field/fieldColor.ts index ba7db8eae4e..7cf39b6e83d 100644 --- a/packages/grafana-data/src/field/fieldColor.ts +++ b/packages/grafana-data/src/field/fieldColor.ts @@ -3,6 +3,8 @@ import { classicColors, getColorForTheme, RegistryItem } from '../utils'; import { Registry } from '../utils/Registry'; import { interpolateRgbBasis } from 'd3-interpolate'; import { fallBackTreshold } from './thresholds'; +import { getScaleCalculator, ColorScaleValue } from './scale'; +import { reduceField } from '../transformations/fieldReducer'; export type FieldValueColorCalculator = (value: number, percent: number, Threshold?: Threshold) => string; @@ -209,6 +211,30 @@ export function getFieldColorMode(mode?: FieldColorModeId): FieldColorMode { return fieldColorModeRegistry.get(mode ?? FieldColorModeId.Thresholds); } +/** + * @alpha + * Function that will return a series color for any given color mode. If the color mode is a by value color + * mode it will use the field.config.color.seriesBy property to figure out which value to use + */ +export function getFieldSeriesColor(field: Field, theme: GrafanaTheme): ColorScaleValue { + const mode = getFieldColorModeForField(field); + + if (!mode.isByValue) { + return { + color: mode.getCalculator(field, theme)(0, 0), + threshold: fallBackTreshold, + percent: 1, + }; + } + + const scale = getScaleCalculator(field, theme); + const stat = field.config.color?.seriesBy ?? 'last'; + const calcs = reduceField({ field, reducers: [stat] }); + const value = calcs[stat] ?? 0; + + return scale(value); +} + function getFixedColor(field: Field, theme: GrafanaTheme) { return () => { return getColorForTheme(field.config.color?.fixedColor ?? FALLBACK_COLOR, theme); diff --git a/packages/grafana-data/src/field/index.ts b/packages/grafana-data/src/field/index.ts index 33cbbda7af8..93a2287b2e8 100644 --- a/packages/grafana-data/src/field/index.ts +++ b/packages/grafana-data/src/field/index.ts @@ -3,7 +3,13 @@ export * from './displayProcessor'; export * from './standardFieldConfigEditorRegistry'; export * from './overrides/processors'; -export { getFieldColorModeForField, getFieldColorMode, fieldColorModeRegistry, FieldColorMode } from './fieldColor'; +export { + getFieldColorModeForField, + getFieldColorMode, + fieldColorModeRegistry, + FieldColorMode, + getFieldSeriesColor, +} from './fieldColor'; export { FieldConfigOptionsRegistry } from './FieldConfigOptionsRegistry'; export { sortThresholds, getActiveThreshold } from './thresholds'; export { applyFieldOverrides, validateFieldConfig, applyRawFieldOverrides } from './fieldOverrides'; diff --git a/packages/grafana-data/src/field/overrides/processors.ts b/packages/grafana-data/src/field/overrides/processors.ts index b620471163a..0a36bfcf0e8 100644 --- a/packages/grafana-data/src/field/overrides/processors.ts +++ b/packages/grafana-data/src/field/overrides/processors.ts @@ -131,6 +131,11 @@ export interface FieldColorConfigSettings { * to from thresholds if it was set to a by series palette */ preferThresholdsMode?: boolean; + /** + * Set to true if the visualization supports both by value and by series + * This will enable the Color by series UI option that sets the `color.seriesBy` option. + */ + bySeriesSupport?: boolean; } export interface StatsPickerConfigSettings { diff --git a/packages/grafana-data/src/field/scale.ts b/packages/grafana-data/src/field/scale.ts index 35d76d35095..f041d041c4f 100644 --- a/packages/grafana-data/src/field/scale.ts +++ b/packages/grafana-data/src/field/scale.ts @@ -4,13 +4,13 @@ import { Field, FieldConfig, FieldType, GrafanaTheme, NumericRange, Threshold } import { getFieldColorModeForField } from './fieldColor'; import { getActiveThresholdForValue } from './thresholds'; -export interface ScaledValue { +export interface ColorScaleValue { percent: number; // 0-1 threshold: Threshold; color: string; } -export type ScaleCalculator = (value: number) => ScaledValue; +export type ScaleCalculator = (value: number) => ColorScaleValue; export function getScaleCalculator(field: Field, theme: GrafanaTheme): ScaleCalculator { const mode = getFieldColorModeForField(field); diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 0865808c571..0551daf5100 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -18,5 +18,5 @@ export { BasicValueMatcherOptions, RangeValueMatcherOptions, } from './transformations/matchers/valueMatchers/types'; -export { PanelPlugin } from './panel/PanelPlugin'; +export { PanelPlugin, SetFieldConfigOptionsArgs } from './panel/PanelPlugin'; export { createFieldConfigRegistry } from './panel/registryFactories'; diff --git a/packages/grafana-data/src/panel/PanelPlugin.ts b/packages/grafana-data/src/panel/PanelPlugin.ts index ddb5cb5a572..3b5e2f9d2af 100644 --- a/packages/grafana-data/src/panel/PanelPlugin.ts +++ b/packages/grafana-data/src/panel/PanelPlugin.ts @@ -21,6 +21,7 @@ type StandardOptionConfig = { settings?: any; }; +/** @beta */ export interface SetFieldConfigOptionsArgs { /** * Configuration object of the standard field config properites diff --git a/packages/grafana-data/src/types/fieldColor.ts b/packages/grafana-data/src/types/fieldColor.ts index f0929679574..cb4b89bb508 100644 --- a/packages/grafana-data/src/types/fieldColor.ts +++ b/packages/grafana-data/src/types/fieldColor.ts @@ -1,3 +1,6 @@ +/** + * @public + */ export enum FieldColorModeId { Thresholds = 'thresholds', PaletteClassic = 'palette-classic', @@ -5,9 +8,21 @@ export enum FieldColorModeId { Fixed = 'fixed', } +/** + * @public + */ export interface FieldColor { + /** The main color scheme mode */ mode: FieldColorModeId; + /** Stores the fixed color value if mode is fixed */ fixedColor?: string; + /** Some visualizations need to know how to assign a series color from by value color schemes */ + seriesBy?: FieldColorSeriesByMode; } +/** + * @beta + */ +export type FieldColorSeriesByMode = 'min' | 'max' | 'last'; + export const FALLBACK_COLOR = 'gray'; diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index 188c8c10c3b..13eeaa98c16 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -8,7 +8,6 @@ import { fieldReducers, FieldType, formattedValueToString, - getFieldColorModeForField, getFieldDisplayName, reduceField, TimeRange, @@ -23,6 +22,7 @@ import { LegendDisplayMode, VizLegendItem, VizLegendOptions } from '../VizLegend import { VizLegend } from '../VizLegend/VizLegend'; import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder'; import { useRevision } from '../uPlot/hooks'; +import { getFieldColorModeForField, getFieldSeriesColor } from '@grafana/data'; import { GraphNGLegendEvent, GraphNGLegendEventMode } from './types'; import { isNumber } from 'lodash'; @@ -109,6 +109,7 @@ export const GraphNG: React.FC = ({ // X is the first field in the aligned frame const xField = alignedFrame.fields[0]; + if (xField.type === FieldType.time) { builder.addScale({ scaleKey: 'x', @@ -118,6 +119,7 @@ export const GraphNG: React.FC = ({ return [r.from.valueOf(), r.to.valueOf()]; }, }); + builder.addAxis({ scaleKey: 'x', isTime: true, @@ -130,6 +132,7 @@ export const GraphNG: React.FC = ({ builder.addScale({ scaleKey: 'x', }); + builder.addAxis({ scaleKey: 'x', placement: AxisPlacement.Bottom, @@ -153,7 +156,8 @@ export const GraphNG: React.FC = ({ const fmt = field.display ?? defaultFormatter; const scaleKey = config.unit || FIXED_UNIT; const colorMode = getFieldColorModeForField(field); - const seriesColor = colorMode.getCalculator(field, theme)(0, 0); + const scaleColor = getFieldSeriesColor(field, theme); + const seriesColor = scaleColor.color; if (customConfig.axisPlacement !== AxisPlacement.Hidden) { // The builder will manage unique scaleKeys and combine where appropriate @@ -200,18 +204,21 @@ export const GraphNG: React.FC = ({ builder.addSeries({ scaleKey, + showPoints, + colorMode, + fillOpacity, + theme, drawStyle: customConfig.drawStyle!, lineColor: customConfig.lineColor ?? seriesColor, lineWidth: customConfig.lineWidth, lineInterpolation: customConfig.lineInterpolation, lineStyle: customConfig.lineStyle, - showPoints, pointSize: customConfig.pointSize, pointColor: customConfig.pointColor ?? seriesColor, - fillOpacity, spanNulls: customConfig.spanNulls || false, show: !customConfig.hideFrom?.graph, - fillGradient: customConfig.fillGradient, + gradientMode: customConfig.gradientMode, + thresholds: config.thresholds, // The following properties are not used in the uPlot config, but are utilized as transport for legend config dataFrameFieldIndex, diff --git a/packages/grafana-ui/src/components/OptionsUI/fieldColor.tsx b/packages/grafana-ui/src/components/OptionsUI/fieldColor.tsx index 04d01714d12..659df8b0384 100644 --- a/packages/grafana-ui/src/components/OptionsUI/fieldColor.tsx +++ b/packages/grafana-ui/src/components/OptionsUI/fieldColor.tsx @@ -9,11 +9,15 @@ import { GrafanaTheme, getColorForTheme, FieldColorConfigSettings, + FieldColorSeriesByMode, + getFieldColorMode, } from '@grafana/data'; import { Select } from '../Select/Select'; import { ColorValueEditor } from './color'; import { useStyles, useTheme } from '../../themes/ThemeContext'; import { css } from 'emotion'; +import { Field } from '../Forms/Field'; +import { RadioButtonGroup } from '../Forms/RadioButtonGroup/RadioButtonGroup'; export const FieldColorEditor: React.FC> = ({ value, @@ -23,6 +27,7 @@ export const FieldColorEditor: React.FC !m.isByValue); @@ -44,17 +49,27 @@ export const FieldColorEditor: React.FC) => { onChange({ + ...value, mode: newMode.value! as FieldColorModeId, }); }; const onColorChange = (color?: string) => { onChange({ + ...value, mode, fixedColor: color, }); }; + const onSeriesModeChange = (seriesBy?: FieldColorSeriesByMode) => { + onChange({ + ...value, + mode, + seriesBy, + }); + }; + const mode = value?.mode ?? FieldColorModeId.Thresholds; if (mode === FieldColorModeId.Fixed) { @@ -66,6 +81,25 @@ export const FieldColorEditor: React.FC> = [ + { label: 'Last', value: 'last' }, + { label: 'Min', value: 'min' }, + { label: 'Max', value: 'max' }, + ]; + + return ( + <> +
+ ; }; diff --git a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx index 9cba4f1e366..746d2cf3b37 100755 --- a/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx +++ b/packages/grafana-ui/src/components/Sparkline/Sparkline.tsx @@ -137,6 +137,7 @@ export class Sparkline extends PureComponent { builder.addSeries({ scaleKey, + theme, fieldName: getFieldDisplayName(field, data), drawStyle: customConfig.drawStyle!, lineColor: customConfig.lineColor ?? seriesColor, diff --git a/packages/grafana-ui/src/components/uPlot/config.ts b/packages/grafana-ui/src/components/uPlot/config.ts index 74c2de3c4e9..847b663a64c 100644 --- a/packages/grafana-ui/src/components/uPlot/config.ts +++ b/packages/grafana-ui/src/components/uPlot/config.ts @@ -73,17 +73,17 @@ export interface LineConfig { export interface FillConfig { fillColor?: string; fillOpacity?: number; - fillGradient?: FillGradientMode; fillBelowTo?: string; // name of the field } /** * @alpha */ -export enum FillGradientMode { +export enum GraphGradientMode { None = 'none', Opacity = 'opacity', Hue = 'hue', + Scheme = 'scheme', } /** @@ -131,6 +131,7 @@ export interface HideSeriesConfig { */ export interface GraphFieldConfig extends LineConfig, FillConfig, PointsConfig, AxisConfig { drawStyle?: DrawStyle; + gradientMode?: GraphGradientMode; hideFrom?: HideSeriesConfig; } @@ -165,8 +166,9 @@ export const graphFieldOptions = { ] as Array>, fillGradient: [ - { label: 'None', value: FillGradientMode.None }, - { label: 'Opacity', value: FillGradientMode.Opacity }, - { label: 'Hue', value: FillGradientMode.Hue }, - ] as Array>, + { label: 'None', value: GraphGradientMode.None }, + { label: 'Opacity', value: GraphGradientMode.Opacity }, + { label: 'Hue', value: GraphGradientMode.Hue }, + // { label: 'Color scheme', value: GraphGradientMode.Scheme }, + ] as Array>, }; 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 d09ef0e5a81..b7c414ce6b4 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -2,8 +2,7 @@ import { UPlotConfigBuilder } from './UPlotConfigBuilder'; import { GrafanaTheme } from '@grafana/data'; -import { expect } from '../../../../../../public/test/lib/common'; -import { FillGradientMode, AxisPlacement, DrawStyle, PointVisibility, ScaleDistribution } from '../config'; +import { GraphGradientMode, AxisPlacement, DrawStyle, PointVisibility, ScaleDistribution } from '../config'; import darkTheme from '../../../themes/dark'; describe('UPlotConfigBuilder', () => { @@ -327,6 +326,7 @@ describe('UPlotConfigBuilder', () => { scaleKey: 'scale-x', fieldName: 'A-series', lineColor: '#0000ff', + theme: darkTheme, }); expect(builder.getConfig().series[1].fill).toBe(undefined); @@ -340,6 +340,7 @@ describe('UPlotConfigBuilder', () => { fieldName: 'A-series', lineColor: '#FFAABB', fillOpacity: 50, + theme: darkTheme, }); expect(builder.getConfig().series[1].fill).toBe('rgba(255, 170, 187, 0.5)'); @@ -354,6 +355,7 @@ describe('UPlotConfigBuilder', () => { lineColor: '#FFAABB', fillOpacity: 50, fillColor: '#FF0000', + theme: darkTheme, }); expect(builder.getConfig().series[1].fill).toBe('#FF0000'); @@ -367,7 +369,8 @@ describe('UPlotConfigBuilder', () => { fieldName: 'A-series', lineColor: '#FFAABB', fillOpacity: 50, - fillGradient: FillGradientMode.Opacity, + gradientMode: GraphGradientMode.Opacity, + theme: darkTheme, }); expect(builder.getConfig().series[1].fill).toBeInstanceOf(Function); @@ -380,13 +383,14 @@ describe('UPlotConfigBuilder', () => { scaleKey: 'scale-x', fieldName: 'A-series', fillOpacity: 50, - fillGradient: FillGradientMode.Opacity, + gradientMode: GraphGradientMode.Opacity, showPoints: PointVisibility.Auto, pointSize: 5, pointColor: '#00ff00', lineColor: '#0000ff', lineWidth: 1, spanNulls: false, + theme: darkTheme, }); expect(builder.getConfig()).toMatchInlineSnapshot(` diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts index 9da09029701..721aec0b862 100755 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotSeriesBuilder.ts @@ -1,6 +1,6 @@ +import { FALLBACK_COLOR, FieldColorMode, GrafanaTheme, ThresholdsConfig } from '@grafana/data'; import tinycolor from 'tinycolor2'; import uPlot, { Series } from 'uplot'; -import { getCanvasContext } from '../../../utils/measureText'; import { DrawStyle, LineConfig, @@ -8,18 +8,25 @@ import { PointsConfig, PointVisibility, LineInterpolation, - FillGradientMode, + GraphGradientMode, } from '../config'; import { PlotConfigBuilder } from '../types'; import { DataFrameFieldIndex } from '@grafana/data'; +import { getScaleGradientFn, getOpacityGradientFn, getHueGradientFn } from './gradientFills'; export interface SeriesProps extends LineConfig, FillConfig, PointsConfig { scaleKey: string; + gradientMode?: GraphGradientMode; + /** Used when gradientMode is set to Scheme */ + thresholds?: ThresholdsConfig; + /** Used when gradientMode is set to Scheme */ + colorMode?: FieldColorMode; fieldName: string; drawStyle: DrawStyle; show?: boolean; dataFrameFieldIndex?: DataFrameFieldIndex; hideInLegend?: boolean; + theme: GrafanaTheme; } export class UPlotSeriesBuilder extends PlotConfigBuilder { @@ -27,7 +34,6 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder { const { drawStyle, lineInterpolation, - lineColor, lineWidth, lineStyle, showPoints, @@ -43,7 +49,7 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder { if (drawStyle === DrawStyle.Points) { lineConfig.paths = () => null; } else { - lineConfig.stroke = lineColor; + lineConfig.stroke = this.getLineColor(); lineConfig.width = lineWidth; if (lineStyle && lineStyle.fill !== 'solid') { if (lineStyle.fill === 'dot') { @@ -90,28 +96,39 @@ export class UPlotSeriesBuilder extends PlotConfigBuilder { }; } - getFill(): Series.Fill | undefined { - const { lineColor, fillColor, fillGradient, fillOpacity } = this.props; + private getLineColor(): Series.Stroke { + const { lineColor, gradientMode, colorMode, thresholds } = this.props; + + if (gradientMode === GraphGradientMode.Scheme) { + return getScaleGradientFn(1, colorMode, thresholds); + } + + return lineColor ?? FALLBACK_COLOR; + } + + private getFill(): Series.Fill | undefined { + const { lineColor, fillColor, gradientMode, fillOpacity, colorMode, thresholds, theme } = this.props; if (fillColor) { return fillColor; } - const mode = fillGradient ?? FillGradientMode.None; - let fillOpacityNumber = fillOpacity ?? 0; + const mode = gradientMode ?? GraphGradientMode.None; + const opacityPercent = (fillOpacity ?? 0) / 100; - if (mode !== FillGradientMode.None) { - return getCanvasGradient({ - color: (fillColor ?? lineColor)!, - opacity: fillOpacityNumber / 100, - mode, - }); - } - - if (fillOpacityNumber > 0) { - return tinycolor(lineColor) - .setAlpha(fillOpacityNumber / 100) - .toString(); + switch (mode) { + case GraphGradientMode.Opacity: + return getOpacityGradientFn((fillColor ?? lineColor)!, opacityPercent); + case GraphGradientMode.Hue: + return getHueGradientFn((fillColor ?? lineColor)!, opacityPercent, theme); + case GraphGradientMode.Scheme: + return getScaleGradientFn(opacityPercent, colorMode, thresholds); + default: + if (opacityPercent > 0) { + return tinycolor(lineColor) + .setAlpha(opacityPercent) + .toString(); + } } return undefined; @@ -165,50 +182,3 @@ function mapDrawStyleToPathBuilder( return builders.linear; // the default } - -interface AreaGradientOptions { - color: string; - mode: FillGradientMode; - opacity: number; -} - -function getCanvasGradient(opts: AreaGradientOptions): (self: uPlot, seriesIdx: number) => CanvasGradient { - return (plot: uPlot, seriesIdx: number) => { - const { color, mode, opacity } = opts; - - const ctx = getCanvasContext(); - const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + plot.bbox.height); - - switch (mode) { - case FillGradientMode.Hue: - const color1 = tinycolor(color) - .spin(-25) - .darken(30) - .setAlpha(opacity) - .toRgbString(); - const color2 = tinycolor(color) - .spin(25) - .lighten(35) - .setAlpha(opacity) - .toRgbString(); - gradient.addColorStop(0, color2); - gradient.addColorStop(1, color1); - - case FillGradientMode.Opacity: - default: - gradient.addColorStop( - 0, - tinycolor(color) - .setAlpha(opacity) - .toRgbString() - ); - gradient.addColorStop( - 1, - tinycolor(color) - .setAlpha(0) - .toRgbString() - ); - return gradient; - } - }; -} diff --git a/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts b/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts new file mode 100644 index 00000000000..188431d2363 --- /dev/null +++ b/packages/grafana-ui/src/components/uPlot/config/gradientFills.ts @@ -0,0 +1,125 @@ +import { FieldColorMode, getColorForTheme, GrafanaTheme, ThresholdsConfig } from '@grafana/data'; +import tinycolor from 'tinycolor2'; +import uPlot from 'uplot'; +import darkTheme from '../../../themes/dark'; +import { getCanvasContext } from '../../../utils/measureText'; + +export function getOpacityGradientFn( + color: string, + opacity: number +): (self: uPlot, seriesIdx: number) => CanvasGradient { + return (plot: uPlot, seriesIdx: number) => { + const ctx = getCanvasContext(); + const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + plot.bbox.height); + + gradient.addColorStop( + 0, + tinycolor(color) + .setAlpha(opacity) + .toRgbString() + ); + gradient.addColorStop( + 1, + tinycolor(color) + .setAlpha(0) + .toRgbString() + ); + + return gradient; + }; +} +export function getHueGradientFn( + color: string, + opacity: number, + theme: GrafanaTheme +): (self: uPlot, seriesIdx: number) => CanvasGradient { + return (plot: uPlot, seriesIdx: number) => { + const ctx = getCanvasContext(); + const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + plot.bbox.height); + const color1 = tinycolor(color).spin(-15); + const color2 = tinycolor(color).spin(15); + + if (theme.isDark) { + gradient.addColorStop( + 0, + color2 + .lighten(10) + .setAlpha(opacity) + .toString() + ); + gradient.addColorStop( + 1, + color1 + .darken(10) + .setAlpha(opacity) + .toString() + ); + } else { + gradient.addColorStop( + 0, + color2 + .lighten(10) + .setAlpha(opacity) + .toString() + ); + gradient.addColorStop(1, color1.setAlpha(opacity).toString()); + } + + return gradient; + }; +} +/** + * Experimental & quick and dirty test + * Not being used + */ +export function getScaleGradientFn( + opacity: number, + colorMode?: FieldColorMode, + thresholds?: ThresholdsConfig +): (self: uPlot, seriesIdx: number) => CanvasGradient { + if (!colorMode) { + throw Error('Missing colorMode required for color scheme gradients'); + } + + if (!thresholds) { + throw Error('Missing thresholds required for color scheme gradients'); + } + + return (plot: uPlot, seriesIdx: number) => { + const ctx = getCanvasContext(); + const gradient = ctx.createLinearGradient(0, plot.bbox.top, 0, plot.bbox.top + 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 tinycolor(getColorForTheme(color, darkTheme)) + .setAlpha(opacity) + .toString(); + }; + + 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)); + }; + + 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); + + // to make the gradient discrete + if (thresholds.steps.length > idx + 1) { + addColorStop(thresholds.steps[idx + 1].value - 0.0000001, step.color); + } + } + + return gradient; + }; +} diff --git a/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap b/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap index 2fa2dfa64b6..b61f851b6fd 100644 --- a/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap +++ b/public/app/plugins/panel/timeseries/__snapshots__/migrations.test.ts.snap @@ -89,8 +89,8 @@ Object { "custom": Object { "axisPlacement": "hidden", "drawStyle": "line", - "fillGradient": "opacity", "fillOpacity": 60, + "gradientMode": "opacity", "lineInterpolation": "stepAfter", "lineWidth": 1, "pointSize": 6, diff --git a/public/app/plugins/panel/timeseries/config.ts b/public/app/plugins/panel/timeseries/config.ts index 217147a0166..c6094e0a908 100644 --- a/public/app/plugins/panel/timeseries/config.ts +++ b/public/app/plugins/panel/timeseries/config.ts @@ -3,6 +3,7 @@ import { FieldConfigProperty, FieldType, identityOverrideProcessor, + SetFieldConfigOptionsArgs, stringOverrideProcessor, } from '@grafana/data'; import { @@ -15,12 +16,11 @@ import { PointVisibility, ScaleDistribution, ScaleDistributionConfig, + GraphGradientMode, } from '@grafana/ui'; import { SeriesConfigEditor } from './HideSeriesConfigEditor'; import { ScaleDistributionEditor } from './ScaleDistributionEditor'; import { LineStyleEditor } from './LineStyleEditor'; -import { SetFieldConfigOptionsArgs } from '@grafana/data/src/panel/PanelPlugin'; -import { FillGradientMode } from '@grafana/ui/src/components/uPlot/config'; import { FillBellowToEditor } from './FillBelowToEditor'; export const defaultGraphConfig: GraphFieldConfig = { @@ -28,7 +28,7 @@ export const defaultGraphConfig: GraphFieldConfig = { lineInterpolation: LineInterpolation.Linear, lineWidth: 1, fillOpacity: 0, - fillGradient: FillGradientMode.None, + gradientMode: GraphGradientMode.None, }; export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOptionsArgs { @@ -37,6 +37,8 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption [FieldConfigProperty.Color]: { settings: { byValueSupport: false, + bySeriesSupport: true, + preferThresholdsMode: false, }, defaultValue: { mode: FieldColorModeId.PaletteClassic, @@ -85,13 +87,13 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption showIf: c => c.drawStyle !== DrawStyle.Points, }) .addRadio({ - path: 'fillGradient', - name: 'Fill gradient', - defaultValue: graphFieldOptions.fillGradient[0].value, + path: 'gradientMode', + name: 'Gradient mode', + defaultValue: graphFieldOptions.fillGradient[0], settings: { options: graphFieldOptions.fillGradient, }, - showIf: c => !!(c.drawStyle !== DrawStyle.Points && c.fillOpacity && c.fillOpacity > 0), + showIf: c => c.drawStyle !== DrawStyle.Points, }) .addCustomEditor({ id: 'fillBelowTo', diff --git a/public/app/plugins/panel/timeseries/migrations.ts b/public/app/plugins/panel/timeseries/migrations.ts index f4859fc91b9..8959fc757c3 100644 --- a/public/app/plugins/panel/timeseries/migrations.ts +++ b/public/app/plugins/panel/timeseries/migrations.ts @@ -12,9 +12,9 @@ import { } from '@grafana/data'; import { GraphFieldConfig, LegendDisplayMode } from '@grafana/ui'; import { + GraphGradientMode, AxisPlacement, DrawStyle, - FillGradientMode, LineInterpolation, LineStyle, PointVisibility, @@ -251,7 +251,7 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour } if (isNumber(angular.fillGradient) && angular.fillGradient > 0) { - graph.fillGradient = FillGradientMode.Opacity; + graph.gradientMode = GraphGradientMode.Opacity; graph.fillOpacity = angular.fillGradient * 10; // fill is 0-10 }