GraphNG: stacking (#30749)

* First iteration

* Dev dash

* Re-use StackingMode type

* Fix ts and api issues

* Stacking work resurected

* Fix overrides

* Correct values in tooltip and updated test dashboard

* Update dev dashboard

* Apply correct bands for stacking

* Merge fix

* Update snapshot

* Revert go.sum

* Handle null values correctyl and make filleBelowTo and stacking mutual exclusive

* Snapshots update

* Graph->Time series stacking migration

* Review comments

* Indicate overrides in StandardEditorContext

* Change stacking UI editor, migrate stacking to object option

* Small refactor, fix for hiding series and dev dashboard
This commit is contained in:
Dominik Prokop
2021-04-15 13:00:01 +02:00
committed by GitHub
parent 04a8d5407e
commit 0cc620aea7
31 changed files with 2183 additions and 92 deletions

View File

@ -0,0 +1,896 @@
{
"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": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 0,
"y": 0
},
"id": 14,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 15, 14, 30, 30, 30, 30, 30, 30, 30, -15, 30, 30, 30, 15, 0"
},
{
"hide": false,
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 10, 10"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 10, 10"
},
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5"
},
{
"hide": false,
"refId": "E",
"scenarioId": "csv_metric_values",
"stringInput": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Data not stacked",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "normal"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 16,
"x": 8,
"y": 0
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 15, 14, 30, 30, 30, 30, 30, 30, 30, -15, 30, 30, 30, 15, 0"
},
{
"hide": false,
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 10, 10"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 10, 10"
},
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5"
},
{
"hide": false,
"refId": "E",
"scenarioId": "csv_metric_values",
"stringInput": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Normal stacking (series order 1)",
"type": "timeseries"
},
{
"datasource": null,
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "normal"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 16,
"x": 8,
"y": 8
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5"
},
{
"hide": false,
"refId": "E",
"scenarioId": "csv_metric_values",
"stringInput": "5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5"
},
{
"hide": false,
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 10, 10"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 20, 10, 10, 10, 10, 10"
},
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 15, 14, 30, 30, 30, 30, 30, 30, 30, -15, 30, 30, 30, 15, 0"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Normal stacking (series order 2)",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 100,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 16,
"w": 8,
"x": 0,
"y": 16
},
"id": 9,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "109,117,122,104,105,117,119,121,117,121,122,129,119,113,113,121,108,108,100,103,113,110,null,null,null,null,87,83,91,85,81,69,76,61,63,74,76,68,55,61,48,39,54,44,37,30,22,33,29,21,22,43,47,33,47,28,29,31,32,35,37,25,-5,-14,-7,-14,-7,-18,-18,-18,-16,-41,-22,-30,-27,-30,-47,-49,-47,-42,-55,-34,-27,-22,-23,-34,-23,-32,-36,-47,-33,-32,-18,-23,-21,-33,-39,-21,-18,-27,-5"
},
{
"hide": false,
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "253,248,247,282,212,275,306,276,253,243,276,261,313,233,231,315,302,288,251,300,263,227,232,271,258,212,225,234,244,200,249,205,231,173,263,246,190,184,198,233,246,232,228,201,212,163,194,155,177,179,130,160,245,184,225,221,217,229,165,172,227,212,181,133,140,128,111,177,177,159,92,100,92,154,161,124,83,99,104,76,117,87,151,154,77,71,164,68,94,105,107,74,130,156,91,103,97,98,96,76,117"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "408,436,368,422,383,454,459,380,360,400,378,419,468,394,343,457,411,422,448,419,431,389,414,381,415,357,419,398,352,398,379,400,337,299,369,416,329,303,333,362,438,383,350,337,348,289,362,298,355,285,294,319,445,308,375,387,337,406,323,372,342,389,311,254,270,248,271,309,374,323,255,289,223,280,331,237,247,229,293,230,239,242,271,343,255,185,296,217,236,268,257,273,323,346,205,285,277,265,277,253,219"
},
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "556,581,482,552,550,589,575,565,471,575,565,605,663,544,471,641,567,535,630,535,623,579,582,559,611,509,521,563,513,544,523,558,450,486,478,584,440,432,473,480,555,510,451,476,490,470,555,418,463,402,403,476,591,431,560,581,532,579,427,501,452,551,479,405,397,376,458,439,490,508,389,488,360,413,509,351,434,392,430,426,375,414,401,459,435,327,491,406,397,406,384,423,439,538,338,467,421,406,477,428,371"
}
],
"title": "Bars, data not stacked",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 100,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "normal"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 16,
"x": 8,
"y": 16
},
"id": 11,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"stacking": "none",
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "109,117,122,104,105,117,119,121,117,121,122,129,119,113,113,121,108,108,100,103,113,110,null,null,null,null,87,83,91,85,81,69,76,61,63,74,76,68,55,61,48,39,54,44,37,30,22,33,29,21,22,43,47,33,47,28,29,31,32,35,37,25,-5,-14,-7,-14,-7,-18,-18,-18,-16,-41,-22,-30,-27,-30,-47,-49,-47,-42,-55,-34,-27,-22,-23,-34,-23,-32,-36,-47,-33,-32,-18,-23,-21,-33,-39,-21,-18,-27,-5"
},
{
"hide": false,
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "253,248,247,282,212,275,306,276,253,243,276,261,313,233,231,315,302,288,251,300,263,227,232,271,258,212,225,234,244,200,249,205,231,173,263,246,190,184,198,233,246,232,228,201,212,163,194,155,177,179,130,160,245,184,225,221,217,229,165,172,227,212,181,133,140,128,111,177,177,159,92,100,92,154,161,124,83,99,104,76,117,87,151,154,77,71,164,68,94,105,107,74,130,156,91,103,97,98,96,76,117"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "408,436,368,422,383,454,459,380,360,400,378,419,468,394,343,457,411,422,448,419,431,389,414,381,415,357,419,398,352,398,379,400,337,299,369,416,329,303,333,362,438,383,350,337,348,289,362,298,355,285,294,319,445,308,375,387,337,406,323,372,342,389,311,254,270,248,271,309,374,323,255,289,223,280,331,237,247,229,293,230,239,242,271,343,255,185,296,217,236,268,257,273,323,346,205,285,277,265,277,253,219"
},
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "556,581,482,552,550,589,575,565,471,575,565,605,663,544,471,641,567,535,630,535,623,579,582,559,611,509,521,563,513,544,523,558,450,486,478,584,440,432,473,480,555,510,451,476,490,470,555,418,463,402,403,476,591,431,560,581,532,579,427,501,452,551,479,405,397,376,458,439,490,508,389,488,360,413,509,351,434,392,430,426,375,414,401,459,435,327,491,406,397,406,384,423,439,538,338,467,421,406,477,428,371"
}
],
"title": "Bars, normal stacking (series order 1)",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "bars",
"fillOpacity": 100,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "normal"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 16,
"x": 8,
"y": 24
},
"id": 15,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"stacking": "none",
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "556,581,482,552,550,589,575,565,471,575,565,605,663,544,471,641,567,535,630,535,623,579,582,559,611,509,521,563,513,544,523,558,450,486,478,584,440,432,473,480,555,510,451,476,490,470,555,418,463,402,403,476,591,431,560,581,532,579,427,501,452,551,479,405,397,376,458,439,490,508,389,488,360,413,509,351,434,392,430,426,375,414,401,459,435,327,491,406,397,406,384,423,439,538,338,467,421,406,477,428,371"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "408,436,368,422,383,454,459,380,360,400,378,419,468,394,343,457,411,422,448,419,431,389,414,381,415,357,419,398,352,398,379,400,337,299,369,416,329,303,333,362,438,383,350,337,348,289,362,298,355,285,294,319,445,308,375,387,337,406,323,372,342,389,311,254,270,248,271,309,374,323,255,289,223,280,331,237,247,229,293,230,239,242,271,343,255,185,296,217,236,268,257,273,323,346,205,285,277,265,277,253,219"
},
{
"hide": false,
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "253,248,247,282,212,275,306,276,253,243,276,261,313,233,231,315,302,288,251,300,263,227,232,271,258,212,225,234,244,200,249,205,231,173,263,246,190,184,198,233,246,232,228,201,212,163,194,155,177,179,130,160,245,184,225,221,217,229,165,172,227,212,181,133,140,128,111,177,177,159,92,100,92,154,161,124,83,99,104,76,117,87,151,154,77,71,164,68,94,105,107,74,130,156,91,103,97,98,96,76,117"
},
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "109,117,122,104,105,117,119,121,117,121,122,129,119,113,113,121,108,108,100,103,113,110,null,null,null,null,87,83,91,85,81,69,76,61,63,74,76,68,55,61,48,39,54,44,37,30,22,33,29,21,22,43,47,33,47,28,29,31,32,35,37,25,-5,-14,-7,-14,-7,-18,-18,-18,-16,-41,-22,-30,-27,-30,-47,-49,-47,-42,-55,-34,-27,-22,-23,-34,-23,-32,-36,-47,-33,-32,-18,-23,-21,-33,-39,-21,-18,-27,-5"
}
],
"title": "Bars, normal stacking (series order 2)",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 8,
"x": 0,
"y": 32
},
"id": 13,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"stacking": "none",
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "5,6,7,8,7,6"
},
{
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "5,5,5,5,5,5"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "5,5,5,5,5,5"
},
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "5,5,5,5,5,5"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Stacking groups, data not stacked",
"type": "timeseries"
},
{
"datasource": null,
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"graph": false,
"legend": false,
"tooltip": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "short"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "A-series"
},
"properties": [
{
"id": "custom.stacking",
"value": {
"group": "A",
"mode": "normal"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "B-series"
},
"properties": [
{
"id": "custom.stacking",
"value": {
"group": "A",
"mode": "normal"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "C-series"
},
"properties": [
{
"id": "custom.stacking",
"value": {
"group": "B",
"mode": "normal"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "D-series"
},
"properties": [
{
"id": "custom.stacking",
"value": {
"group": "B",
"mode": "normal"
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 16,
"x": 8,
"y": 32
},
"id": 16,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom"
},
"stacking": "none",
"tooltipOptions": {
"mode": "single"
}
},
"pluginVersion": "7.5.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "csv_metric_values",
"stringInput": "5,6,7,8,7,6"
},
{
"refId": "B",
"scenarioId": "csv_metric_values",
"stringInput": "5,5,5,5,5,5"
},
{
"hide": false,
"refId": "C",
"scenarioId": "csv_metric_values",
"stringInput": "5,5,5,5,5,5"
},
{
"hide": false,
"refId": "D",
"scenarioId": "csv_metric_values",
"stringInput": "5,5,5,5,5,5"
}
],
"timeFrom": null,
"timeShift": null,
"title": "Stacking groups, data stacked, two stacking groups",
"type": "timeseries"
}
],
"schemaVersion": 27,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Panel Tests - TimeSeries - stacking",
"uid": "Ei86FP_Mx",
"version": 6
}

View File

@ -10,6 +10,7 @@ export interface StandardEditorContext<TOptions> {
eventBus?: EventBus;
getSuggestions?: (scope?: VariableSuggestionsScope) => VariableSuggestion[];
options?: TOptions;
isOverride?: boolean;
}
export interface StandardEditorProps<TValue = any, TSettings = any, TOptions = any> {

View File

@ -6,7 +6,8 @@ import { LegendDisplayMode } from '../VizLegend/models.gen';
import { prepDataForStorybook } from '../../utils/storybook/data';
import { useTheme } from '../../themes';
import { select } from '@storybook/addon-knobs';
import { BarChartOptions, BarStackingMode, BarValueVisibility } from './types';
import { BarChartOptions, BarValueVisibility } from './types';
import { StackingMode } from '../uPlot/config';
export default {
title: 'Visualizations/BarChart',
@ -55,7 +56,7 @@ export const Basic: React.FC = () => {
const options: BarChartOptions = {
orientation: orientation,
legend: { displayMode: LegendDisplayMode.List, placement: legendPlacement, calcs: [] },
stacking: BarStackingMode.None,
stacking: StackingMode.None,
showValue: BarValueVisibility.Always,
barWidth: 0.97,
groupWidth: 0.7,

View File

@ -56,6 +56,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -176,6 +177,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -296,6 +298,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -416,6 +419,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -536,6 +540,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -656,6 +661,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -776,6 +782,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -896,6 +903,7 @@ UPlotConfigBuilder {
[Function],
],
},
"isStacking": false,
"scales": Array [
UPlotScaleBuilder {
"props": Object {

View File

@ -1,16 +1,7 @@
import { VizOrientation } from '@grafana/data';
import { AxisConfig, GraphGradientMode, HideableFieldConfig } from '../uPlot/config';
import { AxisConfig, GraphGradientMode, HideableFieldConfig, StackingMode } from '../uPlot/config';
import { VizLegendOptions } from '../VizLegend/models.gen';
/**
* @alpha
*/
export enum BarStackingMode {
None = 'none',
Standard = 'standard',
Percent = 'percent',
}
/**
* @alpha
*/
@ -26,7 +17,7 @@ export enum BarValueVisibility {
export interface BarChartOptions {
orientation: VizOrientation;
legend: VizLegendOptions;
stacking: BarStackingMode;
stacking: StackingMode;
showValue: BarValueVisibility;
barWidth: number;
groupWidth: number;

View File

@ -1,7 +1,7 @@
import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
import { FieldConfig, FieldType, GrafanaTheme, MutableDataFrame, VizOrientation } from '@grafana/data';
import { BarChartFieldConfig, BarChartOptions, BarStackingMode, BarValueVisibility } from './types';
import { GraphGradientMode } from '../uPlot/config';
import { BarChartFieldConfig, BarChartOptions, BarValueVisibility } from './types';
import { GraphGradientMode, StackingMode } from '../uPlot/config';
import { LegendDisplayMode } from '../VizLegend/models.gen';
function mockDataFrame() {
@ -73,7 +73,7 @@ describe('GraphNG utils', () => {
placement: 'bottom',
calcs: [],
},
stacking: BarStackingMode.None,
stacking: StackingMode.None,
};
it.each([VizOrientation.Auto, VizOrientation.Horizontal, VizOrientation.Vertical])('orientation', (v) => {
@ -94,7 +94,7 @@ describe('GraphNG utils', () => {
).toMatchSnapshot();
});
it.each([BarStackingMode.None, BarStackingMode.Percent, BarStackingMode.Standard])('stacking', (v) => {
it.each([StackingMode.None, StackingMode.Percent, StackingMode.Normal])('stacking', (v) => {
expect(
preparePlotConfigBuilder(frame!, { colors: { panelBg: '#000000' } } as GrafanaTheme, {
...config,

View File

@ -64,6 +64,7 @@ export const Lines: Story<StoryProps> = ({ placement, unit, legendDisplayMode, .
placement: placement,
calcs: [],
}}
timeZone="browser"
/>
);
};

View File

@ -31,10 +31,24 @@ UPlotConfigBuilder {
},
},
},
"bands": Array [],
"bands": Array [
Object {
"series": Array [
2,
1,
],
},
Object {
"series": Array [
4,
3,
],
},
],
"hasBottomAxis": true,
"hasLeftAxis": true,
"hooks": Object {},
"isStacking": true,
"scales": Array [
UPlotScaleBuilder {
"props": Object {
@ -146,6 +160,135 @@ UPlotConfigBuilder {
"thresholds": undefined,
},
},
UPlotSeriesBuilder {
"props": Object {
"barAlignment": undefined,
"colorMode": Object {
"description": "Derive colors from thresholds",
"getCalculator": [Function],
"id": "thresholds",
"isByValue": true,
"name": "From thresholds",
},
"dataFrameFieldIndex": Object {
"fieldIndex": 2,
"frameIndex": 1,
},
"drawStyle": "line",
"fieldName": "Metric 3",
"fillOpacity": 0.1,
"gradientMode": "opacity",
"hideInLegend": undefined,
"lineColor": "#ff0000",
"lineInterpolation": "linear",
"lineStyle": Object {
"dash": Array [
1,
2,
],
"fill": "dash",
},
"lineWidth": 2,
"pointColor": "#808080",
"pointSize": undefined,
"scaleKey": "__fixed",
"show": true,
"showPoints": "always",
"spanNulls": false,
"theme": Object {
"colors": Object {
"panelBg": "#000000",
},
},
"thresholds": undefined,
},
},
UPlotSeriesBuilder {
"props": Object {
"barAlignment": -1,
"colorMode": Object {
"description": "Derive colors from thresholds",
"getCalculator": [Function],
"id": "thresholds",
"isByValue": true,
"name": "From thresholds",
},
"dataFrameFieldIndex": Object {
"fieldIndex": 3,
"frameIndex": 1,
},
"drawStyle": "bars",
"fieldName": "Metric 4",
"fillOpacity": 0.1,
"gradientMode": "hue",
"hideInLegend": undefined,
"lineColor": "#ff0000",
"lineInterpolation": "linear",
"lineStyle": Object {
"dash": Array [
1,
2,
],
"fill": "dash",
},
"lineWidth": 2,
"pointColor": "#808080",
"pointSize": undefined,
"scaleKey": "__fixed",
"show": true,
"showPoints": "always",
"spanNulls": false,
"theme": Object {
"colors": Object {
"panelBg": "#000000",
},
},
"thresholds": undefined,
},
},
UPlotSeriesBuilder {
"props": Object {
"barAlignment": -1,
"colorMode": Object {
"description": "Derive colors from thresholds",
"getCalculator": [Function],
"id": "thresholds",
"isByValue": true,
"name": "From thresholds",
},
"dataFrameFieldIndex": Object {
"fieldIndex": 4,
"frameIndex": 1,
},
"drawStyle": "bars",
"fieldName": "Metric 4",
"fillOpacity": 0.1,
"gradientMode": "hue",
"hideInLegend": undefined,
"lineColor": "#ff0000",
"lineInterpolation": "linear",
"lineStyle": Object {
"dash": Array [
1,
2,
],
"fill": "dash",
},
"lineWidth": 2,
"pointColor": "#808080",
"pointSize": undefined,
"scaleKey": "__fixed",
"show": true,
"showPoints": "always",
"spanNulls": false,
"theme": Object {
"colors": Object {
"panelBg": "#000000",
},
},
"thresholds": undefined,
},
},
],
"tz": "UTC",
"tzDate": [Function],

View File

@ -37,5 +37,6 @@ export const useGraphNGContext = () => {
dimFields,
mapSeriesIndexToDataFrameFieldIndex,
getXAxisField,
alignedData: data,
};
};

View File

@ -9,7 +9,15 @@ import {
GrafanaTheme,
MutableDataFrame,
} from '@grafana/data';
import { BarAlignment, DrawStyle, GraphFieldConfig, GraphGradientMode, LineInterpolation, PointVisibility } from '..';
import {
BarAlignment,
DrawStyle,
GraphFieldConfig,
GraphGradientMode,
LineInterpolation,
PointVisibility,
StackingMode,
} from '..';
function mockDataFrame() {
const df1 = new MutableDataFrame({
@ -38,6 +46,10 @@ function mockDataFrame() {
fillColor: '#ff0000',
fillOpacity: 0.1,
showPoints: PointVisibility.Always,
stacking: {
group: 'A',
mode: StackingMode.Normal,
},
},
};
@ -58,6 +70,80 @@ function mockDataFrame() {
fillColor: '#ff0000',
fillOpacity: 0.1,
showPoints: PointVisibility.Always,
stacking: {
group: 'A',
mode: StackingMode.Normal,
},
},
};
const f3Config: FieldConfig<GraphFieldConfig> = {
displayName: 'Metric 3',
decimals: 2,
custom: {
drawStyle: DrawStyle.Line,
gradientMode: GraphGradientMode.Opacity,
lineColor: '#ff0000',
lineWidth: 2,
lineInterpolation: LineInterpolation.Linear,
lineStyle: {
fill: 'dash',
dash: [1, 2],
},
spanNulls: false,
fillColor: '#ff0000',
fillOpacity: 0.1,
showPoints: PointVisibility.Always,
stacking: {
group: 'B',
mode: StackingMode.Normal,
},
},
};
const f4Config: FieldConfig<GraphFieldConfig> = {
displayName: 'Metric 4',
decimals: 2,
custom: {
drawStyle: DrawStyle.Bars,
gradientMode: GraphGradientMode.Hue,
lineColor: '#ff0000',
lineWidth: 2,
lineInterpolation: LineInterpolation.Linear,
lineStyle: {
fill: 'dash',
dash: [1, 2],
},
barAlignment: BarAlignment.Before,
fillColor: '#ff0000',
fillOpacity: 0.1,
showPoints: PointVisibility.Always,
stacking: {
group: 'B',
mode: StackingMode.Normal,
},
},
};
const f5Config: FieldConfig<GraphFieldConfig> = {
displayName: 'Metric 4',
decimals: 2,
custom: {
drawStyle: DrawStyle.Bars,
gradientMode: GraphGradientMode.Hue,
lineColor: '#ff0000',
lineWidth: 2,
lineInterpolation: LineInterpolation.Linear,
lineStyle: {
fill: 'dash',
dash: [1, 2],
},
barAlignment: BarAlignment.Before,
fillColor: '#ff0000',
fillOpacity: 0.1,
showPoints: PointVisibility.Always,
stacking: {
group: 'B',
mode: StackingMode.None,
},
},
};
@ -72,6 +158,21 @@ function mockDataFrame() {
type: FieldType.number,
config: f2Config,
});
df2.addField({
name: 'metric3',
type: FieldType.number,
config: f3Config,
});
df2.addField({
name: 'metric4',
type: FieldType.number,
config: f4Config,
});
df2.addField({
name: 'metric5',
type: FieldType.number,
config: f5Config,
});
return preparePlotFrame([df1, df2], {
x: fieldMatchers.get(FieldMatcherID.firstTimeField).get({}),

View File

@ -26,6 +26,7 @@ import {
ScaleDirection,
ScaleOrientation,
} from '../uPlot/config';
import { collectStackingGroups } from '../uPlot/utils';
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
@ -130,6 +131,8 @@ export function preparePlotConfigBuilder(
});
}
const stackingGroups: Map<string, number[]> = new Map();
let indexByName: Map<string, number> | undefined = undefined;
for (let i = 0; i < frame.fields.length; i++) {
@ -178,6 +181,7 @@ export function preparePlotConfigBuilder(
const showPoints = customConfig.drawStyle === DrawStyle.Points ? PointVisibility.Always : customConfig.showPoints;
let { fillOpacity } = customConfig;
if (customConfig.fillBelowTo) {
if (!indexByName) {
indexByName = getNamesToFieldIndex(frame);
@ -219,8 +223,20 @@ export function preparePlotConfigBuilder(
fieldName: getFieldDisplayName(field, frame),
hideInLegend: customConfig.hideFrom?.legend,
});
collectStackingGroups(field, stackingGroups, seriesIndex);
}
if (stackingGroups.size !== 0) {
builder.setStacking(true);
for (const [_, seriesIdxs] of stackingGroups.entries()) {
for (let j = seriesIdxs.length - 1; j > 0; j--) {
builder.addBand({
series: [seriesIdxs[j], seriesIdxs[j - 1]],
});
}
}
}
return builder;
}

View File

@ -176,7 +176,6 @@ export class Sparkline extends PureComponent<SparklineProps, State> {
render() {
const { data, configBuilder } = this.state;
const { width, height, sparkline } = this.props;
return (
<UPlotChart data={data} config={configBuilder} width={width} height={height} timeRange={sparkline.timeRange!} />
);

View File

@ -228,7 +228,7 @@ export { GraphNG, FIXED_UNIT } from './GraphNG/GraphNG';
export { useGraphNGContext } from './GraphNG/hooks';
export { BarChart } from './BarChart/BarChart';
export { TimelineChart } from './Timeline/TimelineChart';
export { BarChartOptions, BarStackingMode, BarValueVisibility, BarChartFieldConfig } from './BarChart/types';
export { BarChartOptions, BarValueVisibility, BarChartFieldConfig } from './BarChart/types';
export { TimelineOptions, TimelineFieldConfig } from './Timeline/types';
export { GraphNGLegendEvent, GraphNGLegendEventMode } from './GraphNG/types';
export * from './NodeGraph';

View File

@ -133,7 +133,6 @@ describe('UPlotChart', () => {
describe('config update', () => {
it('skips uPlot intialization for width and height equal 0', async () => {
const { data, timeRange, config } = mockData();
const { queryAllByTestId } = render(
<UPlotChart data={preparePlotData(data)} config={config} timeRange={timeRange} width={0} height={0} />
);

View File

@ -175,6 +175,23 @@ export interface HideableFieldConfig {
hideFrom?: HideSeriesConfig;
}
/**
* @alpha
*/
export enum StackingMode {
None = 'none',
Normal = 'normal',
Percent = 'percent',
}
/**
* @alpha
*/
export interface StackingConfig {
mode?: StackingMode;
group?: string;
}
/**
* @alpha
*/
@ -187,6 +204,7 @@ export interface GraphFieldConfig
HideableFieldConfig {
drawStyle?: DrawStyle;
gradientMode?: GraphGradientMode;
stacking?: StackingConfig;
}
/**
@ -231,4 +249,9 @@ export const graphFieldOptions = {
{ label: 'Hue', value: GraphGradientMode.Hue },
// { label: 'Color scheme', value: GraphGradientMode.Scheme },
] as Array<SelectableValue<GraphGradientMode>>,
stacking: [
{ label: 'Off', value: StackingMode.None },
{ label: 'Normal', value: StackingMode.Normal },
] as Array<SelectableValue<StackingMode>>,
};

View File

@ -350,7 +350,7 @@ describe('UPlotConfigBuilder', () => {
`);
});
it('Handles auto axis placement', () => {
it('handles auto axis placement', () => {
const builder = new UPlotConfigBuilder();
builder.addAxis({
@ -370,7 +370,7 @@ describe('UPlotConfigBuilder', () => {
expect(builder.getConfig().axes![1].grid!.show).toBe(false);
});
it('When fillColor is not set fill', () => {
it('when fillColor is not set fill', () => {
const builder = new UPlotConfigBuilder();
builder.addSeries({
drawStyle: DrawStyle.Line,
@ -383,7 +383,7 @@ describe('UPlotConfigBuilder', () => {
expect(builder.getConfig().series[1].fill).toBe(undefined);
});
it('When fillOpacity is set', () => {
it('when fillOpacity is set', () => {
const builder = new UPlotConfigBuilder();
builder.addSeries({
drawStyle: DrawStyle.Line,
@ -397,7 +397,7 @@ describe('UPlotConfigBuilder', () => {
expect(builder.getConfig().series[1].fill).toBe('rgba(255, 170, 187, 0.5)');
});
it('When fillColor is set ignore fillOpacity', () => {
it('when fillColor is set ignore fillOpacity', () => {
const builder = new UPlotConfigBuilder();
builder.addSeries({
drawStyle: DrawStyle.Line,
@ -412,7 +412,7 @@ describe('UPlotConfigBuilder', () => {
expect(builder.getConfig().series[1].fill).toBe('#FF0000');
});
it('When fillGradient mode is opacity', () => {
it('when fillGradient mode is opacity', () => {
const builder = new UPlotConfigBuilder();
builder.addSeries({
drawStyle: DrawStyle.Line,
@ -486,4 +486,147 @@ describe('UPlotConfigBuilder', () => {
}
`);
});
describe('Stacking', () => {
it('allows stacking config', () => {
const builder = new UPlotConfigBuilder();
builder.setStacking();
builder.addSeries({
drawStyle: DrawStyle.Line,
scaleKey: 'scale-x',
fieldName: 'A-series',
fillOpacity: 50,
gradientMode: GraphGradientMode.Opacity,
showPoints: PointVisibility.Auto,
lineColor: '#0000ff',
lineWidth: 1,
spanNulls: false,
theme: darkTheme,
});
builder.addSeries({
drawStyle: DrawStyle.Line,
scaleKey: 'scale-x',
fieldName: 'B-series',
fillOpacity: 50,
gradientMode: GraphGradientMode.Opacity,
showPoints: PointVisibility.Auto,
pointSize: 5,
lineColor: '#00ff00',
lineWidth: 1,
spanNulls: false,
theme: darkTheme,
});
builder.addSeries({
drawStyle: DrawStyle.Line,
scaleKey: 'scale-x',
fieldName: 'C-series',
fillOpacity: 50,
gradientMode: GraphGradientMode.Opacity,
showPoints: PointVisibility.Auto,
pointSize: 5,
lineColor: '#ff0000',
lineWidth: 1,
spanNulls: false,
theme: darkTheme,
});
builder.addBand({
series: [3, 2],
fill: 'red',
});
builder.addBand({
series: [2, 1],
fill: 'blue',
});
expect(builder.getConfig()).toMatchInlineSnapshot(`
Object {
"axes": Array [],
"bands": Array [
Object {
"fill": "red",
"series": Array [
3,
2,
],
},
Object {
"fill": "blue",
"series": Array [
2,
1,
],
},
],
"cursor": Object {
"drag": Object {
"setScale": false,
},
"focus": Object {
"prox": 30,
},
"points": Object {
"fill": [Function],
"size": [Function],
"stroke": [Function],
"width": [Function],
},
},
"hooks": Object {},
"scales": Object {},
"select": undefined,
"series": Array [
Object {},
Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"size": undefined,
"stroke": undefined,
},
"pxAlign": undefined,
"scale": "scale-x",
"show": true,
"spanGaps": false,
"stroke": "#0000ff",
"width": 1,
},
Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"size": 5,
"stroke": undefined,
},
"pxAlign": undefined,
"scale": "scale-x",
"show": true,
"spanGaps": false,
"stroke": "#00ff00",
"width": 1,
},
Object {
"fill": [Function],
"paths": [Function],
"points": Object {
"fill": undefined,
"size": 5,
"stroke": undefined,
},
"pxAlign": undefined,
"scale": "scale-x",
"show": true,
"spanGaps": false,
"stroke": "#ff0000",
"width": 1,
},
],
"tzDate": [Function],
}
`);
});
});
});

View File

@ -15,6 +15,7 @@ export class UPlotConfigBuilder {
private scales: UPlotScaleBuilder[] = [];
private bands: Band[] = [];
private cursor: Cursor | undefined;
private isStacking = false;
// uPlot types don't export the Select interface prior to 1.6.4
private select: Partial<BBox> | undefined;
private hasLeftAxis = false;
@ -78,6 +79,9 @@ export class UPlotConfigBuilder {
this.select = select;
}
setStacking(enabled = true) {
this.isStacking = enabled;
}
addSeries(props: SeriesProps) {
this.series.push(new UPlotSeriesBuilder(props));
}
@ -118,19 +122,25 @@ export class UPlotConfigBuilder {
config.tzDate = this.tzDate;
// When bands exist, only keep fill when defined
if (this.isStacking) {
// Let uPlot handle bands and fills
config.bands = this.bands;
} else {
// When fillBelowTo option enabled, handle series bands fill manually
if (this.bands?.length) {
config.bands = this.bands;
const keepFill = new Set<number>();
for (const b of config.bands) {
keepFill.add(b.series[0]);
}
for (let i = 1; i < config.series.length; i++) {
if (!keepFill.has(i)) {
config.series[i].fill = undefined;
}
}
}
}
const cursorDefaults: Cursor = {
// prevent client-side zoom from triggering at the end of a selection

View File

@ -58,10 +58,11 @@ export const TooltipPlugin: React.FC<TooltipPluginProps> = ({ mode = 'single', t
// when interacting with a point in single mode
if (mode === 'single' && originFieldIndex !== null) {
const field = otherProps.data[originFieldIndex.frameIndex].fields[originFieldIndex.fieldIndex];
const field = graphContext.alignedData.fields[focusedSeriesIdx!];
const plotSeries = plotContext.getSeries();
const fieldFmt = field.display || getDisplayProcessor({ field, timeZone });
const value = fieldFmt(plotContext.data[focusedSeriesIdx!][focusedPointIdx]);
const value = fieldFmt(field.values.get(focusedPointIdx));
tooltip = (
<SeriesTable
@ -95,7 +96,9 @@ export const TooltipPlugin: React.FC<TooltipPluginProps> = ({ mode = 'single', t
continue;
}
const value = field.display!(plotContext.data[i][focusedPointIdx]);
// using aligned data value field here as it's indexes are in line with Plot data
const valueField = graphContext.alignedData.fields[i];
const value = valueField.display!(valueField.values.get(focusedPointIdx));
series.push({
// TODO: align with uPlot typings

View File

@ -1,4 +1,6 @@
import { timeFormatToTemplate } from './utils';
import { preparePlotData, timeFormatToTemplate } from './utils';
import { FieldType, MutableDataFrame } from '@grafana/data';
import { StackingMode } from './config';
describe('timeFormatToTemplate', () => {
it.each`
@ -13,3 +15,285 @@ describe('timeFormatToTemplate', () => {
expect(timeFormatToTemplate(format)).toEqual(expected);
});
});
describe('preparePlotData', () => {
const df = new MutableDataFrame({
fields: [
{ name: 'time', type: FieldType.time, values: [9997, 9998, 9999] },
{ name: 'a', values: [-10, 20, 10] },
{ name: 'b', values: [10, 10, 10] },
{ name: 'c', values: [20, 20, 20] },
],
});
it('creates array from DataFrame', () => {
expect(preparePlotData(df)).toMatchInlineSnapshot(`
Array [
Array [
9997,
9998,
9999,
],
Array [
-10,
20,
10,
],
Array [
10,
10,
10,
],
Array [
20,
20,
20,
],
]
`);
});
describe('stacking', () => {
it('none', () => {
const df = new MutableDataFrame({
fields: [
{ name: 'time', type: FieldType.time, values: [9997, 9998, 9999] },
{
name: 'a',
values: [-10, 20, 10],
config: { custom: { stacking: { mode: StackingMode.None } } },
},
{
name: 'b',
values: [10, 10, 10],
config: { custom: { stacking: { mode: StackingMode.None } } },
},
{
name: 'c',
values: [20, 20, 20],
config: { custom: { stacking: { mode: StackingMode.None } } },
},
],
});
expect(preparePlotData(df)).toMatchInlineSnapshot(`
Array [
Array [
9997,
9998,
9999,
],
Array [
-10,
20,
10,
],
Array [
10,
10,
10,
],
Array [
20,
20,
20,
],
]
`);
});
it('standard', () => {
const df = new MutableDataFrame({
fields: [
{ name: 'time', type: FieldType.time, values: [9997, 9998, 9999] },
{
name: 'a',
values: [-10, 20, 10],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
{
name: 'b',
values: [10, 10, 10],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
{
name: 'c',
values: [20, 20, 20],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
],
});
expect(preparePlotData(df)).toMatchInlineSnapshot(`
Array [
Array [
9997,
9998,
9999,
],
Array [
-10,
20,
10,
],
Array [
0,
30,
20,
],
Array [
20,
50,
40,
],
]
`);
});
it('standard with multiple groups', () => {
const df = new MutableDataFrame({
fields: [
{ name: 'time', type: FieldType.time, values: [9997, 9998, 9999] },
{
name: 'a',
values: [-10, 20, 10],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
{
name: 'b',
values: [10, 10, 10],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
{
name: 'c',
values: [20, 20, 20],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
{
name: 'd',
values: [1, 2, 3],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' } } },
},
{
name: 'e',
values: [1, 2, 3],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' } } },
},
{
name: 'f',
values: [1, 2, 3],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' } } },
},
],
});
expect(preparePlotData(df)).toMatchInlineSnapshot(`
Array [
Array [
9997,
9998,
9999,
],
Array [
-10,
20,
10,
],
Array [
0,
30,
20,
],
Array [
20,
50,
40,
],
Array [
1,
2,
3,
],
Array [
2,
4,
6,
],
Array [
3,
6,
9,
],
]
`);
});
it('standard with multiple groups and hidden fields', () => {
const df = new MutableDataFrame({
fields: [
{ name: 'time', type: FieldType.time, values: [9997, 9998, 9999] },
{
name: 'a',
values: [-10, 20, 10],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' }, hideFrom: { graph: true } } },
},
{
// Will ignore a series as stacking base as it's hidden from graph
name: 'b',
values: [10, 10, 10],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } },
},
{
name: 'd',
values: [1, 2, 3],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' } } },
},
{
name: 'e',
values: [1, 2, 3],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' }, hideFrom: { graph: true } } },
},
{
// Will ignore e series as stacking base as it's hidden from graph
name: 'f',
values: [1, 2, 3],
config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' } } },
},
],
});
expect(preparePlotData(df)).toMatchInlineSnapshot(`
Array [
Array [
9997,
9998,
9999,
],
Array [
-10,
20,
10,
],
Array [
10,
10,
10,
],
Array [
1,
2,
3,
],
Array [
1,
2,
3,
],
Array [
2,
4,
6,
],
]
`);
});
});
});

View File

@ -1,6 +1,7 @@
import { DataFrame, dateTime, FieldType } from '@grafana/data';
import { DataFrame, dateTime, Field, FieldType } from '@grafana/data';
import { AlignedData, Options } from 'uplot';
import { PlotPlugin, PlotProps } from './types';
import { StackingMode } from './config';
import { createLogger } from '../../utils/logger';
import { attachDebugger } from '../../utils';
@ -33,8 +34,11 @@ export function buildPlotConfig(props: PlotProps, plugins: Record<string, PlotPl
}
/** @internal */
export function preparePlotData(frame: DataFrame, ignoreFieldTypes?: FieldType[]): AlignedData {
const result: any[] = [];
const stackingGroups: Map<string, number[]> = new Map();
let seriesIndex = 0;
for (let i = 0; i < frame.fields.length; i++) {
const f = frame.fields[i];
@ -46,20 +50,59 @@ export function preparePlotData(frame: DataFrame, ignoreFieldTypes?: FieldType[]
timestamps.push(dateTime(f.values.get(i)).valueOf());
}
result.push(timestamps);
seriesIndex++;
continue;
}
result.push(f.values.toArray());
seriesIndex++;
continue;
}
if (ignoreFieldTypes && ignoreFieldTypes.indexOf(f.type) > -1) {
continue;
}
collectStackingGroups(f, stackingGroups, seriesIndex);
result.push(f.values.toArray());
seriesIndex++;
}
// Stacking
if (stackingGroups.size !== 0) {
// array or stacking groups
for (const [_, seriesIdxs] of stackingGroups.entries()) {
const acc = Array(result[0].length).fill(0);
for (let j = 0; j < seriesIdxs.length; j++) {
const currentlyStacking = result[seriesIdxs[j]];
for (let k = 0; k < result[0].length; k++) {
const v = currentlyStacking[k];
acc[k] += v === null || v === undefined ? 0 : +v;
}
result[seriesIdxs[j]] = acc.slice();
}
}
return result as AlignedData;
}
return result as AlignedData;
}
export function collectStackingGroups(f: Field, groups: Map<string, number[]>, seriesIdx: number) {
const customConfig = f.config.custom;
if (!customConfig) {
return;
}
if (
customConfig.stacking?.mode !== StackingMode.None &&
customConfig.stacking?.group &&
!customConfig.hideFrom?.graph
) {
if (!groups.has(customConfig.stacking.group)) {
groups.set(customConfig.stacking.group, [seriesIdx]);
} else {
groups.set(customConfig.stacking.group, groups.get(customConfig.stacking.group)!.concat(seriesIdx));
}
}
}
// Dev helpers
/** @internal */

View File

@ -6,6 +6,7 @@ import {
isSystemOverride as isSystemOverrideGuard,
VariableSuggestionsScope,
DynamicConfigValue,
ConfigOverrideRule,
} from '@grafana/data';
import { Container, fieldMatchersUI, ValuePicker } from '@grafana/ui';
import { OptionPaneRenderProps } from './types';
@ -51,6 +52,7 @@ export function getFieldOverrideCategories(props: OptionPaneRenderProps): Option
const context = {
data,
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(data, scope),
isOverride: true,
};
/**
@ -88,7 +90,7 @@ export function getFieldOverrideCategories(props: OptionPaneRenderProps): Option
onOverrideChange(idx, override);
};
const onDynamicConfigValueAdd = (value: SelectableValue<string>) => {
const onDynamicConfigValueAdd = (o: ConfigOverrideRule, value: SelectableValue<string>) => {
const registryItem = registry.get(value.value!);
const propertyConfig: DynamicConfigValue = {
id: registryItem.id,
@ -96,12 +98,12 @@ export function getFieldOverrideCategories(props: OptionPaneRenderProps): Option
};
if (override.properties) {
override.properties.push(propertyConfig);
o.properties.push(propertyConfig);
} else {
override.properties = [propertyConfig];
o.properties = [propertyConfig];
}
onOverrideChange(idx, override);
onOverrideChange(idx, o);
};
/**
@ -185,7 +187,7 @@ export function getFieldOverrideCategories(props: OptionPaneRenderProps): Option
icon="plus"
menuPlacement="auto"
options={configPropertiesOptions}
onChange={onDynamicConfigValueAdd}
onChange={(v) => onDynamicConfigValueAdd(override, v)}
/>
);
},
@ -241,8 +243,8 @@ function getOverrideProperties(registry: FieldConfigOptionsRegistry) {
.filter((o) => !o.hideFromOverrides)
.map((item) => {
let label = item.name;
if (item.category && item.category.length > 1) {
label = [...item.category!.slice(1), item.name].join(' > ');
if (item.category) {
label = [...item.category, item.name].join(' > ');
}
return {
label,

View File

@ -62,7 +62,7 @@ export const TestStuffPage: FC = () => {
timeRange={data.timeRange}
timeZone="browser"
/>
<hr></hr>
<hr />
<Table data={data.series[0]} width={1200} height={300} />
</div>
)}

View File

@ -7,13 +7,7 @@ import {
VizOrientation,
} from '@grafana/data';
import { BarChartPanel } from './BarChartPanel';
import {
BarChartFieldConfig,
BarChartOptions,
BarStackingMode,
BarValueVisibility,
graphFieldOptions,
} from '@grafana/ui';
import { BarChartFieldConfig, BarChartOptions, StackingMode, BarValueVisibility, graphFieldOptions } from '@grafana/ui';
import { addAxisConfig, addHideFrom, addLegendOptions } from '../timeseries/config';
import { defaultBarChartFieldConfig } from '@grafana/ui/src/components/BarChart/types';
@ -80,19 +74,6 @@ export const plugin = new PanelPlugin<BarChartOptions, BarChartFieldConfig>(BarC
},
defaultValue: VizOrientation.Auto,
})
.addRadio({
path: 'stacking',
name: 'Stacking',
settings: {
options: [
{ value: BarStackingMode.None, label: 'None' },
{ value: BarStackingMode.Standard, label: 'Standard' },
{ value: BarStackingMode.Percent, label: 'Percent' },
],
},
defaultValue: BarStackingMode.None,
showIf: () => false, // <<< Hide from the UI for now
})
.addRadio({
path: 'showValue',
name: 'Show values',
@ -115,7 +96,7 @@ export const plugin = new PanelPlugin<BarChartOptions, BarChartFieldConfig>(BarC
step: 0.01,
},
showIf: (c, data) => {
if (c.stacking && c.stacking !== BarStackingMode.None) {
if (c.stacking && c.stacking !== StackingMode.None) {
return false;
}
return countNumberFields(data) !== 1;

View File

@ -0,0 +1,51 @@
import React from 'react';
import { FieldOverrideEditorProps } from '@grafana/data';
import {
HorizontalGroup,
IconButton,
Input,
RadioButtonGroup,
StackingConfig,
StackingMode,
Tooltip,
} from '@grafana/ui';
export const StackingEditor: React.FC<FieldOverrideEditorProps<StackingConfig, any>> = ({
value,
context,
onChange,
item,
}) => {
return (
<HorizontalGroup>
<RadioButtonGroup
value={value?.mode || StackingMode.None}
options={item.settings.options}
onChange={(v) => {
onChange({
...value,
mode: v,
});
}}
/>
{context.isOverride && value?.mode && value?.mode !== StackingMode.None && (
<Input
type="text"
placeholder="Group"
suffix={
<Tooltip content="Name of the stacking group" placement="top">
<IconButton name="question-circle" />
</Tooltip>
}
defaultValue={value?.group}
onChange={(v) => {
onChange({
...value,
group: v.currentTarget.value.trim(),
});
}}
/>
)}
</HorizontalGroup>
);
};

View File

@ -35,7 +35,6 @@ Object {
],
},
"options": Object {
"graph": Object {},
"legend": Object {
"calcs": Array [
"mean",
@ -68,7 +67,6 @@ Object {
"overrides": Array [],
},
"options": Object {
"graph": Object {},
"legend": Object {
"calcs": Array [],
"displayMode": "list",
@ -81,6 +79,150 @@ Object {
}
`;
exports[`Graph Migrations stacking groups 1`] = `
Object {
"fieldConfig": Object {
"defaults": Object {
"custom": Object {
"axisPlacement": "auto",
"drawStyle": "line",
"fillOpacity": 50,
"lineInterpolation": "stepAfter",
"lineWidth": 5,
"showPoints": "never",
"spanNulls": true,
"stacking": Object {
"group": "A",
"mode": "normal",
},
},
"nullValueMode": "null",
"unit": "short",
},
"overrides": Array [
Object {
"matcher": Object {
"id": "byName",
"options": "A-series",
},
"properties": Array [
Object {
"id": "color",
"value": Object {
"fixedColor": "red",
"mode": "fixed",
},
},
],
},
Object {
"matcher": Object {
"id": "byName",
"options": "A-series",
},
"properties": Array [
Object {
"id": "custom.stacking",
"value": Object {
"group": "A",
"mode": "normal",
},
},
],
},
Object {
"matcher": Object {
"id": "byName",
"options": "B-series",
},
"properties": Array [
Object {
"id": "custom.stacking",
"value": Object {
"group": "A",
"mode": "normal",
},
},
],
},
],
},
"options": Object {
"legend": Object {
"calcs": Array [
"mean",
"lastNotNull",
"max",
"min",
"sum",
],
"displayMode": "table",
"placement": "bottom",
},
"tooltipOptions": Object {
"mode": "single",
},
},
}
`;
exports[`Graph Migrations stacking simple 1`] = `
Object {
"fieldConfig": Object {
"defaults": Object {
"custom": Object {
"axisPlacement": "auto",
"drawStyle": "line",
"fillOpacity": 50,
"lineInterpolation": "stepAfter",
"lineWidth": 5,
"showPoints": "never",
"spanNulls": true,
"stacking": Object {
"group": "A",
"mode": "normal",
},
},
"nullValueMode": "null",
"unit": "short",
},
"overrides": Array [
Object {
"matcher": Object {
"id": "byName",
"options": "A-series",
},
"properties": Array [
Object {
"id": "color",
"value": Object {
"fixedColor": "red",
"mode": "fixed",
},
},
],
},
],
},
"options": Object {
"legend": Object {
"calcs": Array [
"mean",
"lastNotNull",
"max",
"min",
"sum",
],
"displayMode": "table",
"placement": "bottom",
},
"tooltipOptions": Object {
"mode": "single",
},
},
}
`;
exports[`Graph Migrations stairscase 1`] = `
Object {
"fieldConfig": Object {
@ -102,7 +244,6 @@ Object {
"overrides": Array [],
},
"options": Object {
"graph": Object {},
"legend": Object {
"calcs": Array [
"mean",
@ -156,7 +297,6 @@ Object {
],
},
"options": Object {
"graph": Object {},
"legend": Object {
"calcs": Array [],
"displayMode": "list",
@ -237,7 +377,6 @@ Object {
],
},
"options": Object {
"graph": Object {},
"legend": Object {
"calcs": Array [],
"displayMode": "list",

View File

@ -11,20 +11,22 @@ import {
stringOverrideProcessor,
} from '@grafana/data';
import {
AxisConfig,
AxisPlacement,
BarAlignment,
DrawStyle,
GraphFieldConfig,
graphFieldOptions,
GraphGradientMode,
HideableFieldConfig,
LegendDisplayMode,
LineInterpolation,
LineStyle,
PointVisibility,
ScaleDistribution,
ScaleDistributionConfig,
GraphGradientMode,
LegendDisplayMode,
AxisConfig,
HideableFieldConfig,
StackingConfig,
StackingMode,
} from '@grafana/ui';
import { SeriesConfigEditor } from './HideSeriesConfigEditor';
import { ScaleDistributionEditor } from './ScaleDistributionEditor';
@ -32,6 +34,7 @@ import { LineStyleEditor } from './LineStyleEditor';
import { FillBellowToEditor } from './FillBelowToEditor';
import { OptionsWithLegend } from './types';
import { SpanNullsEditor } from './SpanNullsEditor';
import { StackingEditor } from './StackingEditor';
export const defaultGraphConfig: GraphFieldConfig = {
drawStyle: DrawStyle.Line,
@ -40,11 +43,15 @@ export const defaultGraphConfig: GraphFieldConfig = {
fillOpacity: 0,
gradientMode: GraphGradientMode.None,
barAlignment: BarAlignment.Center,
stacking: {
mode: StackingMode.None,
group: 'A',
},
};
export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOptionsArgs<GraphFieldConfig> {
const categoryStyles = ['Graph styles'];
export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOptionsArgs<GraphFieldConfig> {
return {
standardOptions: {
[FieldConfigProperty.Color]: {
@ -180,6 +187,7 @@ export function getGraphFieldConfig(cfg: GraphFieldConfig): SetFieldConfigOption
showIf: (c) => c.showPoints !== PointVisibility.Never || c.drawStyle === DrawStyle.Points,
});
addStackingConfig(builder, cfg.stacking);
addAxisConfig(builder, cfg);
addHideFrom(builder);
},
@ -319,3 +327,23 @@ export function addLegendOptions<T extends OptionsWithLegend>(builder: PanelOpti
showIf: (currentConfig) => currentConfig.legend.displayMode !== LegendDisplayMode.Hidden,
});
}
export function addStackingConfig(
builder: FieldConfigEditorBuilder<{ stacking: StackingConfig }>,
defaultConfig?: StackingConfig
) {
builder.addCustomEditor({
id: 'stacking',
path: 'stacking',
name: 'Stack series',
category: categoryStyles,
defaultValue: defaultConfig,
editor: StackingEditor,
override: StackingEditor,
settings: {
options: graphFieldOptions.stacking,
},
process: identityOverrideProcessor,
shouldApply: (f) => f.type === FieldType.number,
});
}

View File

@ -48,6 +48,25 @@ describe('Graph Migrations', () => {
panel.options = graphPanelChangedHandler(panel, 'graph', old);
expect(panel).toMatchSnapshot();
});
describe('stacking', () => {
test('simple', () => {
const old: any = {
angular: stacking,
};
const panel = {} as PanelModel;
panel.options = graphPanelChangedHandler(panel, 'graph', old);
expect(panel).toMatchSnapshot();
});
test('groups', () => {
const old: any = {
angular: stackingGroups,
};
const panel = {} as PanelModel;
panel.options = graphPanelChangedHandler(panel, 'graph', old);
expect(panel).toMatchSnapshot();
});
});
});
const stairscase = {
@ -409,3 +428,200 @@ const legend = {
timeShift: null,
datasource: null,
};
const stacking = {
aliasColors: {
'A-series': 'red',
},
dashLength: 10,
fieldConfig: {
defaults: {
custom: {},
},
overrides: [],
},
fill: 5,
gridPos: {
h: 9,
w: 12,
x: 0,
y: 0,
},
id: 2,
legend: {
avg: true,
current: true,
max: false,
min: false,
show: true,
total: true,
values: true,
alignAsTable: true,
},
lines: true,
linewidth: 5,
maxDataPoints: 20,
nullPointMode: 'null',
options: {
alertThreshold: true,
},
pluginVersion: '7.4.0-pre',
pointradius: 2,
renderer: 'flot',
seriesOverrides: [],
spaceLength: 10,
steppedLine: true,
thresholds: [],
timeRegions: [],
title: 'Panel Title',
tooltip: {
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
buckets: null,
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
$$hashKey: 'object:38',
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
$$hashKey: 'object:39',
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
yaxis: {
align: false,
alignLevel: null,
},
bars: false,
dashes: false,
fillGradient: 0,
hiddenSeries: false,
percentage: false,
points: false,
stack: true,
timeFrom: null,
timeShift: null,
datasource: null,
};
const stackingGroups = {
aliasColors: {
'A-series': 'red',
},
dashLength: 10,
fieldConfig: {
defaults: {
custom: {},
},
overrides: [],
},
fill: 5,
gridPos: {
h: 9,
w: 12,
x: 0,
y: 0,
},
id: 2,
legend: {
avg: true,
current: true,
max: false,
min: false,
show: true,
total: true,
values: true,
alignAsTable: true,
},
lines: true,
linewidth: 5,
maxDataPoints: 20,
nullPointMode: 'null',
options: {
alertThreshold: true,
},
pluginVersion: '7.4.0-pre',
pointradius: 2,
renderer: 'flot',
seriesOverrides: [
{
alias: 'A-series',
stack: 'A',
},
{
alias: 'B-series',
stack: 'A',
},
],
spaceLength: 10,
steppedLine: true,
thresholds: [],
timeRegions: [],
title: 'Panel Title',
tooltip: {
shared: true,
sort: 0,
value_type: 'individual',
},
type: 'graph',
xaxis: {
buckets: null,
mode: 'time',
name: null,
show: true,
values: [],
},
yaxes: [
{
$$hashKey: 'object:38',
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
{
$$hashKey: 'object:39',
format: 'short',
label: null,
logBase: 1,
max: null,
min: null,
show: true,
},
],
yaxis: {
align: false,
alignLevel: null,
},
bars: false,
dashes: false,
fillGradient: 0,
hiddenSeries: false,
percentage: false,
points: false,
stack: true,
timeFrom: null,
timeShift: null,
datasource: null,
};

View File

@ -10,19 +10,22 @@ import {
NullValueMode,
PanelModel,
} from '@grafana/data';
import { GraphFieldConfig, LegendDisplayMode } from '@grafana/ui';
import {
GraphGradientMode,
AxisPlacement,
DrawStyle,
GraphFieldConfig,
GraphGradientMode,
LegendDisplayMode,
LineInterpolation,
LineStyle,
PointVisibility,
} from '@grafana/ui/src/components/uPlot/config';
StackingMode,
} from '@grafana/ui';
import { Options } from './types';
import omitBy from 'lodash/omitBy';
import isNil from 'lodash/isNil';
import { isNumber, isString } from 'lodash';
import { defaultGraphConfig } from './config';
/**
* This is called when the panel changes from another panel
@ -210,6 +213,12 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour
break;
}
break;
case 'stack':
rule.properties.push({
id: 'custom.stacking',
value: { mode: StackingMode.Normal, group: v },
});
break;
default:
console.log('Ignore override migration:', seriesOverride.alias, p, v);
}
@ -265,11 +274,17 @@ export function flotToGraphOptions(angular: any): { fieldConfig: FieldConfigSour
graph.fillOpacity = 100; // bars were always
}
if (angular.stack) {
graph.stacking = {
mode: StackingMode.Normal,
group: defaultGraphConfig.stacking!.group,
};
}
y1.custom = omitBy(graph, isNil);
y1.nullValueMode = angular.nullPointMode as NullValueMode;
const options: Options = {
graph: {},
legend: {
displayMode: LegendDisplayMode.List,
placement: 'bottom',

View File

@ -23,5 +23,6 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(TimeSeriesPanel
],
},
});
addLegendOptions(builder);
});

View File

@ -1,14 +1,9 @@
import { VizLegendOptions, GraphTooltipOptions } from '@grafana/ui';
export interface GraphOptions {
// nothing for now
}
export interface OptionsWithLegend {
legend: VizLegendOptions;
}
export interface Options extends OptionsWithLegend {
graph: GraphOptions;
tooltipOptions: GraphTooltipOptions;
}

View File

@ -1,5 +1,5 @@
import React, { useCallback, useMemo } from 'react';
import { Button, TooltipPlugin, GraphNG, GraphNGLegendEvent } from '@grafana/ui';
import { Button, GraphNG, GraphNGLegendEvent, TooltipPlugin } from '@grafana/ui';
import { PanelProps } from '@grafana/data';
import { Options } from './types';
import { hideSeriesConfigFactory } from '../timeseries/overrides/hideSeriesConfigFactory';