diff --git a/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json new file mode 100644 index 00000000000..993409346be --- /dev/null +++ b/apps/dashboard/pkg/migration/testdata/dev-dashboards-output/panel-gauge/gauge_tests_new.v42.json @@ -0,0 +1,1909 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations \u0026 Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 525, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Basic single ", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 1, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": true, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Plain", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 4, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": false, + "centerGlow": true, + "rounded": true, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Center glow", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 3, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Center and bar glow", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 12, + "y": 1 + }, + "id": 5, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Spotlight", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 16, + "y": 1 + }, + "id": 8, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Sparkline", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 7 + }, + "id": 22, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": false, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Square butts", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 4, + "y": 7 + }, + "id": 23, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": false, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "gauge", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Square butts gauge", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 17, + "panels": [], + "title": "Bar width", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 14 + }, + "id": 18, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.1, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,1", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 14 + }, + "id": 19, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.32, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,32", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 10, + "y": 14 + }, + "id": 20, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.57, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,57", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 15, + "y": 14 + }, + "id": 21, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.8, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,8", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 24, + "panels": [], + "title": "Thresholds", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 21 + }, + "id": 25, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.9, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "gauge", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Plain thresholds", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 21 + }, + "id": 26, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 37, + "segmentSpacing": 0.19, + "shape": "gauge", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Thresholds segmented", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 12, + "y": 21 + }, + "id": 29, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 46, + "segmentSpacing": 0.19, + "shape": "gauge", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Segmented + gradient", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 27 + }, + "id": 27, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.43, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.19, + "shape": "circle", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Thresholds circle", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 27 + }, + "id": 28, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 62, + "segmentSpacing": 0.19, + "shape": "circle", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Thresholds circle segmented", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 6, + "panels": [], + "title": "Gradient modes", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "palette-classic" + }, + "mappings": [], + "max": 100, + "min": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 9, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 98, + "min": 5, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 5, + "spread": 12, + "startValue": 50 + } + ], + "title": "Auto gradient + classic palette", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "max": 100, + "min": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 11, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "scheme", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "gauge", + "showThresholdMarkers": false, + "sparkline": true, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 98, + "min": 5, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 5, + "spread": 12, + "startValue": 50 + } + ], + "title": "Continuous color scheme ", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 47 + }, + "id": 12, + "panels": [], + "title": "Segmented", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "continuous-GrYlRd" + }, + "decimals": 0, + "mappings": [], + "max": 8, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "suffix:/ 8 " + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 0, + "y": 48 + }, + "id": 13, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.49, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "scheme", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 8, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 8, + "min": 1, + "noise": 2, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 6, + "startValue": 0 + } + ], + "title": "Active gateways", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "continuous-GrYlRd" + }, + "decimals": 0, + "mappings": [], + "max": 12, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "suffix:/ 12" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 4, + "y": 48 + }, + "id": 14, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.49, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "sparkline": false, + "spotlight": true + }, + "glow": "both", + "gradient": "scheme", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 12, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 12, + "min": 1, + "noise": 2, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 6, + "startValue": 0 + } + ], + "title": "Active pods", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 9, + "y": 48 + }, + "id": 15, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.84, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "hue", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 39, + "segmentSpacing": 0.76, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 100, + "min": 10, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 12, + "startValue": 10 + } + ], + "title": "Backend", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 14, + "y": 48 + }, + "id": 16, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.66, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "hue", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "segmentCount": 81, + "segmentSpacing": 0.28, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 100, + "min": 10, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 12, + "startValue": 10 + } + ], + "title": "Backend", + "type": "radialbar" + } + ], + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Panel tests - Gauge (new)", + "uid": "panel-tests-gauge-new", + "weekStart": "" +} \ No newline at end of file diff --git a/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json new file mode 100644 index 00000000000..adc3ef3ecac --- /dev/null +++ b/devenv/dev-dashboards/panel-gauge/gauge_tests_new.json @@ -0,0 +1,1866 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 525, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "Basic single ", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 1, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": true, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Plain", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 4, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": false, + "centerGlow": true, + "rounded": true, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Center glow", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 3, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Center and bar glow", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 12, + "y": 1 + }, + "id": 5, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Spotlight", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 16, + "y": 1 + }, + "id": 8, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Sparkline", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 0, + "y": 7 + }, + "id": 22, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": false, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Square butts", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 4, + "x": 4, + "y": 7 + }, + "id": 23, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": false, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "gauge", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Square butts gauge", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 13 + }, + "id": 17, + "panels": [], + "title": "Bar width", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 0, + "y": 14 + }, + "id": 18, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.1, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,1", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 5, + "y": 14 + }, + "id": 19, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.32, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,32", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 10, + "y": 14 + }, + "id": 20, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.57, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,57", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 15, + "y": 14 + }, + "id": 21, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.8, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "max": 100, + "min": 1, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "spread": 22, + "startValue": 1 + } + ], + "title": "Bar width 0,8", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 24, + "panels": [], + "title": "Thresholds", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 0, + "y": 21 + }, + "id": 25, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.9, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "gauge", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Plain thresholds", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 6, + "x": 6, + "y": 21 + }, + "id": 26, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 37, + "segmentSpacing": 0.19, + "shape": "gauge", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Thresholds segmented", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 5, + "x": 12, + "y": 21 + }, + "id": 29, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 46, + "segmentSpacing": 0.19, + "shape": "gauge", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Segmented + gradient", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 27 + }, + "id": 27, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.43, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.19, + "shape": "circle", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Thresholds circle", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "orange", + "value": 65 + }, + { + "color": "red", + "value": 85 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 27 + }, + "id": 28, + "maxDataPoints": 20, + "options": { + "barWidthFactor": 0.72, + "effects": { + "barGlow": false, + "centerGlow": false, + "rounded": false, + "spotlight": false + }, + "gradient": "none", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 62, + "segmentSpacing": 0.19, + "shape": "circle", + "showThresholdMarkers": true, + "sparkline": false + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "alias": "1", + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_metric_values", + "stringInput": "1,20,90,30,5,70" + } + ], + "title": "Thresholds circle segmented", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 6, + "panels": [], + "title": "Gradient modes", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "palette-classic" + }, + "mappings": [], + "max": 100, + "min": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 35 + }, + "id": 9, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 98, + "min": 5, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 5, + "spread": 12, + "startValue": 50 + } + ], + "title": "Auto gradient + classic palette", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "continuous-GrYlRd" + }, + "mappings": [], + "max": 100, + "min": 1, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 11, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.4, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "scheme", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 1, + "segmentSpacing": 0.3, + "shape": "gauge", + "showThresholdMarkers": false, + "sparkline": true, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 98, + "min": 5, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 5, + "spread": 12, + "startValue": 50 + } + ], + "title": "Continuous color scheme ", + "type": "radialbar" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 47 + }, + "id": 12, + "panels": [], + "title": "Segmented", + "type": "row" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "continuous-GrYlRd" + }, + "decimals": 0, + "mappings": [], + "max": 8, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "suffix:/ 8 " + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 4, + "x": 0, + "y": 48 + }, + "id": 13, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.49, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "scheme", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 8, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 8, + "min": 1, + "noise": 2, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 6, + "startValue": 0 + } + ], + "title": "Active gateways", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "blue", + "mode": "continuous-GrYlRd" + }, + "decimals": 0, + "mappings": [], + "max": 12, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "suffix:/ 12" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 4, + "y": 48 + }, + "id": 14, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.49, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "sparkline": false, + "spotlight": true + }, + "glow": "both", + "gradient": "scheme", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 12, + "segmentSpacing": 0.3, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 12, + "min": 1, + "noise": 2, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 6, + "startValue": 0 + } + ], + "title": "Active pods", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 5, + "x": 9, + "y": 48 + }, + "id": 15, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.84, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "hue", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 39, + "segmentSpacing": 0.76, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 100, + "min": 10, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 12, + "startValue": 10 + } + ], + "title": "Backend", + "type": "radialbar" + }, + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "fixed" + }, + "mappings": [], + "max": 100, + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 14, + "y": 48 + }, + "id": 16, + "maxDataPoints": 20, + "options": { + "barWidth": 12, + "barWidthFactor": 0.66, + "effects": { + "barGlow": true, + "centerGlow": true, + "rounded": true, + "spotlight": true + }, + "glow": "both", + "gradient": "hue", + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "segmentCount": 81, + "segmentSpacing": 0.28, + "shape": "circle", + "showThresholdMarkers": false, + "sparkline": false, + "spotlight": true + }, + "pluginVersion": "12.2.0-pre", + "targets": [ + { + "datasource": { + "type": "grafana-testdata-datasource", + "uid": "PD8C576611E62080A" + }, + "hide": false, + "max": 100, + "min": 10, + "noise": 22, + "refId": "A", + "scenarioId": "random_walk", + "seriesCount": 1, + "spread": 12, + "startValue": 10 + } + ], + "title": "Backend", + "type": "radialbar" + } + ], + "preload": false, + "schemaVersion": 42, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "Panel tests - Gauge (new)", + "uid": "panel-tests-gauge-new", + "version": 1 +} diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet index 2338d8c7f8a..f74755b981c 100644 --- a/devenv/jsonnet/dev-dashboards.libsonnet +++ b/devenv/jsonnet/dev-dashboards.libsonnet @@ -37,6 +37,7 @@ "filter": (import '../dev-dashboards/transforms/filter.json'), "gauge-multi-series": (import '../dev-dashboards/panel-gauge/gauge-multi-series.json'), "gauge_tests": (import '../dev-dashboards/panel-gauge/gauge_tests.json'), + "gauge_tests_new": (import '../dev-dashboards/panel-gauge/gauge_tests_new.json'), "geomap-color-field": (import '../dev-dashboards/panel-geomap/geomap-color-field.json'), "geomap-photo-layer": (import '../dev-dashboards/panel-geomap/geomap-photo-layer.json'), "geomap-route-layer": (import '../dev-dashboards/panel-geomap/geomap-route-layer.json'), diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index c3a64d496ae..2aac477cd2b 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -257,6 +257,12 @@ function calculateRange( globalRange: NumericRange | undefined, data: DataFrame[] ): { range?: { min?: number | null; max?: number | null; delta: number }; newGlobalRange: NumericRange | undefined } { + // If range is defined with min/max, use it + if (isNumber(config.min) && isNumber(config.max)) { + const range = { min: config.min, max: config.max, delta: config.max - config.min }; + return { range, newGlobalRange: globalRange ?? range }; + } + // Only calculate ranges when the field is a number and one of min/max is set to auto. if (field.type !== FieldType.number || (isNumber(config.min) && isNumber(config.max))) { return { newGlobalRange: globalRange }; diff --git a/packages/grafana-data/src/types/icon.ts b/packages/grafana-data/src/types/icon.ts index 791d14c9a9a..57e086c1ba2 100644 --- a/packages/grafana-data/src/types/icon.ts +++ b/packages/grafana-data/src/types/icon.ts @@ -285,6 +285,7 @@ export const availableIconsIndex = { 'ai-sparkle': true, bitbucket: true, git: true, + 'tachometer-fast': true, }; export type IconName = keyof typeof availableIconsIndex; diff --git a/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts new file mode 100644 index 00000000000..84c7b7f5e29 --- /dev/null +++ b/packages/grafana-schema/src/raw/composable/newgauge/panelcfg/x/NewGaugePanelCfg_types.gen.ts @@ -0,0 +1,49 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. +// +// Generated by: +// public/app/plugins/gen.go +// Using jennies: +// TSTypesJenny +// PluginTsTypesJenny +// +// Run 'make gen-cue' from repository root to regenerate. + +import * as common from '@grafana/schema'; + +export const pluginVersion = "12.3.0-pre"; + +export interface GaugePanelEffects { + barGlow?: boolean; + centerGlow?: boolean; + rounded?: boolean; + spotlight?: boolean; +} + +export const defaultGaugePanelEffects: Partial = { + barGlow: false, + centerGlow: true, + rounded: false, + spotlight: false, +}; + +export interface Options extends common.SingleStatBaseOptions { + barWidthFactor: number; + effects: GaugePanelEffects; + gradient: ('none' | 'auto'); + segmentCount: number; + segmentSpacing: number; + shape: ('circle' | 'gauge'); + showThresholdMarkers: boolean; + sparkline?: boolean; +} + +export const defaultOptions: Partial = { + barWidthFactor: 0.4, + effects: {}, + gradient: 'none', + segmentCount: 1, + segmentSpacing: 0.3, + shape: 'gauge', + showThresholdMarkers: true, + sparkline: false, +}; diff --git a/packages/grafana-ui/src/components/Gauge/Gauge.tsx b/packages/grafana-ui/src/components/Gauge/Gauge.tsx index d73339b12b6..8c050c109c0 100644 --- a/packages/grafana-ui/src/components/Gauge/Gauge.tsx +++ b/packages/grafana-ui/src/components/Gauge/Gauge.tsx @@ -10,8 +10,10 @@ import { GAUGE_DEFAULT_MAXIMUM, GAUGE_DEFAULT_MINIMUM, GrafanaTheme2, + FieldColorModeId, + FALLBACK_COLOR, } from '@grafana/data'; -import { VizTextDisplayOptions, VizOrientation } from '@grafana/schema'; +import { VizTextDisplayOptions, VizOrientation, Threshold } from '@grafana/schema'; import { calculateFontSize } from '../../utils/measureText'; import { clearButtonStyles } from '../Button/Button'; @@ -96,6 +98,14 @@ export class Gauge extends PureComponent { max = +max.toFixed(decimals); } + let thresholds: Threshold[] = []; + + if (field.color?.mode === FieldColorModeId.Thresholds) { + thresholds = getFormattedThresholds(decimals, field, theme); + } else { + thresholds = [{ value: field.min ?? GAUGE_DEFAULT_MINIMUM, color: value.color ?? FALLBACK_COLOR }]; + } + const options = { series: { gauges: { @@ -113,13 +123,13 @@ export class Gauge extends PureComponent { layout: { margin: 0, thresholdWidth: 0, vMargin: 0 }, cell: { border: { width: 0 } }, threshold: { - values: getFormattedThresholds(decimals, field, value, theme), + values: thresholds, label: { show: showThresholdLabels, margin: thresholdMarkersWidth + 1, font: { size: thresholdLabelFontSize }, }, - show: showThresholdMarkers, + show: showThresholdMarkers && thresholds.length > 1, width: thresholdMarkersWidth, }, value: { diff --git a/packages/grafana-ui/src/components/Gauge/utils.test.ts b/packages/grafana-ui/src/components/Gauge/utils.test.ts index 4c53929af0f..1144da4a2e2 100644 --- a/packages/grafana-ui/src/components/Gauge/utils.test.ts +++ b/packages/grafana-ui/src/components/Gauge/utils.test.ts @@ -6,10 +6,6 @@ import { getTheme } from '../../themes/getTheme'; import { calculateGaugeAutoProps, getFormattedThresholds } from './utils'; describe('getFormattedThresholds', () => { - const value = { - text: '25', - numeric: 25, - }; const theme = getTheme(); let field: FieldConfig; @@ -30,7 +26,7 @@ describe('getFormattedThresholds', () => { it('should return first thresholds color for min and max', () => { field.thresholds = { mode: ThresholdsMode.Absolute, steps: [{ value: -Infinity, color: '#7EB26D' }] }; - expect(getFormattedThresholds(2, field, value, theme)).toEqual([ + expect(getFormattedThresholds(2, field, theme)).toEqual([ { value: 0, color: '#7EB26D' }, { value: 100, color: '#7EB26D' }, ]); @@ -46,7 +42,7 @@ describe('getFormattedThresholds', () => { ], }; - expect(getFormattedThresholds(2, field, value, theme)).toEqual([ + expect(getFormattedThresholds(2, field, theme)).toEqual([ { value: 0, color: '#7EB26D' }, { value: 50, color: '#7EB26D' }, { value: 75, color: '#EAB839' }, diff --git a/packages/grafana-ui/src/components/Gauge/utils.ts b/packages/grafana-ui/src/components/Gauge/utils.ts index 4ca51fcd10b..8609785d135 100644 --- a/packages/grafana-ui/src/components/Gauge/utils.ts +++ b/packages/grafana-ui/src/components/Gauge/utils.ts @@ -1,7 +1,4 @@ import { - DisplayValue, - FALLBACK_COLOR, - FieldColorModeId, FieldConfig, GAUGE_DEFAULT_MAXIMUM, GAUGE_DEFAULT_MINIMUM, @@ -51,13 +48,8 @@ export function calculateGaugeAutoProps( export function getFormattedThresholds( decimals: number, field: FieldConfig, - value: DisplayValue, theme: GrafanaTheme | GrafanaTheme2 ): Threshold[] { - if (field.color?.mode !== FieldColorModeId.Thresholds) { - return [{ value: field.min ?? GAUGE_DEFAULT_MINIMUM, color: value.color ?? FALLBACK_COLOR }]; - } - const thresholds = field.thresholds ?? DEFAULT_THRESHOLDS; const isPercent = thresholds.mode === ThresholdsMode.Percentage; const steps = thresholds.steps; diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx new file mode 100644 index 00000000000..f54f3cd6f22 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialArcPath.tsx @@ -0,0 +1,52 @@ +import { GaugeDimensions, toRad } from './utils'; + +export interface RadialArcPathProps { + startAngle: number; + dimensions: GaugeDimensions; + color: string; + glowFilter?: string; + arcLengthDeg: number; + roundedBars?: boolean; +} + +export function RadialArcPath({ + startAngle: angle, + dimensions, + color, + glowFilter, + arcLengthDeg, + roundedBars, +}: RadialArcPathProps) { + const { radius, centerX, centerY, barWidth } = dimensions; + + if (arcLengthDeg === 360) { + // For some reason a 100% full arc cannot be rendered + arcLengthDeg = 359.99; + } + + const startRadians = toRad(angle); + const endRadians = toRad(angle + arcLengthDeg); + + let x1 = centerX + radius * Math.cos(startRadians); + let y1 = centerY + radius * Math.sin(startRadians); + let x2 = centerX + radius * Math.cos(endRadians); + let y2 = centerY + radius * Math.sin(endRadians); + + const largeArc = arcLengthDeg > 180 ? 1 : 0; + + const path = ['M', x1, y1, 'A', radius, radius, 0, largeArc, 1, x2, y2].join(' '); + + return ( + + ); +} diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx new file mode 100644 index 00000000000..2bacc3af3e8 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialBar.tsx @@ -0,0 +1,97 @@ +import { GrafanaTheme2 } from '@grafana/data'; + +import { useTheme2 } from '../../themes/ThemeContext'; + +import { RadialArcPath } from './RadialArcPath'; +import { RadialColorDefs } from './RadialColorDefs'; +import { GaugeDimensions, toRad } from './utils'; + +export interface RadialBarProps { + dimensions: GaugeDimensions; + colorDefs: RadialColorDefs; + angleRange: number; + angle: number; + startAngle: number; + roundedBars?: boolean; + spotlightStroke: string; + glowFilter?: string; +} +export function RadialBar({ + dimensions, + colorDefs, + angleRange, + angle, + startAngle, + roundedBars, + spotlightStroke, + glowFilter, +}: RadialBarProps) { + const theme = useTheme2(); + + return ( + <> + + {/** Track */} + + {/** The colored bar */} + + {spotlightStroke && angle > 8 && ( + + )} + + {colorDefs.getDefs()} + + ); +} + +interface SpotlightEffectProps { + dimensions: GaugeDimensions; + angle: number; + glowFilter?: string; + spotlightStroke: string; + theme: GrafanaTheme2; + roundedBars?: boolean; +} + +function SpotlightSquareEffect({ dimensions, angle, glowFilter, spotlightStroke, roundedBars }: SpotlightEffectProps) { + const { radius, centerX, centerY, barWidth } = dimensions; + + const angleRadian = toRad(angle); + const x1 = centerX + radius * Math.cos(angleRadian - 0.2); + const y1 = centerY + radius * Math.sin(angleRadian - 0.2); + const x2 = centerX + radius * Math.cos(angleRadian); + const y2 = centerY + radius * Math.sin(angleRadian); + + const path = ['M', x1, y1, 'A', radius, radius, 0, 0, 1, x2, y2].join(' '); + + return ( + + ); +} diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx new file mode 100644 index 00000000000..b9090c6fd45 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialBarSegmented.tsx @@ -0,0 +1,126 @@ +import { FieldDisplay } from '@grafana/data'; + +import { useTheme2 } from '../../themes/ThemeContext'; + +import { RadialArcPath } from './RadialArcPath'; +import { RadialColorDefs } from './RadialColorDefs'; +import { GaugeDimensions } from './utils'; + +export interface RadialBarSegmentedProps { + fieldDisplay: FieldDisplay; + dimensions: GaugeDimensions; + colorDefs: RadialColorDefs; + angleRange: number; + startAngle: number; + glowFilter?: string; + segmentCount: number; + segmentSpacing: number; +} +export function RadialBarSegmented({ + fieldDisplay, + dimensions, + startAngle, + angleRange, + glowFilter, + segmentCount, + segmentSpacing, + colorDefs, +}: RadialBarSegmentedProps) { + const segments: React.ReactNode[] = []; + const theme = useTheme2(); + + const segmentCountAdjusted = getOptimalSegmentCount(dimensions, segmentSpacing, segmentCount, angleRange); + const min = fieldDisplay.field.min ?? 0; + const max = fieldDisplay.field.max ?? 100; + const value = fieldDisplay.display.numeric; + const angleBetweenSegments = getAngleBetweenSegments(segmentSpacing, segmentCount, angleRange); + const segmentArcLengthDeg = angleRange / segmentCountAdjusted - angleBetweenSegments; + + for (let i = 0; i < segmentCountAdjusted; i++) { + const angleValue = ((max - min) / segmentCountAdjusted) * i; + const angleColor = colorDefs.getSegmentColor(angleValue); + const segmentAngle = startAngle + (angleRange / segmentCountAdjusted) * i + 0.01; + const segmentColor = angleValue > value ? theme.colors.action.hover : angleColor; + + segments.push( + + ); + } + + return ( + <> + {segments} + {colorDefs.getDefs()} + + ); +} + +export function getAngleBetweenSegments(segmentSpacing: number, segmentCount: number, range: number) { + // Max spacing is 8 degrees between segments + // Changing this constant could be considered a breaking change + const maxAngleBetweenSegments = Math.max(range / 4 / segmentCount, 2); + return segmentSpacing * maxAngleBetweenSegments; +} + +function getOptimalSegmentCount( + dimensions: GaugeDimensions, + segmentSpacing: number, + segmentCount: number, + range: number +) { + const angleBetweenSegments = getAngleBetweenSegments(segmentSpacing, segmentCount, range); + + const innerRadius = dimensions.radius - dimensions.barWidth / 2; + const circumference = Math.PI * innerRadius * 2 * (range / 360); + const maxSegments = Math.floor(circumference / (angleBetweenSegments + 3)); + + return Math.min(maxSegments, segmentCount); +} + +// export function RadialSegmentLine({ +// gaugeId, +// center, +// angle, +// size, +// color, +// barWidth, +// roundedBars, +// glow, +// margin, +// segmentWidth, +// }: RadialSegmentProps) { +// const arcSize = size - barWidth; +// const radius = arcSize / 2 - margin; + +// const angleRad = (Math.PI * (angle - 90)) / 180; +// const lineLength = radius - barWidth; + +// const x1 = center + radius * Math.cos(angleRad); +// const y1 = center + radius * Math.sin(angleRad); +// const x2 = center + lineLength * Math.cos(angleRad); +// const y2 = center + lineLength * Math.sin(angleRad); + +// return ( +// +// ); +// } diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx new file mode 100644 index 00000000000..861e216c742 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialColorDefs.tsx @@ -0,0 +1,137 @@ +import tinycolor from 'tinycolor2'; + +import { DisplayProcessor, FALLBACK_COLOR, FieldDisplay, getFieldColorMode, GrafanaTheme2 } from '@grafana/data'; + +import { RadialGradientMode, RadialShape } from './RadialGauge'; +import { GaugeDimensions } from './utils'; + +export interface RadialColorDefsOptions { + gradient: RadialGradientMode; + fieldDisplay: FieldDisplay; + theme: GrafanaTheme2; + dimensions: GaugeDimensions; + shape: RadialShape; + gaugeId: string; + displayProcessor: DisplayProcessor; +} + +export class RadialColorDefs { + private colorToIds: Record = {}; + private defs: React.ReactNode[] = []; + + constructor(private options: RadialColorDefsOptions) {} + + getSegmentColor(forValue: number): string { + const { displayProcessor } = this.options; + const baseColor = displayProcessor(forValue).color ?? FALLBACK_COLOR; + + return this.getColor(baseColor, true); + } + + getColor(baseColor: string, forSegment?: boolean): string { + const { gradient, dimensions, gaugeId, fieldDisplay, shape, theme } = this.options; + + const id = `value-color-${baseColor}-${gaugeId}`; + + if (this.colorToIds[id]) { + return this.colorToIds[id]; + } + + // If no gradient, just return the base color + if (gradient === 'none') { + this.colorToIds[id] = baseColor; + return baseColor; + } + + const returnColor = (this.colorToIds[id] = `url(#${id})`); + const colorModeId = fieldDisplay.field.color?.mode; + const colorMode = getFieldColorMode(colorModeId); + const valuePercent = fieldDisplay.display.percent ?? 0; + + // Handle continusous color modes first + // If it's a segment color we don't want to do continuous gradients + if (colorMode.isContinuous && colorMode.getColors && !forSegment) { + const colors = colorMode.getColors(theme); + const count = colors.length; + + this.defs.push( + + {colors.map((stopColor, i) => ( + + ))} + + ); + + return returnColor; + } + + // For value based colors we want to stay more true to the specific color + // So a radial gradient that adds a bit of light and shade works best + if (colorMode.isByValue) { + const color1 = tinycolor(baseColor).darken(5); + + this.defs.push( + + + + + + ); + } + + // For fixed / palette based color scales we can create a more fun + // hue and light based linear gradient that we rotate/move with the value + + const x2 = shape === 'circle' ? 0 : dimensions.centerX + dimensions.radius; + const y2 = shape === 'circle' ? dimensions.centerY + dimensions.radius : 0; + const color1 = tinycolor(baseColor).spin(-20).darken(5); + const color2 = tinycolor(baseColor).saturate(20).spin(20).brighten(10); + + // this makes it so the gradient is always brightest at the current value + const transform = + shape === 'circle' + ? `rotate(${360 * valuePercent - 180} ${dimensions.centerX} ${dimensions.centerY})` + : `translate(-${dimensions.radius * 2 * (1 - valuePercent)}, 0)`; + + this.defs.push( + + {theme.isDark ? ( + <> + + + + ) : ( + <> + + + + )} + + ); + + return returnColor; + } + + getMainBarColor(): string { + return this.getColor(this.options.fieldDisplay.display.color ?? FALLBACK_COLOR); + } + + getDefs(): React.ReactNode[] { + return this.defs; + } +} diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx new file mode 100644 index 00000000000..a2ff51c4b89 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.story.tsx @@ -0,0 +1,482 @@ +import { Meta, StoryFn } from '@storybook/react'; + +import { + applyFieldOverrides, + Field, + FieldType, + getFieldDisplayValues, + GrafanaTheme2, + toDataFrame, +} from '@grafana/data'; +import { FieldColorModeId } from '@grafana/schema'; + +import { useTheme2 } from '../../themes/ThemeContext'; +import { Stack } from '../Layout/Stack/Stack'; + +import { RadialGauge, RadialGaugeProps, RadialGradientMode, RadialShape, RadialTextMode } from './RadialGauge'; + +interface StoryProps extends RadialGaugeProps { + value: number; + seriesCount: number; + sparkline: boolean; + colorScheme: FieldColorModeId; + decimals: number; +} + +const meta: Meta = { + title: 'Plugins/RadialGauge', + component: RadialGauge, + excludeStories: ['RadialGaugeExample'], + parameters: { + controls: { + exclude: ['theme', 'values', 'vizCount'], + }, + }, + args: { + barWidthFactor: 0.2, + spotlight: false, + glowBar: false, + glowCenter: false, + sparkline: false, + value: undefined, + width: 200, + height: 200, + shape: 'circle', + gradient: 'none', + seriesCount: 1, + segmentCount: 0, + segmentSpacing: 0.4, + roundedBars: false, + thresholdsBar: false, + colorScheme: FieldColorModeId.Thresholds, + decimals: 0, + }, + argTypes: { + barWidthFactor: { control: { type: 'range', min: 0.1, max: 1, step: 0.01 } }, + width: { control: { type: 'range', min: 50, max: 600 } }, + height: { control: { type: 'range', min: 50, max: 600 } }, + value: { control: { type: 'range', min: 0, max: 110 } }, + spotlight: { control: 'boolean' }, + roundedBars: { control: 'boolean' }, + sparkline: { control: 'boolean' }, + thresholdsBar: { control: 'boolean' }, + gradient: { control: { type: 'radio' } }, + seriesCount: { control: { type: 'range', min: 1, max: 20 } }, + segmentCount: { control: { type: 'range', min: 0, max: 100 } }, + segmentSpacing: { control: { type: 'range', min: 0, max: 1, step: 0.01 } }, + colorScheme: { + control: { type: 'select' }, + options: [ + FieldColorModeId.Thresholds, + FieldColorModeId.Fixed, + FieldColorModeId.ContinuousGrYlRd, + FieldColorModeId.ContinuousBlYlRd, + FieldColorModeId.ContinuousBlPu, + ], + }, + decimals: { control: { type: 'range', min: 0, max: 7 } }, + }, +}; + +export const Basic: StoryFn = (args) => { + const visualizations: React.ReactNode[] = []; + const colors = ['blue', 'green', 'red', 'purple', 'orange', 'yellow', 'dark-red', 'dark-blue', 'dark-green']; + + for (let i = 0; i < args.seriesCount; i++) { + const color = args.colorScheme === FieldColorModeId.Fixed ? colors[i % colors.length] : undefined; + + visualizations.push( + + ); + } + + return ( + + {visualizations} + + ); +}; + +export const Examples: StoryFn = (args) => { + return ( + +
Bar width
+ + + + + + +
Effects
+ + + + + + +
Shape: Gauge & color scale
+ + + + +
Sparklines
+ + + + + + +
Segmented
+ + + + + +
Segmented color scale
+ + + + +
Thresholds
+ + + + + +
+ ); +}; + +Examples.parameters = { + controls: { include: ['barWidthFactor', 'value'] }, +}; + +export const MultiSeries: StoryFn = (args) => { + return ( + + + + ); +}; + +MultiSeries.args = { + barWidthFactor: 0.2, +}; + +export const Temp: StoryFn = (args) => { + return ( + + + + ); +}; + +interface ExampleProps { + gradient?: RadialGradientMode; + color?: string; + seriesName?: string; + value?: number; + shape?: RadialShape; + min?: number; + max?: number; + width?: number; + height?: number; + spotlight?: boolean; + glowBar?: boolean; + glowCenter?: boolean; + barWidthFactor?: number; + sparkline?: boolean; + seriesCount?: number; + vizCount?: number; + textMode?: RadialTextMode; + segmentCount?: number; + segmentSpacing?: number; + roundedBars?: boolean; + thresholdsBar?: boolean; + colorScheme?: FieldColorModeId; + decimals?: number; +} + +export function RadialGaugeExample({ + gradient = 'none', + color, + seriesName = 'Server A', + value = 70, + shape = 'circle', + min = 0, + max = 100, + width = 200, + height = 200, + spotlight = false, + glowBar = false, + glowCenter = false, + barWidthFactor = 0.4, + sparkline = false, + seriesCount = 0, + vizCount = 1, + textMode = 'auto', + segmentCount = 0, + segmentSpacing = 0.1, + roundedBars = false, + thresholdsBar = false, + colorScheme = FieldColorModeId.Thresholds, + decimals = 0, +}: ExampleProps) { + const theme = useTheme2(); + + if (color) { + colorScheme = FieldColorModeId.Fixed; + } + + const frame = toDataFrame({ + name: 'TestData', + length: 18, + fields: [ + { + name: 'Time', + type: FieldType.time, + values: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17], + config: { + min: 0, + max: 4, + }, + }, + { + name: seriesName, + type: FieldType.number, + values: [40, 45, 20, 25, 30, 28, 27, 30, 31, 26, 50, 55, 52, 20, 25, 30, 60, value], + config: { + min: min, + max: max, + unit: 'percent', + decimals: decimals, + color: { mode: colorScheme, fixedColor: color ? theme.visualization.getColorByName(color) : undefined }, + thresholds: { + mode: 'absolute', + steps: [ + { value: -Infinity, color: 'green' }, + { value: 65, color: 'orange' }, + { value: 85, color: 'red' }, + ], + }, + }, + // Add state and getLinks + state: {}, + getLinks: () => [], + }, + ...getExtraSeries(seriesCount, colorScheme, decimals, theme), + ], + }); + + const data = applyFieldOverrides({ + data: [frame], + fieldConfig: { + defaults: {}, + overrides: [], + }, + replaceVariables: (value) => value, + timeZone: 'utc', + theme, + }); + + const values = getFieldDisplayValues({ + fieldConfig: { overrides: [], defaults: {} }, + reduceOptions: { calcs: ['last'] }, + replaceVariables: (value) => value, + theme: theme, + data, + sparkline, + }); + + return ( + + ); +} + +function getExtraSeries(seriesCount: number, colorScheme: FieldColorModeId, decimals: number, theme: GrafanaTheme2) { + const fields: Field[] = []; + const colors = ['blue', 'green', 'purple', 'orange', 'yellow']; + + for (let i = 1; i < seriesCount; i++) { + fields.push({ + name: `Series ${i + 1}`, + type: FieldType.number, + values: [40, 45, 20, 25, 30, 28, 27, 30, 31, 26, 50, 55, 52, 20, 25, 30, 60, 20 * (i + 1)], + config: { + min: 0, + max: 100, + decimals: decimals, + unit: 'percent', + color: { mode: colorScheme, fixedColor: theme.visualization.getColorByName(colors[i % colors.length]) }, + }, + // Add state and getLinks + state: {}, + getLinks: () => [], + }); + } + + return fields; +} + +export default meta; diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.test.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.test.tsx new file mode 100644 index 00000000000..53b631e8549 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.test.tsx @@ -0,0 +1,11 @@ +import { render, screen } from '@testing-library/react'; + +import { RadialGaugeExample } from './RadialGauge.story'; + +describe('RadialGauge', () => { + it('should render', () => { + render(); + + expect(screen.getByRole('img')).toBeInTheDocument(); + }); +}); diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx new file mode 100644 index 00000000000..0974c475d94 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialGauge.tsx @@ -0,0 +1,259 @@ +import { css } from '@emotion/css'; +import { isNumber } from 'lodash'; +import { useId } from 'react'; + +import { DisplayValueAlignmentFactors, FieldDisplay, getDisplayProcessor, GrafanaTheme2 } from '@grafana/data'; +import { t } from '@grafana/i18n'; + +import { useStyles2, useTheme2 } from '../../themes/ThemeContext'; + +import { RadialBar } from './RadialBar'; +import { RadialBarSegmented } from './RadialBarSegmented'; +import { RadialColorDefs } from './RadialColorDefs'; +import { RadialSparkline } from './RadialSparkline'; +import { RadialText } from './RadialText'; +import { ThresholdsBar } from './ThresholdsBar'; +import { GlowGradient, MiddleCircleGlow, SpotlightGradient } from './effects'; +import { calculateDimensions, getValueAngleForValue } from './utils'; + +export interface RadialGaugeProps { + values: FieldDisplay[]; + width: number; + height: number; + /** + * Circle or gauge (partial circle) + */ + shape?: RadialShape; + gradient?: RadialGradientMode; + /** + * Bar width is always relative to size of the gauge. + * But this gives you control over the width relative to size. + * Range 0 - 1 (1 being the thickest) + * Defaults to 0.4 + **/ + barWidthFactor?: number; + /** Adds a white spotlight for the end position */ + spotlight?: boolean; + glowBar?: boolean; + glowCenter?: boolean; + roundedBars?: boolean; + thresholdsBar?: boolean; + /** + * Number of segments depends on size of gauge but this + * factor 1-10 gives you relative control + **/ + segmentCount?: number; + /** + * Distance between segments + * Factor between 0-1 + */ + segmentSpacing?: number; + /** + * If multiple is shown in a group (via VizRepeater). + * This impacts the auto textMode + */ + vizCount?: number; + /** Factors that should influence the positioning of the text */ + alignmentFactors?: DisplayValueAlignmentFactors; + /** Explicit font size control */ + valueManualFontSize?: number; + /** Explicit font size control */ + nameManualFontSize?: number; + /** Specify which text should be visible */ + textMode?: RadialTextMode; +} + +export type RadialGradientMode = 'none' | 'auto'; +export type RadialTextMode = 'auto' | 'value_and_name' | 'value' | 'name' | 'none'; +export type RadialShape = 'circle' | 'gauge'; + +export function RadialGauge(props: RadialGaugeProps) { + const { + width = 256, + height = 256, + shape = 'circle', + gradient = 'none', + barWidthFactor = 0.4, + spotlight = false, + glowBar = false, + glowCenter = false, + textMode = 'auto', + vizCount = 1, + segmentCount = 0, + segmentSpacing = 0.1, + roundedBars = true, + thresholdsBar = false, + values, + } = props; + const theme = useTheme2(); + const gaugeId = useId(); + const styles = useStyles2(getStyles); + + const startAngle = shape === 'gauge' ? 250 : 0; + const endAngle = shape === 'gauge' ? 110 : 360; + + const defs: React.ReactNode[] = []; + const graphics: React.ReactNode[] = []; + let sparklineElement: React.ReactNode | null = null; + + for (let barIndex = 0; barIndex < values.length; barIndex++) { + const displayValue = values[barIndex]; + const { angle, angleRange } = getValueAngleForValue(displayValue, startAngle, endAngle); + const color = displayValue.display.color ?? 'gray'; + const dimensions = calculateDimensions( + width, + height, + endAngle, + glowBar, + roundedBars, + barWidthFactor, + barIndex, + thresholdsBar + ); + + const displayProcessor = getFieldDisplayProcessor(displayValue); + const spotlightGradientId = `spotlight-${barIndex}-${gaugeId}`; + const glowFilterId = `glow-${gaugeId}`; + const colorDefs = new RadialColorDefs({ + gradient, + fieldDisplay: displayValue, + theme, + dimensions, + shape, + gaugeId, + displayProcessor, + }); + + if (spotlight) { + defs.push( + + ); + } + + if (segmentCount > 1) { + graphics.push( + + ); + } else { + graphics.push( + + ); + } + + // These elements are only added for first value / bar + if (barIndex === 0) { + if (glowBar) { + defs.push(); + } + + if (glowCenter) { + graphics.push(); + } + + if (thresholdsBar) { + graphics.push( + + ); + } + + graphics.push( + + ); + + if (displayValue.sparkline) { + sparklineElement = ( + + ); + } + } + } + + return ( +
+ + {defs} + {graphics} + + {sparklineElement} +
+ ); +} + +function getFieldDisplayProcessor(displayValue: FieldDisplay) { + if (displayValue.view && isNumber(displayValue.colIndex)) { + const dp = displayValue.view.getFieldDisplayProcessor(displayValue.colIndex); + if (dp) { + return dp; + } + } + + return getDisplayProcessor(); +} + +function getStyles(theme: GrafanaTheme2) { + return { + vizWrapper: css({ + position: 'relative', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + // Adds subtle shadow in light themes to help bar stand out + '.radial-arc-path': { + filter: theme.isLight ? `drop-shadow(0px 0px 1px #888);` : '', + }, + }), + }; +} diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialSparkline.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialSparkline.tsx new file mode 100644 index 00000000000..11426501a65 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialSparkline.tsx @@ -0,0 +1,52 @@ +import { css } from '@emotion/css'; + +import { FieldDisplay, GrafanaTheme2, FieldConfig } from '@grafana/data'; +import { GraphFieldConfig, GraphGradientMode, LineInterpolation } from '@grafana/schema'; + +import { Sparkline } from '../Sparkline/Sparkline'; + +import { RadialShape } from './RadialGauge'; +import { GaugeDimensions } from './utils'; + +interface RadialSparklineProps { + sparkline: FieldDisplay['sparkline']; + dimensions: GaugeDimensions; + theme: GrafanaTheme2; + color?: string; + shape?: RadialShape; +} +export function RadialSparkline({ sparkline, dimensions, theme, color, shape }: RadialSparklineProps) { + if (!sparkline) { + return null; + } + + const { radius, barWidth } = dimensions; + + const height = radius / 4; + const widthFactor = shape === 'gauge' ? 1.6 : 1.4; + const width = radius * widthFactor - barWidth; + const topPos = shape === 'gauge' ? `calc(50% + ${radius / 1.75}px)` : `calc(50% + ${radius / 2.8}px)`; + + const styles = css({ + position: 'absolute', + top: topPos, + }); + + const config: FieldConfig = { + color: { + mode: 'fixed', + fixedColor: color ?? 'blue', + }, + custom: { + gradientMode: GraphGradientMode.Opacity, + fillOpacity: 40, + lineInterpolation: LineInterpolation.Smooth, + }, + }; + + return ( +
+ +
+ ); +} diff --git a/packages/grafana-ui/src/components/RadialGauge/RadialText.tsx b/packages/grafana-ui/src/components/RadialGauge/RadialText.tsx new file mode 100644 index 00000000000..fac70fb7825 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/RadialText.tsx @@ -0,0 +1,151 @@ +import { css } from '@emotion/css'; + +import { DisplayValue, DisplayValueAlignmentFactors, formattedValueToString, GrafanaTheme2 } from '@grafana/data'; + +import { useStyles2 } from '../../themes/ThemeContext'; +import { calculateFontSize } from '../../utils/measureText'; + +import { RadialShape, RadialTextMode } from './RadialGauge'; +import { GaugeDimensions } from './utils'; + +// function toCartesian(centerX: number, centerY: number, radius: number, angleInDegrees: number) { +// let radian = ((angleInDegrees - 90) * Math.PI) / 180.0; +// return { +// x: centerX + radius * Math.cos(radian), +// y: centerY + radius * Math.sin(radian), +// }; +// } + +interface RadialTextProps { + displayValue: DisplayValue; + theme: GrafanaTheme2; + dimensions: GaugeDimensions; + textMode: RadialTextMode; + vizCount: number; + shape: RadialShape; + alignmentFactors?: DisplayValueAlignmentFactors; + valueManualFontSize?: number; + nameManualFontSize?: number; +} + +export function RadialText({ + displayValue, + theme, + dimensions, + textMode, + vizCount, + shape, + alignmentFactors, + valueManualFontSize, + nameManualFontSize, +}: RadialTextProps) { + const styles = useStyles2(getStyles); + const { centerX, centerY, radius, barWidth } = dimensions; + + if (textMode === 'none') { + return null; + } + + if (textMode === 'auto') { + textMode = vizCount === 1 ? 'value' : 'value_and_name'; + } + + const nameToAlignTo = (alignmentFactors ? alignmentFactors.title : displayValue.title) ?? ''; + const valueToAlignTo = formattedValueToString(alignmentFactors ? alignmentFactors : displayValue); + + const showValue = textMode === 'value' || textMode === 'value_and_name'; + const showName = textMode === 'name' || textMode === 'value_and_name'; + const maxTextWidth = radius * 2 - barWidth - radius / 7; + + // Not sure where this comes from but svg text is not using body line-height + const lineHeight = 1.21; + const valueWidthToRadiusFactor = 0.6; + const nameToHeightFactor = 0.3; + const largeRadiusScalingDecay = 0.92; + + // This pow 0.92 factor is to create a decay so the font size does not become rediculously large for very large panels + let maxValueHeight = valueWidthToRadiusFactor * Math.pow(radius, largeRadiusScalingDecay); + let maxNameHeight = radius / 4; + + if (showValue && showName) { + maxValueHeight = valueWidthToRadiusFactor * Math.pow(radius, largeRadiusScalingDecay); + maxNameHeight = nameToHeightFactor * Math.pow(radius, largeRadiusScalingDecay); + } + + const valueFontSize = + valueManualFontSize ?? + calculateFontSize( + valueToAlignTo, + maxTextWidth, + maxValueHeight, + lineHeight, + undefined, + theme.typography.body.fontWeight + ); + + const nameFontSize = + nameManualFontSize ?? + calculateFontSize( + nameToAlignTo, + maxTextWidth, + maxNameHeight, + lineHeight, + undefined, + theme.typography.body.fontWeight + ); + + const unitFontSize = Math.max(valueFontSize * 0.7, 5); + const valueHeight = valueFontSize * lineHeight; + const nameHeight = nameFontSize * lineHeight; + + const valueY = showName ? centerY - nameHeight / 2 : centerY; + const nameY = showValue ? valueY + valueHeight * 0.7 : centerY; + const nameColor = showValue ? theme.colors.text.secondary : theme.colors.text.primary; + const suffixShift = (valueFontSize - unitFontSize * 1.2) / 2; + + // For gauge shape we shift text up a bit + const valueDy = shape === 'gauge' ? -valueFontSize * 0.3 : 0; + const nameDy = shape === 'gauge' ? -nameFontSize * 0.7 : 0; + + return ( + + {showValue && ( + + {displayValue.prefix ?? ''} + {displayValue.text} + + {displayValue.suffix ?? ''} + + + )} + {showName && ( + + {displayValue.title} + + )} + + ); +} + +const getStyles = (theme: GrafanaTheme2) => ({ + text: css({ + verticalAlign: 'bottom', + }), +}); diff --git a/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx b/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx new file mode 100644 index 00000000000..df81b363b26 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/ThresholdsBar.tsx @@ -0,0 +1,72 @@ +import { FieldDisplay } from '@grafana/data'; + +import { useTheme2 } from '../../themes/ThemeContext'; +import { getFormattedThresholds } from '../Gauge/utils'; + +import { RadialArcPath } from './RadialArcPath'; +import { RadialColorDefs } from './RadialColorDefs'; +import { GaugeDimensions } from './utils'; + +export interface Props { + dimensions: GaugeDimensions; + angleRange: number; + startAngle: number; + endAngle: number; + fieldDisplay: FieldDisplay; + roundedBars?: boolean; + glowFilter?: string; + colorDefs: RadialColorDefs; +} +export function ThresholdsBar({ + dimensions, + fieldDisplay, + startAngle, + angleRange, + roundedBars, + glowFilter, + colorDefs, +}: Props) { + const theme = useTheme2(); + const fieldConfig = fieldDisplay.field; + const decimals = fieldConfig.decimals ?? 2; + const min = fieldConfig.min ?? 0; + const max = fieldConfig.max ?? 100; + const thresholds = getFormattedThresholds(decimals, fieldConfig, theme); + + const outerRadius = dimensions.radius + dimensions.barWidth / 2; + const thresholdDimensions = { + ...dimensions, + barWidth: dimensions.thresholdsBarWidth, + radius: outerRadius + dimensions.thresholdsBarWidth / 2 + dimensions.thresholdsBarSpacing, + }; + + let currentStart = startAngle; + let paths: React.ReactNode[] = []; + + for (let i = 1; i < thresholds.length; i++) { + const threshold = thresholds[i]; + const valueDeg = ((threshold.value - min) / (max - min)) * angleRange; + const lengthDeg = valueDeg - currentStart + startAngle; + + paths.push( + + ); + + currentStart += lengthDeg; + } + + return ( + <> + {paths} + {colorDefs.getDefs()} + + ); +} diff --git a/packages/grafana-ui/src/components/RadialGauge/effects.tsx b/packages/grafana-ui/src/components/RadialGauge/effects.tsx new file mode 100644 index 00000000000..76f15900a14 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/effects.tsx @@ -0,0 +1,94 @@ +import { GrafanaTheme2 } from '@grafana/data'; + +import { GaugeDimensions } from './utils'; + +export interface GlowGradientProps { + id: string; + radius: number; +} + +export function GlowGradient({ id, radius }: GlowGradientProps) { + const glowSize = 0.03 * radius; + + return ( + + + + + + + + ); +} + +export function SpotlightGradient({ + id, + dimensions, + roundedBars, + angle, + theme, +}: { + id: string; + dimensions: GaugeDimensions; + angle: number; + roundedBars: boolean; + theme: GrafanaTheme2; +}) { + const angleRadian = ((angle - 90) * Math.PI) / 180; + + let x1 = dimensions.centerX + dimensions.radius * Math.cos(angleRadian - 0.2); + let y1 = dimensions.centerY + dimensions.radius * Math.sin(angleRadian - 0.2); + let x2 = dimensions.centerX + dimensions.radius * Math.cos(angleRadian); + let y2 = dimensions.centerY + dimensions.radius * Math.sin(angleRadian); + + if (theme.isLight) { + return ( + + + + + + ); + } + + return ( + + + + {roundedBars && } + + ); +} + +export function CenterGlowGradient({ gaugeId, color }: { gaugeId: string; color: string }) { + return ( + + + + + ); +} + +export interface CenterGlowProps { + dimensions: GaugeDimensions; + gaugeId: string; + color?: string; +} + +export function MiddleCircleGlow({ dimensions, gaugeId, color }: CenterGlowProps) { + const gradientId = `circle-glow-${gaugeId}`; + + return ( + <> + + + + + + + + + + + ); +} diff --git a/packages/grafana-ui/src/components/RadialGauge/utils.test.ts b/packages/grafana-ui/src/components/RadialGauge/utils.test.ts new file mode 100644 index 00000000000..bd24ee4c00b --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/utils.test.ts @@ -0,0 +1,173 @@ +import { FieldDisplay } from '@grafana/data'; + +import type { RadialGaugeProps } from './RadialGauge'; +import { calculateDimensions, toRad, getValueAngleForValue } from './utils'; + +describe('RadialGauge utils', () => { + function calc(overrides: Partial = {}) { + return calculateDimensions( + overrides.width ?? 200, + overrides.height ?? 200, + overrides.shape === 'gauge' ? 110 : 360, + overrides.glowBar ?? false, + overrides.roundedBars ?? false, + overrides.barWidthFactor ?? 0.4, + overrides.barIndex ?? 0, + overrides.thresholdsBar ?? false + ); + } + + describe('calculateDimensions', () => { + it('should calculate basic dimensions for a square gauge', () => { + const result = calc(); + + expect(result).toMatchObject({ + centerX: 100, // width / 2 + centerY: 100, // height / 2 + barWidth: expect.closeTo(13.33, 1), + radius: expect.closeTo(93.33, 1), + margin: 0, // no glow + barIndex: 0, + thresholdsBarWidth: 0, + thresholdsBarSpacing: 2, + }); + }); + + it('should handle different aspect ratios', () => { + const wideGauge = calc({ width: 400, height: 200 }); + const tallGauge = calc({ width: 200, height: 400 }); + + expect(wideGauge.centerX).toBe(200); + expect(wideGauge.centerY).toBe(100); + expect(tallGauge.centerX).toBe(100); + expect(tallGauge.centerY).toBe(200); + }); + + it('should apply glow margin when glow is enabled', () => { + const withoutGlow = calc({ width: 200, height: 200 }); + const withGlow = calc({ width: 200, height: 200, glowBar: true }); + + expect(withGlow.margin).toBeGreaterThan(0); + expect(withoutGlow.margin).toBe(0); + expect(withGlow.radius).toBeLessThan(withoutGlow.radius); // glow reduces available space + }); + + it('should adjust radius for rounded bars when endAngle < 180', () => { + const sharpBars = calc({}); + const roundedBars = calc({ roundedBars: true }); + const roundedGauge = calc({ roundedBars: true, shape: 'gauge' }); + + expect(roundedBars.radius).toEqual(sharpBars.radius); + expect(roundedGauge.radius).toBeLessThan(sharpBars.radius); + }); + + it('should handle threshold bars', () => { + const withoutThresholds = calc({ width: 200, height: 200 }); + const withThresholds = calc({ width: 200, height: 200, thresholdsBar: true }); + + expect(withThresholds.thresholdsBarWidth).toBe(4); + expect(withThresholds.radius).toBeLessThan(withoutThresholds.radius); + }); + + it('should adjust radius for multiple bars (barIndex > 0)', () => { + const firstBar = calc({ width: 200, height: 200, barIndex: 0 }); + const secondBar = calc({ width: 200, height: 200, barIndex: 1 }); + const thirdBar = calc({ width: 200, height: 200, barIndex: 2 }); + + expect(secondBar.radius).toBeLessThan(firstBar.radius); + expect(thirdBar.radius).toBeLessThan(secondBar.radius); + expect(thirdBar.barIndex).toBe(2); + }); + + it('should handle different barWidthFactors', () => { + const thinBar = calc({ width: 200, height: 200, barWidthFactor: 0.2, barIndex: 0 }); + const thickBar = calc({ width: 200, height: 200, barWidthFactor: 0.8, barIndex: 0 }); + + expect(thickBar.barWidth).toBeGreaterThan(thinBar.barWidth); + expect(thinBar.radius).toBeGreaterThan(thickBar.radius); // thinner bars leave more space + }); + + it('should enforce minimum bar width', () => { + const result = calc({ width: 50, height: 50, barWidthFactor: 0.01, barIndex: 0 }); + expect(result.barWidth).toBeGreaterThanOrEqual(2); + }); + + it('should optimize space and position for gauge shape', () => { + const gauge = calc({ width: 200, height: 200, shape: 'gauge', barIndex: 0 }); + + // Different end angles should affect the available space differently + expect(gauge.radius).toBeCloseTo(93.33, 1); + expect(gauge.centerY).toBeCloseTo(132.89, 1); // centerY can be much lower when shape is a semi circle + }); + }); + + describe('toRad', () => { + it('should convert degrees to radians with -90 degree offset', () => { + expect(toRad(0)).toBeCloseTo(-Math.PI / 2, 5); // 0° becomes -90° in radians + expect(toRad(90)).toBeCloseTo(0, 5); // 90° becomes 0° in radians + expect(toRad(180)).toBeCloseTo(Math.PI / 2, 5); // 180° becomes 90° in radians + expect(toRad(270)).toBeCloseTo(Math.PI, 5); // 270° becomes 180° in radians + }); + + it('should handle negative angles', () => { + expect(toRad(-90)).toBeCloseTo(-Math.PI, 5); + }); + }); + + describe('getValueAngleForValue', () => { + const createFieldDisplay = (value: number, min = 0, max = 100): FieldDisplay => ({ + display: { + numeric: value, + text: value.toString(), + color: 'blue', + }, + field: { + min, + max, + }, + view: undefined, + colIndex: 0, + rowIndex: 0, + name: 'test', + getLinks: () => [], + hasLinks: false, + }); + + it('should calculate angle for value in range', () => { + const fieldDisplay = createFieldDisplay(50, 0, 100); + const result = getValueAngleForValue(fieldDisplay, 0, 360); + + expect(result.angle).toBe(180); // 50% of 360° + expect(result.angleRange).toBe(360); + }); + + it('should handle different start and end angles', () => { + const fieldDisplay = createFieldDisplay(50, 0, 100); + const result = getValueAngleForValue(fieldDisplay, 90, 270); + + expect(result.angle).toBe(135); // 50% of 360° range + expect(result.angleRange).toBe(270); + }); + + it('should clamp angle to maximum range', () => { + const fieldDisplay = createFieldDisplay(150, 0, 100); // value exceeds max + const result = getValueAngleForValue(fieldDisplay, 0, 360); + + expect(result.angle).toBe(360); // clamped to angleRange + }); + + it('should handle minimum values', () => { + const fieldDisplay = createFieldDisplay(0, 0, 100); + const result = getValueAngleForValue(fieldDisplay, 0, 360); + + expect(result.angle).toBe(0); + }); + + it('should handle maximum values', () => { + const fieldDisplay = createFieldDisplay(100, 0, 100); + const result = getValueAngleForValue(fieldDisplay, 0, 360); + + expect(result.angle).toBe(360); + }); + }); +}); diff --git a/packages/grafana-ui/src/components/RadialGauge/utils.ts b/packages/grafana-ui/src/components/RadialGauge/utils.ts new file mode 100644 index 00000000000..f59243f9d66 --- /dev/null +++ b/packages/grafana-ui/src/components/RadialGauge/utils.ts @@ -0,0 +1,105 @@ +import { FieldDisplay } from '@grafana/data'; + +export function getValueAngleForValue(fieldDisplay: FieldDisplay, startAngle: number, endAngle: number) { + const angleRange = (360 % (startAngle === 0 ? 1 : startAngle)) + endAngle; + const min = fieldDisplay.field.min ?? 0; + const max = fieldDisplay.field.max ?? 100; + + let angle = ((fieldDisplay.display.numeric - min) / (max - min)) * angleRange; + + if (angle > angleRange) { + angle = angleRange; + } + + return { angleRange, angle }; +} + +/** + * Returns the angle in radians for a given angle in degrees + * But shifted -90 degrees to make 0 degree angle point upwards + * @param angle + * @returns + */ +export function toRad(angle: number) { + return ((angle - 90) * Math.PI) / 180; +} + +export interface GaugeDimensions { + margin: number; + radius: number; + centerX: number; + centerY: number; + barWidth: number; + endAngle?: number; + barIndex: number; + thresholdsBarWidth: number; + thresholdsBarSpacing: number; +} + +export function calculateDimensions( + width: number, + height: number, + endAngle: number, + glow: boolean, + roundedBars: boolean, + barWidthFactor: number, + barIndex: number, + thresholdBar?: boolean +): GaugeDimensions { + const yMaxAngle = endAngle > 180 ? 180 : endAngle; + let margin = 0; + + // Max radius based on width + let maxRadiusH = width / 2 - margin; + + // Max radius based on height + let heightRatioV = Math.sin(toRad(yMaxAngle)); + let maxRadiusV = (height - margin * 2) / (1 + heightRatioV); + + let maxRadius = Math.min(maxRadiusH, maxRadiusV); + + const barWidth = Math.max(barWidthFactor * (maxRadius / 3), 2); + const thresholdsToBarWidth = 0.2 * Math.pow(barWidth, 0.92); + const thresholdsBarWidth = thresholdBar ? Math.min(Math.max(thresholdsToBarWidth, 4), 12) : 0; + const thresholdsBarSpacing = Math.min(Math.max(thresholdsBarWidth / 2, 2), 12); + + let outerRadius = maxRadius; + + // If rounded bars is enabled they need a bit more vertical space + if (yMaxAngle < 180 && roundedBars) { + outerRadius -= barWidth; + } + + if (thresholdsBarWidth > 0) { + maxRadiusH -= thresholdsBarWidth + thresholdsBarSpacing; + maxRadiusV -= thresholdsBarWidth + thresholdsBarSpacing; + outerRadius = Math.min(maxRadiusH, maxRadiusV); + } + + if (glow) { + margin = 0.04 * outerRadius; + outerRadius -= (margin * 2) / (1 + heightRatioV); + } + + let innerRadius = outerRadius - barWidth / 2; + + const maxY = maxRadius * Math.sin(toRad(yMaxAngle)) + maxRadius; + const rest = height - maxY - margin * 2; + const centerX = width / 2; + const centerY = maxRadius + margin + rest / 2; + + if (barIndex > 0) { + innerRadius = innerRadius - (barWidth + 4) * barIndex; + } + + return { + margin, + radius: innerRadius, + centerX, + centerY, + barWidth, + barIndex, + thresholdsBarWidth, + thresholdsBarSpacing, + }; +} diff --git a/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx b/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx index 7fe09d92777..8a42baf89d7 100644 --- a/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx +++ b/packages/grafana-ui/src/components/VizRepeater/VizRepeater.tsx @@ -139,7 +139,7 @@ export class VizRepeater extends PureComponent{items}; + return
{items}
; } render() { diff --git a/packages/grafana-ui/src/internal/index.ts b/packages/grafana-ui/src/internal/index.ts index 6b55c2224ea..07ee4c5d211 100644 --- a/packages/grafana-ui/src/internal/index.ts +++ b/packages/grafana-ui/src/internal/index.ts @@ -104,3 +104,4 @@ export { useComponentInstanceId } from '../utils/useComponetInstanceId'; export { closePopover } from '../utils/closePopover'; export { flattenTokens } from '../slate-plugins/slate-prism'; +export { RadialGauge } from '../components/RadialGauge/RadialGauge'; diff --git a/pkg/registry/schemas/composable_kind.go b/pkg/registry/schemas/composable_kind.go index e17a71e15a9..06000984e96 100644 --- a/pkg/registry/schemas/composable_kind.go +++ b/pkg/registry/schemas/composable_kind.go @@ -278,6 +278,16 @@ func GetComposableKinds() ([]ComposableKind, error) { CueFile: piechartCue, }) + radialbarCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/radialbar/panelcfg.cue")) + if err != nil { + return nil, err + } + kinds = append(kinds, ComposableKind{ + Name: "radialbar", + Filename: "panelcfg.cue", + CueFile: radialbarCue, + }) + statCue, err := loadCueFileWithCommon(root, filepath.Join(root, "./public/app/plugins/panel/stat/panelcfg.cue")) if err != nil { return nil, err diff --git a/pkg/services/pluginsintegration/plugintest/plugins_test.go b/pkg/services/pluginsintegration/plugintest/plugins_test.go index 9983fe5ebbb..25fff53a5fb 100644 --- a/pkg/services/pluginsintegration/plugintest/plugins_test.go +++ b/pkg/services/pluginsintegration/plugintest/plugins_test.go @@ -241,6 +241,7 @@ func verifyCorePluginCatalogue(t *testing.T, ctx context.Context, ps *pluginstor "welcome": {}, "xychart": {}, "datagrid": {}, + "radialbar": {}, } expDataSources := map[string]struct{}{ diff --git a/public/app/core/icons/cached.json b/public/app/core/icons/cached.json index a56e7d35c3d..e78419e6d72 100644 --- a/public/app/core/icons/cached.json +++ b/public/app/core/icons/cached.json @@ -192,5 +192,6 @@ "unicons/window-grid", "unicons/ban", "unicons/git", - "unicons/bitbucket" + "unicons/bitbucket", + "unicons/tachometer-fast" ] diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index 33827f3a896..49ea5872f7e 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -68,6 +68,9 @@ const heatmapPanel = async () => const nodeGraph = async () => await import(/* webpackChunkName: "nodeGraphPanel" */ 'app/plugins/panel/nodeGraph/module'); +const radialBar = async () => + await import(/* webpackChunkName: "radialBarPanel" */ 'app/plugins/panel/radialbar/module'); + const builtInPlugins: Record Promise)> = { // datasources 'core:plugin/cloudwatch': cloudwatchPlugin, @@ -110,6 +113,7 @@ const builtInPlugins: Record Promise) { + return ( + + + props.onChange({ ...props.value, rounded: e.currentTarget.checked })} + /> + + + + props.onChange({ ...props.value, barGlow: e.currentTarget.checked })} + /> + + + + props.onChange({ ...props.value, centerGlow: e.currentTarget.checked })} + /> + + + + props.onChange({ ...props.value, spotlight: e.currentTarget.checked })} + /> + + + + ); +} diff --git a/public/app/plugins/panel/radialbar/RadialBarPanel.tsx b/public/app/plugins/panel/radialbar/RadialBarPanel.tsx new file mode 100644 index 00000000000..2d023c95bf5 --- /dev/null +++ b/public/app/plugins/panel/radialbar/RadialBarPanel.tsx @@ -0,0 +1,105 @@ +import { + DisplayValueAlignmentFactors, + FieldDisplay, + getDisplayValueAlignmentFactors, + getFieldDisplayValues, + PanelProps, +} from '@grafana/data'; +import { DataLinksContextMenu, Stack, VizRepeater, VizRepeaterRenderValueProps } from '@grafana/ui'; +import { DataLinksContextMenuApi, RadialGauge } from '@grafana/ui/internal'; +import { config } from 'app/core/config'; + +import { Options } from './panelcfg.gen'; + +export function RadialBarPanel({ + height, + width, + data, + renderCounter, + options, + replaceVariables, + fieldConfig, + timeZone, +}: PanelProps) { + function renderComponent( + valueProps: VizRepeaterRenderValueProps, + menuProps: DataLinksContextMenuApi + ) { + const { width, height, value } = valueProps; + + return ( + + ); + } + + function renderValue( + valueProps: VizRepeaterRenderValueProps + ): JSX.Element { + const { value } = valueProps; + const { getLinks, hasLinks } = value; + + if (hasLinks && getLinks) { + return ( + + {(api) => { + return renderComponent(valueProps, api); + }} + + ); + } + + return renderComponent(valueProps, {}); + } + + function getValues(): FieldDisplay[] { + return getFieldDisplayValues({ + fieldConfig, + reduceOptions: options.reduceOptions, + replaceVariables, + theme: config.theme2, + data: data.series, + sparkline: options.sparkline, + timeZone, + }); + } + + const minVizHeight = 60; + const minVizWidth = 60; + + return ( + + + + ); +} diff --git a/public/app/plugins/panel/radialbar/img/icon_gauge.svg b/public/app/plugins/panel/radialbar/img/icon_gauge.svg new file mode 100644 index 00000000000..46e667f6e88 --- /dev/null +++ b/public/app/plugins/panel/radialbar/img/icon_gauge.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/app/plugins/panel/radialbar/module.tsx b/public/app/plugins/panel/radialbar/module.tsx new file mode 100644 index 00000000000..ea21463ce1f --- /dev/null +++ b/public/app/plugins/panel/radialbar/module.tsx @@ -0,0 +1,107 @@ +import { PanelPlugin } from '@grafana/data'; +import { t } from '@grafana/i18n'; +import { commonOptionsBuilder } from '@grafana/ui'; + +import { addOrientationOption, addStandardDataReduceOptions } from '../stat/common'; + +import { EffectsEditor } from './EffectsEditor'; +import { RadialBarPanel } from './RadialBarPanel'; +import { defaultGaugePanelEffects, defaultOptions, Options } from './panelcfg.gen'; +import { GaugeSuggestionsSupplier } from './suggestions'; + +export const plugin = new PanelPlugin(RadialBarPanel) + .useFieldConfig({}) + .setPanelOptions((builder) => { + const category = [t('gauge.category-radial-bar', 'Gauge')]; + addStandardDataReduceOptions(builder); + addOrientationOption(builder, category); + commonOptionsBuilder.addTextSizeOptions(builder, { withTitle: true, withValue: true }); + + builder.addRadio({ + path: 'shape', + name: t('radialbar.config.shape', 'Shape'), + category, + defaultValue: defaultOptions.shape, + settings: { + options: [ + { value: 'circle', label: t('radialbar.config.shape-circle', 'Circle'), icon: 'circle' }, + { value: 'gauge', label: t('radialbar.config.shape-gauge', 'Gauge'), icon: 'tachometer-fast' }, + ], + }, + }); + + builder.addRadio({ + path: 'gradient', + name: t('radialbar.config.gradient', 'Gradient'), + category, + defaultValue: 'none', + settings: { + options: [ + { value: 'none', label: t('radialbar.config.gradient-none', 'None') }, + { value: 'auto', label: t('radialbar.config.gradient-auto', 'Auto') }, + ], + }, + }); + + builder.addSliderInput({ + path: 'barWidthFactor', + name: t('radialbar.config.bar-width', 'Bar width'), + category, + defaultValue: defaultOptions.barWidthFactor, + settings: { + min: 0.1, + max: 1, + step: 0.01, + }, + }); + + builder.addSliderInput({ + path: 'segmentCount', + name: t('radialbar.config.segment-count', 'Segments'), + category, + defaultValue: defaultOptions.segmentCount, + settings: { + min: 1, + max: 100, + step: 1, + }, + }); + + builder.addSliderInput({ + path: 'segmentSpacing', + name: t('radialbar.config.segment-spacing', 'Segment spacing'), + category, + defaultValue: defaultOptions.segmentSpacing, + showIf: (options) => options.segmentCount > 1, + settings: { + min: 0, + max: 1, + step: 0.01, + }, + }); + + builder.addBooleanSwitch({ + path: 'sparkline', + name: t('radialbar.config.sparkline', 'Show sparkline'), + category, + defaultValue: defaultOptions.sparkline, + }); + + builder.addBooleanSwitch({ + path: 'showThresholdMarkers', + name: t('radialbar.config.threshold-markers', 'Show thresholds'), + category, + defaultValue: defaultOptions.showThresholdMarkers, + }); + + builder.addCustomEditor({ + id: 'radialbar-effects', + path: 'effects', + name: 'Effects', + category, + editor: EffectsEditor, + settings: {}, + defaultValue: defaultGaugePanelEffects, + }); + }) + .setSuggestionsSupplier(new GaugeSuggestionsSupplier()); diff --git a/public/app/plugins/panel/radialbar/notes.txt b/public/app/plugins/panel/radialbar/notes.txt new file mode 100644 index 00000000000..abdece62a2e --- /dev/null +++ b/public/app/plugins/panel/radialbar/notes.txt @@ -0,0 +1,12 @@ + +* unit where name is +* dynamic font size based on text length +* text alignment factors +* segment hue gradient +* Rethink gradient / color options +* threshold & min/max labels + + +Gauge => new gauge migration notes + +Old gauge "Show threshold markers" does nothing when color scheme != From thresholds diff --git a/public/app/plugins/panel/radialbar/panelcfg.cue b/public/app/plugins/panel/radialbar/panelcfg.cue new file mode 100644 index 00000000000..dd364967ced --- /dev/null +++ b/public/app/plugins/panel/radialbar/panelcfg.cue @@ -0,0 +1,50 @@ +// Copyright 2021 Grafana Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package grafanaplugin + +import ( + "github.com/grafana/grafana/packages/grafana-schema/src/common" +) + +composableKinds: PanelCfg: { + maturity: "experimental" + + lineage: { + schemas: [{ + version: [0, 0] + schema: { + GaugePanelEffects: { + barGlow?: bool | *false + spotlight?: bool | *false + rounded?: bool | *false + centerGlow?: bool | *true + } @cuetsy(kind="interface") + + Options: { + common.SingleStatBaseOptions + showThresholdMarkers: bool | *true + segmentCount: number | *1 + segmentSpacing: number | *0.3 + sparkline?: bool | *false + shape: "circle" | *"gauge" + barWidthFactor: number | *0.4 + gradient: *"none" | "auto" + effects: GaugePanelEffects | *{} + } @cuetsy(kind="interface") + } + }] + lenses: [] + } +} diff --git a/public/app/plugins/panel/radialbar/panelcfg.gen.ts b/public/app/plugins/panel/radialbar/panelcfg.gen.ts new file mode 100644 index 00000000000..9a89bcb48f2 --- /dev/null +++ b/public/app/plugins/panel/radialbar/panelcfg.gen.ts @@ -0,0 +1,47 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. +// +// Generated by: +// public/app/plugins/gen.go +// Using jennies: +// TSTypesJenny +// PluginTsTypesJenny +// +// Run 'make gen-cue' from repository root to regenerate. + +import * as common from '@grafana/schema'; + +export interface GaugePanelEffects { + barGlow?: boolean; + centerGlow?: boolean; + rounded?: boolean; + spotlight?: boolean; +} + +export const defaultGaugePanelEffects: Partial = { + barGlow: false, + centerGlow: true, + rounded: false, + spotlight: false, +}; + +export interface Options extends common.SingleStatBaseOptions { + barWidthFactor: number; + effects: GaugePanelEffects; + gradient: ('none' | 'auto'); + segmentCount: number; + segmentSpacing: number; + shape: ('circle' | 'gauge'); + showThresholdMarkers: boolean; + sparkline?: boolean; +} + +export const defaultOptions: Partial = { + barWidthFactor: 0.4, + effects: {}, + gradient: 'none', + segmentCount: 1, + segmentSpacing: 0.3, + shape: 'gauge', + showThresholdMarkers: true, + sparkline: false, +}; diff --git a/public/app/plugins/panel/radialbar/plugin.json b/public/app/plugins/panel/radialbar/plugin.json new file mode 100644 index 00000000000..d8e9ab862b9 --- /dev/null +++ b/public/app/plugins/panel/radialbar/plugin.json @@ -0,0 +1,24 @@ +{ + "type": "panel", + "name": "New Gauge", + "id": "radialbar", + "state": "alpha", + "info": { + "description": "Standard gauge visualization", + "author": { + "name": "Grafana Labs", + "url": "https://grafana.com" + }, + "logos": { + "small": "img/icon_gauge.svg", + "large": "img/icon_gauge.svg" + }, + "links": [ + { "name": "Raise issue", "url": "https://github.com/grafana/grafana/issues/new" }, + { + "name": "Documentation", + "url": "https://grafana.com/docs/grafana/latest/panels-visualizations/visualizations/gauge/" + } + ] + } +} diff --git a/public/app/plugins/panel/radialbar/suggestions.ts b/public/app/plugins/panel/radialbar/suggestions.ts new file mode 100644 index 00000000000..31f2f6e610e --- /dev/null +++ b/public/app/plugins/panel/radialbar/suggestions.ts @@ -0,0 +1,58 @@ +import { VisualizationSuggestionsBuilder } from '@grafana/data'; +import { SuggestionName } from 'app/types/suggestions'; + +import { Options } from './panelcfg.gen'; + +export class GaugeSuggestionsSupplier { + getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { + const { dataSummary } = builder; + + if (!dataSummary.hasData || !dataSummary.hasNumberField) { + return; + } + + // for many fields / series this is probably not a good fit + if (dataSummary.numberFieldCount >= 50) { + return; + } + + const list = builder.getListAppender({ + name: SuggestionName.Gauge, + pluginId: 'gauge', + options: {}, + fieldConfig: { + defaults: {}, + overrides: [], + }, + cardOptions: { + previewModifier: (s) => { + if (s.options!.reduceOptions.values) { + s.options!.reduceOptions.limit = 2; + } + }, + }, + }); + + if (dataSummary.hasStringField && dataSummary.frameCount === 1 && dataSummary.rowCountTotal < 10) { + list.append({ + name: SuggestionName.RadialBar, + options: { + reduceOptions: { + values: true, + calcs: [], + }, + }, + }); + } else { + list.append({ + name: SuggestionName.RadialBar, + options: { + reduceOptions: { + values: false, + calcs: ['lastNotNull'], + }, + }, + }); + } + } +} diff --git a/public/app/types/suggestions.ts b/public/app/types/suggestions.ts index 2df8c82b9af..beea4ecb59c 100644 --- a/public/app/types/suggestions.ts +++ b/public/app/types/suggestions.ts @@ -19,6 +19,7 @@ export enum SuggestionName { StatColoredBackground = 'Stat colored background', Gauge = 'Gauge', GaugeNoThresholds = 'Gauge no thresholds', + RadialBar = 'Radial bar', BarGaugeBasic = 'Bar gauge basic', BarGaugeLCD = 'Bar gauge LCD', Table = 'Table', diff --git a/public/locales/en-US/grafana.json b/public/locales/en-US/grafana.json index dbcb5040a1e..c3eb0a2d8af 100644 --- a/public/locales/en-US/grafana.json +++ b/public/locales/en-US/grafana.json @@ -7678,6 +7678,7 @@ }, "gauge": { "category-gauge": "Gauge", + "category-radial-bar": "Gauge", "description-min-height": "Minimum row height (horizontal orientation)", "description-min-width": "Minimum column width (vertical orientation)", "description-neutral": "Leave empty to use Min as neutral point", @@ -12051,6 +12052,27 @@ }, "query-editor-not-exported": "Data source plugin does not export any Query Editor component" }, + "radialbar": { + "config": { + "bar-width": "Bar width", + "effects": { + "bar-glow": "Bar glow", + "center-glow": "Center glow", + "rounded-bars": "Rounded bars", + "spotlight": "Spotlight" + }, + "gradient": "Gradient", + "gradient-auto": "Auto", + "gradient-none": "None", + "segment-count": "Segments", + "segment-spacing": "Segment spacing", + "shape": "Shape", + "shape-circle": "Circle", + "shape-gauge": "Gauge", + "sparkline": "Show sparkline", + "threshold-markers": "Show thresholds" + } + }, "recently-deleted": { "buttons": { "restore": "Restore"