mirror of
https://github.com/grafana/grafana.git
synced 2025-08-02 13:07:47 +08:00
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:
896
devenv/dev-dashboards/panel-graph/graph-ng-stacking.json
Normal file
896
devenv/dev-dashboards/panel-graph/graph-ng-stacking.json
Normal 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
|
||||
}
|
@ -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> {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -64,6 +64,7 @@ export const Lines: Story<StoryProps> = ({ placement, unit, legendDisplayMode, .
|
||||
placement: placement,
|
||||
calcs: [],
|
||||
}}
|
||||
timeZone="browser"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -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],
|
||||
|
@ -37,5 +37,6 @@ export const useGraphNGContext = () => {
|
||||
dimFields,
|
||||
mapSeriesIndexToDataFrameFieldIndex,
|
||||
getXAxisField,
|
||||
alignedData: data,
|
||||
};
|
||||
};
|
||||
|
@ -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({}),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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!} />
|
||||
);
|
||||
|
@ -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';
|
||||
|
@ -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} />
|
||||
);
|
||||
|
@ -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>>,
|
||||
};
|
||||
|
@ -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],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
],
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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 */
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
)}
|
||||
|
@ -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;
|
||||
|
51
public/app/plugins/panel/timeseries/StackingEditor.tsx
Normal file
51
public/app/plugins/panel/timeseries/StackingEditor.tsx
Normal 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>
|
||||
);
|
||||
};
|
@ -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",
|
||||
|
@ -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,
|
||||
});
|
||||
}
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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',
|
||||
|
@ -23,5 +23,6 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(TimeSeriesPanel
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
addLegendOptions(builder);
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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';
|
||||
|
Reference in New Issue
Block a user