Merge branch 'main' into kristina/rtk-corr

This commit is contained in:
Kristina Durivage
2025-12-22 11:52:46 -06:00
72 changed files with 5061 additions and 3470 deletions
@@ -71,12 +71,11 @@
"id": 1,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": true,
"spotlight": false,
"gradient": false
},
"orientation": "auto",
@@ -150,12 +149,11 @@
"id": 4,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": true,
"rounded": true,
"spotlight": false,
"gradient": false
},
"orientation": "auto",
@@ -229,12 +227,11 @@
"id": 3,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": false,
"gradient": false
},
"orientation": "auto",
@@ -271,85 +268,6 @@
"title": "Center and bar glow",
"type": "radialbar"
},
{
"datasource": {
"type": "grafana-testdata-datasource"
},
"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": false
},
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"segmentCount": 1,
"segmentSpacing": 0.3,
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
{
"alias": "1",
"datasource": {
"type": "grafana-testdata-datasource"
},
"max": 100,
"min": 1,
"noise": 22,
"refId": "A",
"scenarioId": "random_walk",
"spread": 22,
"startValue": 1
}
],
"title": "Spotlight",
"type": "radialbar"
},
{
"datasource": {
"type": "grafana-testdata-datasource"
@@ -391,10 +309,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -470,10 +387,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": false,
"spotlight": true,
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -549,10 +465,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": false,
"spotlight": true,
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -641,10 +556,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -720,10 +634,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -799,10 +712,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -878,10 +790,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -974,10 +885,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1053,10 +963,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1132,10 +1041,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1211,10 +1119,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1290,10 +1197,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1386,10 +1292,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1469,10 +1374,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1552,10 +1456,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1641,13 +1544,13 @@
"options": {
"barWidth": 12,
"barWidthFactor": 0.4,
"barShape": "rounded",
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": true
},
"endpointMarker": "glow",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1662,8 +1565,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1730,10 +1632,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1748,8 +1649,7 @@
"shape": "gauge",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": true,
"spotlight": true
"sparkline": true
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1830,10 +1730,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1848,8 +1747,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1917,9 +1815,6 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"sparkline": false,
"spotlight": true,
"gradient": true
},
"glow": "both",
@@ -1934,10 +1829,10 @@
"segmentCount": 12,
"segmentSpacing": 0.3,
"shape": "circle",
"barShape": "rounded",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2004,10 +1899,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -2022,8 +1916,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2090,10 +1983,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"rounded": true,
"spotlight": true,
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -2108,8 +2000,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -955,8 +955,6 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": false
},
"orientation": "auto",
@@ -0,0 +1,166 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v1beta1",
"metadata": {
"name": "groupby-test"
},
"spec": {
"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,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.4.0-pre",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"editorMode": "code",
"expr": "sum(counters_requests)",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "works with group by var",
"type": "timeseries"
}
],
"preload": false,
"schemaVersion": 42,
"tags": [],
"templating": {
"list": [
{
"current": {
"text": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
],
"value": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
]
},
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"name": "Group by",
"type": "groupby"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "groupby test",
"weekStart": ""
}
}
@@ -77,13 +77,12 @@
"id": 1,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -156,13 +155,12 @@
"id": 4,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -235,13 +233,12 @@
"id": 3,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -277,85 +274,6 @@
"title": "Center and bar glow",
"type": "radialbar"
},
{
"datasource": {
"type": "grafana-testdata-datasource"
},
"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,
"gradient": false,
"rounded": true,
"spotlight": true
},
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"segmentCount": 1,
"segmentSpacing": 0.3,
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
{
"alias": "1",
"datasource": {
"type": "grafana-testdata-datasource"
},
"max": 100,
"min": 1,
"noise": 22,
"refId": "A",
"scenarioId": "random_walk",
"spread": 22,
"startValue": 1
}
],
"title": "Spotlight",
"type": "radialbar"
},
{
"datasource": {
"type": "grafana-testdata-datasource"
@@ -393,13 +311,12 @@
"id": 8,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -472,13 +389,12 @@
"id": 22,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -551,13 +467,12 @@
"id": 23,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -643,13 +558,12 @@
"id": 18,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.1,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -722,13 +636,12 @@
"id": 19,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.32,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -801,13 +714,12 @@
"id": 20,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.57,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -880,13 +792,12 @@
"id": 21,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.8,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -976,13 +887,12 @@
"id": 25,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1055,13 +965,12 @@
"id": 26,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1134,13 +1043,12 @@
"id": 29,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1213,13 +1121,12 @@
"id": 30,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1292,13 +1199,12 @@
"id": 28,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1388,13 +1294,12 @@
"id": 32,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1471,13 +1376,12 @@
"id": 34,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1554,13 +1458,12 @@
"id": 33,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1645,15 +1548,15 @@
"id": 9,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"endpointMarker": "glow",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1668,8 +1571,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1731,14 +1633,13 @@
"id": 11,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1754,8 +1655,7 @@
"shape": "gauge",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": true,
"spotlight": true
"sparkline": true
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1831,14 +1731,13 @@
"id": 13,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1854,8 +1753,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1918,15 +1816,13 @@
"id": 14,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"sparkline": false,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1942,8 +1838,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2005,14 +1900,13 @@
"id": 15,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.84,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -2028,8 +1922,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2091,14 +1984,13 @@
"id": 16,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.66,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -2114,8 +2006,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2160,4 +2051,4 @@
"storedVersion": "v0alpha1"
}
}
}
}
@@ -73,13 +73,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -165,14 +164,13 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -188,8 +186,7 @@
"shape": "gauge",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": true,
"spotlight": true
"sparkline": true
},
"fieldConfig": {
"defaults": {
@@ -262,14 +259,13 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -285,8 +281,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -360,15 +355,13 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"sparkline": false,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -384,8 +377,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -459,14 +451,13 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.84,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -482,8 +473,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -556,14 +546,13 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.66,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -579,8 +568,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -653,13 +641,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.1,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -745,13 +732,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.32,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -837,13 +823,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.57,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -929,13 +914,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.8,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1021,13 +1005,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1113,13 +1096,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1201,13 +1183,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1293,13 +1274,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1385,13 +1365,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1477,13 +1456,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1573,13 +1551,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1661,13 +1638,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1753,13 +1729,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1849,13 +1824,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1945,13 +1919,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -2045,105 +2018,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
},
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"segmentCount": 1,
"segmentSpacing": 0.3,
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false
},
"fieldConfig": {
"defaults": {
"min": 0,
"max": 100,
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": 0,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "thresholds"
}
},
"overrides": []
}
}
}
}
},
"panel-5": {
"kind": "Panel",
"spec": {
"id": 5,
"title": "Spotlight",
"description": "",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "grafana-testdata-datasource",
"spec": {
"alias": "1",
"max": 100,
"min": 1,
"noise": 22,
"scenarioId": "random_walk",
"spread": 22,
"startValue": 1
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {
"maxDataPoints": 20
}
}
},
"vizConfig": {
"kind": "radialbar",
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -2229,13 +2109,12 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -2321,15 +2200,15 @@
"spec": {
"pluginVersion": "13.0.0-pre",
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"endpointMarker": "glow",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -2344,8 +2223,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -2429,19 +2307,6 @@
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 0,
"width": 4,
"height": 6,
"element": {
"kind": "ElementReference",
"name": "panel-5"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
@@ -2826,4 +2691,4 @@
"storedVersion": "v0alpha1"
}
}
}
}
@@ -77,13 +77,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -172,14 +171,13 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -195,8 +193,7 @@
"shape": "gauge",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": true,
"spotlight": true
"sparkline": true
},
"fieldConfig": {
"defaults": {
@@ -272,14 +269,13 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -295,8 +291,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -373,15 +368,13 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"sparkline": false,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -397,8 +390,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -475,14 +467,13 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.84,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -498,8 +489,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -575,14 +565,13 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.66,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -598,8 +587,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -675,13 +663,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.1,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -770,13 +757,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.32,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -865,13 +851,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.57,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -960,13 +945,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.8,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1055,13 +1039,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1150,13 +1133,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1241,13 +1223,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1336,13 +1317,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1431,13 +1411,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1526,13 +1505,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1625,13 +1603,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1716,13 +1693,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1811,13 +1787,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1910,13 +1885,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -2009,13 +1983,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -2112,108 +2085,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
},
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"segmentCount": 1,
"segmentSpacing": 0.3,
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false
},
"fieldConfig": {
"defaults": {
"min": 0,
"max": 100,
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": 0,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "thresholds"
}
},
"overrides": []
}
}
}
}
},
"panel-5": {
"kind": "Panel",
"spec": {
"id": 5,
"title": "Spotlight",
"description": "",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "grafana-testdata-datasource",
"version": "v0",
"spec": {
"alias": "1",
"max": 100,
"min": 1,
"noise": 22,
"scenarioId": "random_walk",
"spread": 22,
"startValue": 1
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {
"maxDataPoints": 20
}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "radialbar",
"version": "13.0.0-pre",
"spec": {
"options": {
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -2302,13 +2179,12 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -2397,15 +2273,15 @@
"version": "13.0.0-pre",
"spec": {
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"endpointMarker": "glow",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -2420,8 +2296,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"fieldConfig": {
"defaults": {
@@ -2505,19 +2380,6 @@
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 0,
"width": 4,
"height": 6,
"element": {
"kind": "ElementReference",
"name": "panel-5"
}
}
},
{
"kind": "GridLayoutItem",
"spec": {
@@ -2902,4 +2764,4 @@
"storedVersion": "v0alpha1"
}
}
}
}
@@ -961,9 +961,7 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1175,4 +1173,4 @@
"storedVersion": "v0alpha1"
}
}
}
}
@@ -864,9 +864,7 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1620,4 +1618,4 @@
"storedVersion": "v0alpha1"
}
}
}
}
@@ -901,9 +901,7 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1672,4 +1670,4 @@
"storedVersion": "v0alpha1"
}
}
}
}
@@ -0,0 +1,172 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v0alpha1",
"metadata": {
"name": "groupby-test"
},
"spec": {
"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,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": 0
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"id": 2,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "12.4.0-pre",
"targets": [
{
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"editorMode": "code",
"expr": "sum(counters_requests)",
"legendFormat": "__auto",
"range": true,
"refId": "A"
}
],
"title": "works with group by var",
"type": "timeseries"
}
],
"preload": false,
"schemaVersion": 42,
"tags": [],
"templating": {
"list": [
{
"current": {
"text": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
],
"value": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
]
},
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"name": "Group by",
"type": "groupby"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "groupby test",
"weekStart": ""
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -0,0 +1,229 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v2alpha1",
"metadata": {
"name": "groupby-test"
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"query": {
"kind": "grafana",
"spec": {}
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"builtIn": true,
"legacyOptions": {
"type": "dashboard"
}
}
}
],
"cursorSync": "Off",
"editable": true,
"elements": {
"panel-2": {
"kind": "Panel",
"spec": {
"id": 2,
"title": "works with group by var",
"description": "",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "prometheus",
"spec": {
"editorMode": "code",
"expr": "sum(counters_requests)",
"legendFormat": "__auto",
"range": true
}
},
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "timeseries",
"spec": {
"pluginVersion": "12.4.0-pre",
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": 0,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
}
},
"overrides": []
}
}
}
}
}
},
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-2"
}
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [],
"timeSettings": {
"timezone": "browser",
"from": "now-6h",
"to": "now",
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"hideTimepicker": false,
"fiscalYearStartMonth": 0
},
"title": "groupby test",
"variables": [
{
"kind": "GroupByVariable",
"spec": {
"name": "Group by",
"datasource": {
"type": "prometheus",
"uid": "test-uid"
},
"current": {
"text": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
],
"value": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
]
},
"options": [],
"multi": true,
"hide": "dontHide",
"skipUrlSync": false
}
}
]
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -0,0 +1,232 @@
{
"kind": "DashboardWithAccessInfo",
"apiVersion": "dashboard.grafana.app/v2beta1",
"metadata": {
"name": "groupby-test"
},
"spec": {
"annotations": [
{
"kind": "AnnotationQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "grafana",
"version": "v0",
"datasource": {
"name": "-- Grafana --"
},
"spec": {}
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations \u0026 Alerts",
"builtIn": true,
"legacyOptions": {
"type": "dashboard"
}
}
}
],
"cursorSync": "Off",
"editable": true,
"elements": {
"panel-2": {
"kind": "Panel",
"spec": {
"id": 2,
"title": "works with group by var",
"description": "",
"links": [],
"data": {
"kind": "QueryGroup",
"spec": {
"queries": [
{
"kind": "PanelQuery",
"spec": {
"query": {
"kind": "DataQuery",
"group": "prometheus",
"version": "v0",
"datasource": {
"name": "test-uid"
},
"spec": {
"editorMode": "code",
"expr": "sum(counters_requests)",
"legendFormat": "__auto",
"range": true
}
},
"refId": "A",
"hidden": false
}
}
],
"transformations": [],
"queryOptions": {}
}
},
"vizConfig": {
"kind": "VizConfig",
"group": "timeseries",
"version": "12.4.0-pre",
"spec": {
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"tooltip": {
"hideZeros": false,
"mode": "single",
"sort": "none"
}
},
"fieldConfig": {
"defaults": {
"thresholds": {
"mode": "absolute",
"steps": [
{
"value": 0,
"color": "green"
},
{
"value": 80,
"color": "red"
}
]
},
"color": {
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"barWidthFactor": 0.6,
"drawStyle": "line",
"fillOpacity": 0,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"insertNulls": false,
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "auto",
"showValues": false,
"spanNulls": false,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
}
},
"overrides": []
}
}
}
}
}
},
"layout": {
"kind": "GridLayout",
"spec": {
"items": [
{
"kind": "GridLayoutItem",
"spec": {
"x": 12,
"y": 0,
"width": 12,
"height": 8,
"element": {
"kind": "ElementReference",
"name": "panel-2"
}
}
}
]
}
},
"links": [],
"liveNow": false,
"preload": false,
"tags": [],
"timeSettings": {
"timezone": "browser",
"from": "now-6h",
"to": "now",
"autoRefresh": "",
"autoRefreshIntervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"hideTimepicker": false,
"fiscalYearStartMonth": 0
},
"title": "groupby test",
"variables": [
{
"kind": "GroupByVariable",
"group": "prometheus",
"datasource": {
"name": "test-uid"
},
"spec": {
"name": "Group by",
"current": {
"text": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
],
"value": [
"a_legacy_label",
"app",
"exported_instance",
"exported_job"
]
},
"options": [],
"multi": true,
"hide": "dontHide",
"skipUrlSync": false
}
}
]
},
"status": {
"conversion": {
"failed": false,
"storedVersion": "v1beta1"
}
}
}
@@ -1734,7 +1734,9 @@ func buildGroupByVariable(ctx context.Context, varMap map[string]interface{}, co
Hide: commonProps.Hide,
SkipUrlSync: commonProps.SkipUrlSync,
Current: buildVariableCurrent(varMap["current"]),
Multi: getBoolField(varMap, "multi", false),
// We set it to true by default because GroupByVariable
// constructor defaults to multi: true
Multi: getBoolField(varMap, "multi", true),
},
}
@@ -75,10 +75,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -154,10 +153,9 @@
"effects": {
"barGlow": false,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -233,10 +231,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -305,85 +302,6 @@
"x": 12,
"y": 1
},
"id": 5,
"maxDataPoints": 20,
"options": {
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
},
"orientation": "auto",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"segmentCount": 1,
"segmentSpacing": 0.3,
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
{
"alias": "1",
"datasource": {
"type": "grafana-testdata-datasource"
},
"max": 100,
"min": 1,
"noise": 22,
"refId": "A",
"scenarioId": "random_walk",
"spread": 22,
"startValue": 1
}
],
"title": "Spotlight",
"type": "radialbar"
},
{
"datasource": {
"type": "grafana-testdata-datasource"
},
"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": {
@@ -391,10 +309,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -460,8 +377,8 @@
"gridPos": {
"h": 6,
"w": 4,
"x": 0,
"y": 7
"x": 16,
"y": 1
},
"id": 22,
"maxDataPoints": 20,
@@ -470,10 +387,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -539,8 +455,8 @@
"gridPos": {
"h": 6,
"w": 4,
"x": 4,
"y": 7
"x": 20,
"y": 1
},
"id": 23,
"maxDataPoints": 20,
@@ -549,10 +465,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -593,7 +508,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 13
"y": 7
},
"id": 17,
"panels": [],
@@ -630,9 +545,9 @@
},
"gridPos": {
"h": 6,
"w": 5,
"w": 4,
"x": 0,
"y": 14
"y": 8
},
"id": 18,
"maxDataPoints": 20,
@@ -641,10 +556,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -709,9 +623,9 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 5,
"y": 14
"w": 4,
"x": 4,
"y": 8
},
"id": 19,
"maxDataPoints": 20,
@@ -720,10 +634,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -788,9 +701,9 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 10,
"y": 14
"w": 4,
"x": 8,
"y": 8
},
"id": 20,
"maxDataPoints": 20,
@@ -799,10 +712,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -867,9 +779,9 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 15,
"y": 14
"w": 4,
"x": 12,
"y": 8
},
"id": 21,
"maxDataPoints": 20,
@@ -878,10 +790,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"barShape": "rounded",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -922,7 +833,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 20
"y": 14
},
"id": 24,
"panels": [],
@@ -963,9 +874,9 @@
},
"gridPos": {
"h": 6,
"w": 6,
"w": 4,
"x": 0,
"y": 21
"y": 15
},
"id": 25,
"maxDataPoints": 20,
@@ -974,10 +885,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1042,9 +952,9 @@
},
"gridPos": {
"h": 6,
"w": 6,
"x": 6,
"y": 21
"w": 4,
"x": 4,
"y": 15
},
"id": 26,
"maxDataPoints": 20,
@@ -1053,10 +963,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1121,9 +1030,9 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 12,
"y": 21
"w": 4,
"x": 8,
"y": 15
},
"id": 29,
"maxDataPoints": 20,
@@ -1132,10 +1041,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1199,10 +1107,10 @@
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 0,
"y": 27
"h": 6,
"w": 4,
"x": 12,
"y": 15
},
"id": 30,
"maxDataPoints": 20,
@@ -1211,10 +1119,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1278,10 +1185,10 @@
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 6,
"y": 27
"h": 6,
"w": 4,
"x": 16,
"y": 15
},
"id": 28,
"maxDataPoints": 20,
@@ -1290,10 +1197,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1330,7 +1236,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 34
"y": 21
},
"id": 31,
"panels": [],
@@ -1377,7 +1283,7 @@
"h": 10,
"w": 7,
"x": 0,
"y": 35
"y": 22
},
"id": 32,
"maxDataPoints": 20,
@@ -1386,10 +1292,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1460,7 +1365,7 @@
"h": 10,
"w": 7,
"x": 7,
"y": 35
"y": 22
},
"id": 34,
"maxDataPoints": 20,
@@ -1469,10 +1374,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1543,7 +1447,7 @@
"h": 10,
"w": 6,
"x": 14,
"y": 35
"y": 22
},
"id": 33,
"maxDataPoints": 20,
@@ -1552,10 +1456,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -1592,7 +1495,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 45
"y": 32
},
"id": 6,
"panels": [],
@@ -1633,20 +1536,20 @@
"h": 6,
"w": 24,
"x": 0,
"y": 46
"y": 33
},
"id": 9,
"maxDataPoints": 20,
"options": {
"barWidth": 12,
"barWidthFactor": 0.4,
"barShape": "rounded",
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"endpointMarker": "glow",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1661,8 +1564,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1717,7 +1619,7 @@
"h": 6,
"w": 24,
"x": 0,
"y": 52
"y": 39
},
"id": 11,
"maxDataPoints": 20,
@@ -1727,10 +1629,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1745,8 +1646,7 @@
"shape": "gauge",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": true,
"spotlight": true
"sparkline": true
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1773,7 +1673,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 58
"y": 45
},
"id": 12,
"panels": [],
@@ -1815,7 +1715,7 @@
"h": 7,
"w": 4,
"x": 0,
"y": 59
"y": 46
},
"id": 13,
"maxDataPoints": 20,
@@ -1825,10 +1725,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1843,8 +1742,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1900,7 +1798,7 @@
"h": 7,
"w": 5,
"x": 4,
"y": 59
"y": 46
},
"id": 14,
"maxDataPoints": 20,
@@ -1910,10 +1808,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1928,8 +1825,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1984,7 +1880,7 @@
"h": 7,
"w": 5,
"x": 9,
"y": 59
"y": 46
},
"id": 15,
"maxDataPoints": 20,
@@ -1994,10 +1890,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -2012,8 +1907,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2068,7 +1962,7 @@
"h": 7,
"w": 6,
"x": 14,
"y": 59
"y": 46
},
"id": 16,
"maxDataPoints": 20,
@@ -2078,10 +1972,9 @@
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"barShape": "rounded",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -2096,8 +1989,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2124,7 +2016,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 66
"y": 53
},
"id": 35,
"panels": [],
@@ -2155,10 +2047,10 @@
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"h": 5,
"w": 12,
"x": 0,
"y": 67
"y": 54
},
"id": 36,
"options": {
@@ -2166,10 +2058,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -2223,10 +2114,10 @@
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 6,
"y": 67
"h": 5,
"w": 12,
"x": 12,
"y": 54
},
"id": 37,
"options": {
@@ -2234,10 +2125,9 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"barShape": "flat",
"orientation": "auto",
"reduceOptions": {
"calcs": [
@@ -2279,4 +2169,4 @@
"title": "Panel tests - Gauge (new)",
"uid": "panel-tests-gauge-new",
"weekStart": ""
}
}
@@ -955,9 +955,7 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1162,4 +1160,4 @@
"title": "Panel tests - Old gauge to new",
"uid": "panel-tests-old-gauge-to-new",
"weekStart": ""
}
}
@@ -71,13 +71,12 @@
"id": 1,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -148,13 +147,12 @@
"id": 4,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": false,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -225,13 +223,12 @@
"id": 3,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -299,93 +296,15 @@
"x": 12,
"y": 1
},
"id": 5,
"maxDataPoints": 20,
"options": {
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
},
"orientation": "auto",
"reduceOptions": {
"calcs": ["lastNotNull"],
"fields": "",
"values": false
},
"segmentCount": 1,
"segmentSpacing": 0.3,
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
{
"alias": "1",
"datasource": {
"type": "grafana-testdata-datasource"
},
"max": 100,
"min": 1,
"noise": 22,
"refId": "A",
"scenarioId": "random_walk",
"spread": 22,
"startValue": 1
}
],
"title": "Spotlight",
"type": "radialbar"
},
{
"datasource": {
"type": "grafana-testdata-datasource"
},
"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": {
"barShape": "rounded",
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -450,19 +369,18 @@
"gridPos": {
"h": 6,
"w": 4,
"x": 0,
"y": 7
"x": 16,
"y": 1
},
"id": 22,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -527,19 +445,18 @@
"gridPos": {
"h": 6,
"w": 4,
"x": 4,
"y": 7
"x": 20,
"y": 1
},
"id": 23,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": false,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -579,7 +496,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 13
"y": 7
},
"id": 17,
"panels": [],
@@ -616,20 +533,19 @@
},
"gridPos": {
"h": 6,
"w": 5,
"w": 4,
"x": 0,
"y": 14
"y": 8
},
"id": 18,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.1,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -693,20 +609,19 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 5,
"y": 14
"w": 4,
"x": 4,
"y": 8
},
"id": 19,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.32,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -770,20 +685,19 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 10,
"y": 14
"w": 4,
"x": 8,
"y": 8
},
"id": 20,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.57,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -847,20 +761,19 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 15,
"y": 14
"w": 4,
"x": 12,
"y": 8
},
"id": 21,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidthFactor": 0.8,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": false,
"rounded": true,
"spotlight": true
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -900,7 +813,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 20
"y": 14
},
"id": 24,
"panels": [],
@@ -941,20 +854,19 @@
},
"gridPos": {
"h": 6,
"w": 6,
"w": 4,
"x": 0,
"y": 21
"y": 15
},
"id": 25,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1018,20 +930,19 @@
},
"gridPos": {
"h": 6,
"w": 6,
"x": 6,
"y": 21
"w": 4,
"x": 4,
"y": 15
},
"id": 26,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1095,20 +1006,19 @@
},
"gridPos": {
"h": 6,
"w": 5,
"x": 12,
"y": 21
"w": 4,
"x": 8,
"y": 15
},
"id": 29,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1171,21 +1081,20 @@
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 0,
"y": 27
"h": 6,
"w": 4,
"x": 12,
"y": 15
},
"id": 30,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1248,21 +1157,20 @@
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 6,
"y": 27
"h": 6,
"w": 4,
"x": 16,
"y": 15
},
"id": 28,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.72,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": false,
"rounded": false,
"spotlight": false
"gradient": false
},
"orientation": "auto",
"reduceOptions": {
@@ -1298,7 +1206,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 34
"y": 21
},
"id": 31,
"panels": [],
@@ -1345,18 +1253,17 @@
"h": 10,
"w": 7,
"x": 0,
"y": 35
"y": 22
},
"id": 32,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1426,18 +1333,17 @@
"h": 10,
"w": 7,
"x": 7,
"y": 35
"y": 22
},
"id": 34,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1507,18 +1413,17 @@
"h": 10,
"w": 6,
"x": 14,
"y": 35
"y": 22
},
"id": 33,
"maxDataPoints": 20,
"options": {
"barShape": "flat",
"barWidthFactor": 0.9,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -1554,7 +1459,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 45
"y": 32
},
"id": 6,
"panels": [],
@@ -1595,20 +1500,20 @@
"h": 6,
"w": 24,
"x": 0,
"y": 46
"y": 33
},
"id": 9,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"endpointMarker": "glow",
"glow": "both",
"orientation": "auto",
"reduceOptions": {
@@ -1621,8 +1526,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1677,19 +1581,18 @@
"h": 6,
"w": 24,
"x": 0,
"y": 52
"y": 39
},
"id": 11,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.4,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1703,8 +1606,7 @@
"shape": "gauge",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": true,
"spotlight": true
"sparkline": true
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1731,7 +1633,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 58
"y": 45
},
"id": 12,
"panels": [],
@@ -1773,19 +1675,18 @@
"h": 7,
"w": 4,
"x": 0,
"y": 59
"y": 46
},
"id": 13,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1799,8 +1700,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1856,19 +1756,18 @@
"h": 7,
"w": 5,
"x": 4,
"y": 59
"y": 46
},
"id": 14,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.49,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1882,8 +1781,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -1938,19 +1836,18 @@
"h": 7,
"w": 5,
"x": 9,
"y": 59
"y": 46
},
"id": 15,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.84,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -1964,8 +1861,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2020,19 +1916,18 @@
"h": 7,
"w": 6,
"x": 14,
"y": 59
"y": 46
},
"id": 16,
"maxDataPoints": 20,
"options": {
"barShape": "rounded",
"barWidth": 12,
"barWidthFactor": 0.66,
"effects": {
"barGlow": true,
"centerGlow": true,
"gradient": true,
"rounded": true,
"spotlight": true
"gradient": true
},
"glow": "both",
"orientation": "auto",
@@ -2046,8 +1941,7 @@
"shape": "circle",
"showThresholdLabels": false,
"showThresholdMarkers": false,
"sparkline": false,
"spotlight": true
"sparkline": false
},
"pluginVersion": "13.0.0-pre",
"targets": [
@@ -2074,7 +1968,7 @@
"h": 1,
"w": 24,
"x": 0,
"y": 66
"y": 53
},
"id": 35,
"panels": [],
@@ -2105,20 +1999,19 @@
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"h": 5,
"w": 12,
"x": 0,
"y": 67
"y": 54
},
"id": 36,
"options": {
"barShape": "flat",
"barWidthFactor": 0.5,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -2171,20 +2064,19 @@
"overrides": []
},
"gridPos": {
"h": 8,
"w": 6,
"x": 6,
"y": 67
"h": 5,
"w": 12,
"x": 12,
"y": 54
},
"id": 37,
"options": {
"barShape": "flat",
"barWidthFactor": 0.5,
"effects": {
"barGlow": false,
"centerGlow": false,
"gradient": true,
"rounded": false,
"spotlight": false
"gradient": true
},
"orientation": "auto",
"reduceOptions": {
@@ -2224,5 +2116,6 @@
"timezone": "browser",
"title": "Panel tests - Gauge (new)",
"uid": "panel-tests-gauge-new",
"version": 9
"version": 22,
"weekStart": ""
}
@@ -956,8 +956,6 @@
"effects": {
"barGlow": false,
"centerGlow": false,
"rounded": false,
"spotlight": false,
"gradient": false
}
}
@@ -16,21 +16,19 @@ export interface GaugePanelEffects {
barGlow?: boolean;
centerGlow?: boolean;
gradient?: boolean;
rounded?: boolean;
spotlight?: boolean;
}
export const defaultGaugePanelEffects: Partial<GaugePanelEffects> = {
barGlow: false,
centerGlow: false,
gradient: true,
rounded: false,
spotlight: false,
};
export interface Options extends common.SingleStatBaseOptions {
barShape: ('flat' | 'rounded');
barWidthFactor: number;
effects: GaugePanelEffects;
endpointMarker?: ('point' | 'glow' | 'none');
segmentCount: number;
segmentSpacing: number;
shape: ('circle' | 'gauge');
@@ -40,8 +38,10 @@ export interface Options extends common.SingleStatBaseOptions {
}
export const defaultOptions: Partial<Options> = {
barShape: 'flat',
barWidthFactor: 0.5,
effects: {},
endpointMarker: 'point',
segmentCount: 1,
segmentSpacing: 0.3,
shape: 'gauge',
@@ -1,52 +1,149 @@
import { GaugeDimensions, toRad } from './utils';
import { useId, memo, HTMLAttributes, ReactNode } from 'react';
export interface RadialArcPathProps {
startAngle: number;
dimensions: GaugeDimensions;
color: string;
glowFilter?: string;
import { FieldDisplay } from '@grafana/data';
import { getBarEndcapColors, getGradientCss, getEndpointMarkerColors } from './colors';
import { RadialShape, RadialGaugeDimensions, GradientStop } from './types';
import { drawRadialArcPath, toRad } from './utils';
export interface RadialArcPathPropsBase {
arcLengthDeg: number;
barEndcaps?: boolean;
dimensions: RadialGaugeDimensions;
fieldDisplay: FieldDisplay;
roundedBars?: boolean;
shape: RadialShape;
endpointMarker?: 'point' | 'glow';
startAngle: number;
glowFilter?: string;
endpointMarkerGlowFilter?: string;
}
export function RadialArcPath({
startAngle: angle,
dimensions,
color,
glowFilter,
arcLengthDeg,
roundedBars,
}: RadialArcPathProps) {
const { radius, centerX, centerY, barWidth } = dimensions;
interface RadialArcPathPropsWithColor extends RadialArcPathPropsBase {
color: string;
}
if (arcLengthDeg === 360) {
// For some reason a 100% full arc cannot be rendered
arcLengthDeg = 359.99;
interface RadialArcPathPropsWithGradient extends RadialArcPathPropsBase {
gradient: GradientStop[];
}
type RadialArcPathProps = RadialArcPathPropsWithColor | RadialArcPathPropsWithGradient;
const ENDPOINT_MARKER_MIN_ANGLE = 10;
const DOT_OPACITY = 0.5;
const DOT_RADIUS_FACTOR = 0.4;
const MAX_DOT_RADIUS = 8;
export const RadialArcPath = memo(
({
arcLengthDeg,
dimensions,
fieldDisplay,
roundedBars,
shape,
endpointMarker,
barEndcaps,
startAngle: angle,
glowFilter,
endpointMarkerGlowFilter,
...rest
}: RadialArcPathProps) => {
const id = useId();
const bgDivStyle: HTMLAttributes<HTMLDivElement>['style'] = { width: '100%', height: '100%' };
if ('color' in rest) {
bgDivStyle.backgroundColor = rest.color;
} else {
bgDivStyle.backgroundImage = getGradientCss(rest.gradient, shape);
}
const { radius, centerX, centerY, barWidth } = dimensions;
const path = drawRadialArcPath(angle, arcLengthDeg, dimensions, roundedBars);
const startRadians = toRad(angle);
const endRadians = toRad(angle + arcLengthDeg);
const xStart = centerX + radius * Math.cos(startRadians);
const yStart = centerY + radius * Math.sin(startRadians);
const xEnd = centerX + radius * Math.cos(endRadians);
const yEnd = centerY + radius * Math.sin(endRadians);
const dotRadius =
endpointMarker === 'point' ? Math.min((barWidth / 2) * DOT_RADIUS_FACTOR, MAX_DOT_RADIUS) : barWidth / 2;
let barEndcapColors: [string, string] | undefined;
let endpointMarks: ReactNode = null;
if ('gradient' in rest) {
if (endpointMarker && (rest.gradient?.length ?? 0) > 0) {
switch (endpointMarker) {
case 'point':
const [pointColorStart, pointColorEnd] = getEndpointMarkerColors(
rest.gradient!,
fieldDisplay.display.percent
);
endpointMarks = (
<>
{arcLengthDeg > ENDPOINT_MARKER_MIN_ANGLE && (
<circle cx={xStart} cy={yStart} r={dotRadius} fill={pointColorStart} opacity={DOT_OPACITY} />
)}
<circle cx={xEnd} cy={yEnd} r={dotRadius} fill={pointColorEnd} opacity={DOT_OPACITY} />
</>
);
break;
case 'glow':
const offsetAngle = toRad(ENDPOINT_MARKER_MIN_ANGLE);
const xStartMark = centerX + radius * Math.cos(endRadians + offsetAngle);
const yStartMark = centerY + radius * Math.sin(endRadians + offsetAngle);
endpointMarks =
arcLengthDeg > ENDPOINT_MARKER_MIN_ANGLE ? (
<path
d={['M', xStartMark, yStartMark, 'A', radius, radius, 0, 0, 1, xEnd, yEnd].join(' ')}
fill="none"
strokeWidth={barWidth}
stroke={endpointMarkerGlowFilter}
strokeLinecap={roundedBars ? 'round' : 'butt'}
filter={glowFilter}
/>
) : null;
break;
default:
break;
}
}
if (barEndcaps) {
barEndcapColors = getBarEndcapColors(rest.gradient, fieldDisplay.display.percent);
}
}
return (
<>
{/* FIXME: optimize this by only using clippath + foreign obj for gradients */}
<clipPath id={id}>
<path d={path} />
</clipPath>
<g filter={glowFilter}>
<foreignObject
x={centerX - radius - barWidth}
y={centerY - radius - barWidth}
width={(radius + barWidth) * 2}
height={(radius + barWidth) * 2}
clipPath={`url(#${id})`}
>
<div style={bgDivStyle} />
</foreignObject>
{barEndcapColors?.[0] && <circle cx={xStart} cy={yStart} r={barWidth / 2} fill={barEndcapColors[0]} />}
{barEndcapColors?.[1] && (
<circle cx={xEnd} cy={yEnd} r={barWidth / 2} fill={barEndcapColors[1]} opacity={0.5} />
)}
</g>
{endpointMarks}
</>
);
}
);
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 (
<path
d={path}
fill="none"
fillOpacity="1"
stroke={color}
strokeOpacity="1"
strokeWidth={barWidth}
filter={glowFilter}
strokeLinecap={roundedBars ? 'round' : 'butt'}
className="radial-arc-path"
/>
);
}
RadialArcPath.displayName = 'RadialArcPath';
@@ -1,97 +1,64 @@
import { GrafanaTheme2 } from '@grafana/data';
import { FALLBACK_COLOR, FieldDisplay } from '@grafana/data';
import { useTheme2 } from '../../themes/ThemeContext';
import { RadialArcPath } from './RadialArcPath';
import { RadialColorDefs } from './RadialColorDefs';
import { GaugeDimensions, toRad } from './utils';
import { RadialShape, RadialGaugeDimensions, GradientStop } from './types';
export interface RadialBarProps {
dimensions: GaugeDimensions;
colorDefs: RadialColorDefs;
angleRange: number;
angle: number;
startAngle: number;
angleRange: number;
dimensions: RadialGaugeDimensions;
fieldDisplay: FieldDisplay;
gradient?: GradientStop[];
roundedBars?: boolean;
spotlightStroke: string;
endpointMarker?: 'point' | 'glow';
shape: RadialShape;
startAngle: number;
glowFilter?: string;
endpointMarkerGlowFilter?: string;
}
export function RadialBar({
dimensions,
colorDefs,
angleRange,
angle,
startAngle,
angleRange,
dimensions,
fieldDisplay,
gradient,
roundedBars,
spotlightStroke,
endpointMarker,
shape,
startAngle,
glowFilter,
endpointMarkerGlowFilter,
}: RadialBarProps) {
const theme = useTheme2();
const colorProps = gradient ? { gradient } : { color: fieldDisplay.display.color ?? FALLBACK_COLOR };
return (
<>
<g>
{/** Track */}
<RadialArcPath
startAngle={startAngle + angle}
dimensions={dimensions}
arcLengthDeg={angleRange - angle}
color={theme.colors.action.hover}
roundedBars={roundedBars}
/>
{/** The colored bar */}
<RadialArcPath
dimensions={dimensions}
startAngle={startAngle}
arcLengthDeg={angle}
color={colorDefs.getMainBarColor()}
roundedBars={roundedBars}
glowFilter={glowFilter}
/>
{spotlightStroke && angle > 8 && (
<SpotlightSquareEffect
dimensions={dimensions}
angle={startAngle + angle}
glowFilter={glowFilter}
spotlightStroke={spotlightStroke}
theme={theme}
roundedBars={roundedBars}
/>
)}
</g>
<defs>{colorDefs.getDefs()}</defs>
{/** Track */}
<RadialArcPath
arcLengthDeg={angleRange - angle}
fieldDisplay={fieldDisplay}
color={theme.colors.action.hover}
dimensions={dimensions}
roundedBars={roundedBars}
shape={shape}
startAngle={startAngle + angle}
/>
{/** The colored bar */}
<RadialArcPath
arcLengthDeg={angle}
barEndcaps={shape === 'circle' && roundedBars}
dimensions={dimensions}
endpointMarker={roundedBars ? endpointMarker : undefined}
endpointMarkerGlowFilter={endpointMarkerGlowFilter}
fieldDisplay={fieldDisplay}
glowFilter={glowFilter}
roundedBars={roundedBars}
shape={shape}
startAngle={startAngle}
{...colorProps}
/>
</>
);
}
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 (
<path
d={path}
fill="none"
strokeWidth={barWidth}
stroke={spotlightStroke}
strokeLinecap={roundedBars ? 'round' : 'butt'}
filter={glowFilter}
/>
);
}
@@ -1,126 +1,74 @@
import { FieldDisplay } from '@grafana/data';
import { memo } from 'react';
import { FALLBACK_COLOR, FieldDisplay } from '@grafana/data';
import { useTheme2 } from '../../themes/ThemeContext';
import { RadialArcPath } from './RadialArcPath';
import { RadialColorDefs } from './RadialColorDefs';
import { GaugeDimensions } from './utils';
import { RadialShape, RadialGaugeDimensions, GradientStop } from './types';
import {
getAngleBetweenSegments,
getFieldConfigMinMax,
getFieldDisplayProcessor,
getOptimalSegmentCount,
} from './utils';
export interface RadialBarSegmentedProps {
fieldDisplay: FieldDisplay;
dimensions: GaugeDimensions;
colorDefs: RadialColorDefs;
dimensions: RadialGaugeDimensions;
angleRange: number;
startAngle: number;
glowFilter?: string;
segmentCount: number;
segmentSpacing: number;
shape: RadialShape;
gradient?: GradientStop[];
}
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;
export const RadialBarSegmented = memo(
({
fieldDisplay,
dimensions,
startAngle,
angleRange,
glowFilter,
gradient,
segmentCount,
segmentSpacing,
shape,
}: RadialBarSegmentedProps) => {
const theme = useTheme2();
const segments: React.ReactNode[] = [];
const segmentCountAdjusted = getOptimalSegmentCount(dimensions, segmentSpacing, segmentCount, angleRange);
const [min, max] = getFieldConfigMinMax(fieldDisplay);
const value = fieldDisplay.display.numeric;
const angleBetweenSegments = getAngleBetweenSegments(segmentSpacing, segmentCount, angleRange);
const segmentArcLengthDeg = angleRange / segmentCountAdjusted - angleBetweenSegments;
const displayProcessor = getFieldDisplayProcessor(fieldDisplay);
for (let i = 0; i < segmentCountAdjusted; i++) {
const angleValue = min + ((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;
for (let i = 0; i < segmentCountAdjusted; i++) {
const angleValue = min + ((max - min) / segmentCountAdjusted) * i;
const segmentAngle = startAngle + (angleRange / segmentCountAdjusted) * i + 0.01;
const segmentColor =
angleValue >= value ? theme.colors.border.medium : (displayProcessor(angleValue).color ?? FALLBACK_COLOR);
const colorProps = angleValue < value && gradient ? { gradient } : { color: segmentColor };
segments.push(
<RadialArcPath
key={i}
startAngle={segmentAngle}
dimensions={dimensions}
color={segmentColor}
glowFilter={glowFilter}
arcLengthDeg={segmentArcLengthDeg}
/>
);
segments.push(
<RadialArcPath
key={i}
arcLengthDeg={segmentArcLengthDeg}
dimensions={dimensions}
fieldDisplay={fieldDisplay}
glowFilter={glowFilter}
shape={shape}
startAngle={segmentAngle}
{...colorProps}
/>
);
}
return <g>{segments}</g>;
}
);
return (
<>
<g>{segments}</g>
<defs>{colorDefs.getDefs()}</defs>
</>
);
}
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 / 1.5 / 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 (
// <line
// x1={x1}
// y1={y1}
// x2={x2}
// y2={y2}
// fill="none"
// fillOpacity="0.85"
// stroke={color}
// strokeOpacity="1"
// strokeLinecap={roundedBars ? 'round' : 'butt'}
// strokeWidth={segmentWidth}
// strokeDasharray="0"
// filter={glow ? `url(#glow-${gaugeId})` : undefined}
// />
// );
// }
RadialBarSegmented.displayName = 'RadialBarSegmented';
@@ -1,141 +0,0 @@
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<string, string> = {};
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(
<linearGradient x1="0" y1="0" x2={1 / valuePercent} y2="0" id={id}>
{colors.map((stopColor, i) => (
<stop key={i} offset={`${(i / (count - 1)).toFixed(2)}`} stopColor={stopColor} stopOpacity={1} />
))}
</linearGradient>
);
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(
<radialGradient
key={id}
id={id}
cx={dimensions.centerX}
cy={dimensions.centerY}
r={dimensions.radius + dimensions.barWidth / 2}
fr={dimensions.radius - dimensions.barWidth / 2}
gradientUnits="userSpaceOnUse"
>
<stop offset="0%" stopColor={tinycolor(baseColor).spin(20).lighten(10).toString()} stopOpacity={1} />
<stop offset="60%" stopColor={color1.toString()} stopOpacity={1} />
<stop offset="100%" stopColor={color1.toString()} stopOpacity={1} />
</radialGradient>
);
return returnColor;
}
// 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(
<linearGradient
key={id}
id={id}
x1="0"
y1="0"
x2={x2}
y2={y2}
gradientUnits="userSpaceOnUse"
gradientTransform={transform}
>
{theme.isDark ? (
<>
<stop offset="0%" stopColor={color1.darken(10).toString()} stopOpacity={1} />
<stop offset="100%" stopColor={color2.lighten(10).toString()} stopOpacity={1} />
</>
) : (
<>
<stop offset="0%" stopColor={color2.lighten(10).toString()} stopOpacity={1} />
<stop offset="100%" stopColor={color1.toString()} stopOpacity={1} />
</>
)}
</linearGradient>
);
return returnColor;
}
getMainBarColor(): string {
return this.getColor(this.options.fieldDisplay.display.color ?? FALLBACK_COLOR);
}
getDefs(): React.ReactNode[] {
return this.defs;
}
}
@@ -13,7 +13,8 @@ import { FieldColorModeId } from '@grafana/schema';
import { useTheme2 } from '../../themes/ThemeContext';
import { Stack } from '../Layout/Stack/Stack';
import { RadialGauge, RadialGaugeProps, RadialGradientMode, RadialShape, RadialTextMode } from './RadialGauge';
import { RadialGauge, RadialGaugeProps } from './RadialGauge';
import { RadialShape, RadialTextMode } from './types';
interface StoryProps extends RadialGaugeProps {
value: number;
@@ -31,10 +32,27 @@ const meta: Meta<StoryProps> = {
controls: {
exclude: ['theme', 'values', 'vizCount'],
},
a11y: {
config: {
rules: [
{
id: 'scrollable-region-focusable',
selector: 'body',
enabled: false,
},
// NOTE: this is necessary due to a false positive with the filered svg glow in one of the examples.
// The color-contrast in this component should be accessible!
{
id: 'color-contrast',
selector: 'text',
enabled: false,
},
],
},
},
},
args: {
barWidthFactor: 0.2,
spotlight: false,
glowBar: false,
glowCenter: false,
sparkline: false,
@@ -42,7 +60,7 @@ const meta: Meta<StoryProps> = {
width: 200,
height: 200,
shape: 'circle',
gradient: 'none',
gradient: false,
seriesCount: 1,
segmentCount: 0,
segmentSpacing: 0.2,
@@ -56,14 +74,14 @@ const meta: Meta<StoryProps> = {
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' } },
gradient: { control: { type: 'boolean' } },
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 } },
endpointMarker: { control: { type: 'select' }, options: ['none', 'point', 'glow'] },
colorScheme: {
control: { type: 'select' },
options: [
@@ -102,57 +120,17 @@ export const Examples: StoryFn<StoryProps> = (args) => {
<Stack direction={'column'} gap={3} wrap="wrap">
<div>Bar width</div>
<Stack direction="row" alignItems="center" gap={3} wrap="wrap">
<RadialGaugeExample
seriesName="0.1"
value={args.value ?? 30}
color="blue"
gradient="auto"
barWidthFactor={0.1}
/>
<RadialGaugeExample
seriesName="0.4"
value={args.value ?? 40}
color="green"
gradient="auto"
barWidthFactor={0.4}
/>
<RadialGaugeExample
seriesName="0.6"
value={args.value ?? 60}
color="red"
gradient="auto"
barWidthFactor={0.6}
/>
<RadialGaugeExample
seriesName="0.8"
value={args.value ?? 70}
color="purple"
gradient="auto"
barWidthFactor={0.8}
/>
<RadialGaugeExample seriesName="0.1" value={args.value ?? 30} color="blue" gradient barWidthFactor={0.1} />
<RadialGaugeExample seriesName="0.4" value={args.value ?? 40} color="green" gradient barWidthFactor={0.4} />
<RadialGaugeExample seriesName="0.6" value={args.value ?? 60} color="red" gradient barWidthFactor={0.6} />
<RadialGaugeExample seriesName="0.8" value={args.value ?? 70} color="purple" gradient barWidthFactor={0.8} />
</Stack>
<div>Effects</div>
<Stack direction="row" alignItems="center" gap={3} wrap="wrap">
<RadialGaugeExample value={args.value ?? 30} spotlight glowBar glowCenter color="blue" gradient="auto" />
<RadialGaugeExample value={args.value ?? 40} spotlight glowBar glowCenter color="green" gradient="auto" />
<RadialGaugeExample
value={args.value ?? 60}
spotlight
glowBar
glowCenter
color="red"
gradient="auto"
roundedBars
/>
<RadialGaugeExample
value={args.value ?? 70}
spotlight
glowBar
glowCenter
color="purple"
gradient="auto"
roundedBars
/>
<RadialGaugeExample value={args.value ?? 30} glowBar glowCenter color="blue" gradient />
<RadialGaugeExample value={args.value ?? 40} glowBar glowCenter color="green" gradient />
<RadialGaugeExample value={args.value ?? 60} glowBar glowCenter color="red" gradient roundedBars />
<RadialGaugeExample value={args.value ?? 70} glowBar glowCenter color="purple" gradient roundedBars />
</Stack>
<div>Shape: Gauge & color scale</div>
<Stack direction="row" alignItems="center" gap={3} wrap="wrap">
@@ -160,14 +138,14 @@ export const Examples: StoryFn<StoryProps> = (args) => {
value={40}
shape="gauge"
width={250}
gradient="auto"
gradient
colorScheme={FieldColorModeId.ContinuousGrYlRd}
glowCenter={true}
barWidthFactor={0.6}
/>
<RadialGaugeExample
colorScheme={FieldColorModeId.ContinuousGrYlRd}
gradient="auto"
gradient
width={250}
value={90}
barWidthFactor={0.6}
@@ -183,9 +161,8 @@ export const Examples: StoryFn<StoryProps> = (args) => {
value={args.value ?? 70}
color="blue"
shape="gauge"
gradient="auto"
gradient
sparkline={true}
spotlight
glowBar={true}
glowCenter={true}
barWidthFactor={0.2}
@@ -194,9 +171,8 @@ export const Examples: StoryFn<StoryProps> = (args) => {
value={args.value ?? 30}
color="green"
shape="gauge"
gradient="auto"
gradient
sparkline={true}
spotlight
glowBar={true}
glowCenter={true}
barWidthFactor={0.8}
@@ -206,9 +182,8 @@ export const Examples: StoryFn<StoryProps> = (args) => {
color="red"
shape="gauge"
width={250}
gradient="auto"
gradient
sparkline={true}
spotlight
glowBar={true}
glowCenter={true}
barWidthFactor={0.2}
@@ -218,9 +193,8 @@ export const Examples: StoryFn<StoryProps> = (args) => {
color="red"
width={250}
shape="gauge"
gradient="auto"
gradient
sparkline={true}
spotlight
glowBar={true}
glowCenter={true}
barWidthFactor={0.8}
@@ -231,7 +205,7 @@ export const Examples: StoryFn<StoryProps> = (args) => {
<RadialGaugeExample
value={args.value ?? 70}
color="green"
gradient="auto"
gradient
glowCenter={true}
segmentCount={8}
segmentSpacing={0.1}
@@ -240,7 +214,7 @@ export const Examples: StoryFn<StoryProps> = (args) => {
<RadialGaugeExample
value={args.value ?? 30}
color="purple"
gradient="auto"
gradient
segmentCount={30}
glowCenter={true}
barWidthFactor={0.6}
@@ -248,7 +222,7 @@ export const Examples: StoryFn<StoryProps> = (args) => {
<RadialGaugeExample
value={args.value ?? 50}
color="red"
gradient="auto"
gradient
segmentCount={40}
glowCenter={true}
barWidthFactor={1}
@@ -260,7 +234,6 @@ export const Examples: StoryFn<StoryProps> = (args) => {
<RadialGaugeExample
value={args.value ?? 80}
colorScheme={FieldColorModeId.ContinuousGrYlRd}
spotlight
glowBar={true}
glowCenter={true}
segmentCount={20}
@@ -270,9 +243,8 @@ export const Examples: StoryFn<StoryProps> = (args) => {
value={args.value ?? 80}
width={250}
colorScheme={FieldColorModeId.ContinuousGrYlRd}
spotlight
shape="gauge"
gradient="auto"
gradient
glowBar={true}
glowCenter={true}
segmentCount={40}
@@ -285,10 +257,9 @@ export const Examples: StoryFn<StoryProps> = (args) => {
<RadialGaugeExample
value={args.value ?? 70}
colorScheme={FieldColorModeId.Thresholds}
gradient="auto"
gradient
thresholdsBar={true}
roundedBars={false}
spotlight
glowCenter={true}
barWidthFactor={0.7}
/>
@@ -296,7 +267,7 @@ export const Examples: StoryFn<StoryProps> = (args) => {
value={args.value ?? 70}
width={250}
colorScheme={FieldColorModeId.Thresholds}
gradient="auto"
gradient
glowCenter={true}
thresholdsBar={true}
roundedBars={false}
@@ -307,7 +278,7 @@ export const Examples: StoryFn<StoryProps> = (args) => {
value={args.value ?? 70}
width={250}
colorScheme={FieldColorModeId.Thresholds}
gradient="auto"
gradient
glowCenter={true}
thresholdsBar={true}
roundedBars={false}
@@ -347,14 +318,12 @@ export const Temp: StoryFn<StoryProps> = (args) => {
shape="gauge"
roundedBars={false}
barWidthFactor={0.8}
spotlight
/>
</Stack>
);
};
interface ExampleProps {
gradient?: RadialGradientMode;
color?: string;
seriesName?: string;
value?: number;
@@ -363,7 +332,7 @@ interface ExampleProps {
max?: number;
width?: number;
height?: number;
spotlight?: boolean;
gradient?: boolean;
glowBar?: boolean;
glowCenter?: boolean;
barWidthFactor?: number;
@@ -376,12 +345,12 @@ interface ExampleProps {
roundedBars?: boolean;
thresholdsBar?: boolean;
colorScheme?: FieldColorModeId;
endpointMarker?: RadialGaugeProps['endpointMarker'];
decimals?: number;
showScaleLabels?: boolean;
}
export function RadialGaugeExample({
gradient = 'none',
color,
seriesName = 'Server A',
value = 70,
@@ -390,7 +359,7 @@ export function RadialGaugeExample({
max = 100,
width = 200,
height = 200,
spotlight = false,
gradient = false,
glowBar = false,
glowCenter = false,
barWidthFactor = 0.4,
@@ -403,6 +372,7 @@ export function RadialGaugeExample({
roundedBars = false,
thresholdsBar = false,
colorScheme = FieldColorModeId.Thresholds,
endpointMarker = 'glow',
decimals = 0,
showScaleLabels,
}: ExampleProps) {
@@ -480,7 +450,6 @@ export function RadialGaugeExample({
barWidthFactor={barWidthFactor}
gradient={gradient}
shape={shape}
spotlight={spotlight}
glowBar={glowBar}
glowCenter={glowCenter}
textMode={textMode}
@@ -490,6 +459,7 @@ export function RadialGaugeExample({
roundedBars={roundedBars}
thresholdsBar={thresholdsBar}
showScaleLabels={showScaleLabels}
endpointMarker={endpointMarker}
/>
);
}
@@ -1,13 +1,28 @@
import { render, screen } from '@testing-library/react';
import { ComponentProps } from 'react';
import { RadialGaugeExample } from './RadialGauge.story';
describe('RadialGauge', () => {
it('should render', () => {
render(<RadialGaugeExample />);
expect(screen.getByRole('img')).toBeInTheDocument();
});
it.each([
{ description: 'default', props: {} },
{ description: 'gauge shape', props: { shape: 'gauge' } },
{ description: 'with gradient', props: { gradient: true } },
{ description: 'with glow bar', props: { glowBar: true } },
{ description: 'with glow center', props: { glowCenter: true } },
{ description: 'with segments', props: { segmentCount: 5 } },
{ description: 'with rounded bars', props: { roundedBars: true } },
{ description: 'with endpoint marker glow', props: { roundedBars: true, endpointMarker: 'glow' } },
{ description: 'with endpoint marker point', props: { roundedBars: true, endpointMarker: 'point' } },
{ description: 'with thresholds bar', props: { thresholdsBar: true } },
{ description: 'with sparkline', props: { sparkline: true } },
] satisfies Array<{ description: string; props?: ComponentProps<typeof RadialGaugeExample> }>)(
'should render $description without throwing',
({ props }) => {
render(<RadialGaugeExample {...props} />);
expect(screen.getByRole('img')).toBeInTheDocument();
}
);
it('should render threshold labels', () => {
render(<RadialGaugeExample showScaleLabels={true} />);
@@ -1,14 +1,7 @@
import { css, cx } from '@emotion/css';
import { isNumber } from 'lodash';
import { useId } from 'react';
import {
DisplayValueAlignmentFactors,
FieldDisplay,
getDisplayProcessor,
GrafanaTheme2,
TimeRange,
} from '@grafana/data';
import { DisplayValueAlignmentFactors, FALLBACK_COLOR, FieldDisplay, GrafanaTheme2, TimeRange } from '@grafana/data';
import { t } from '@grafana/i18n';
import { useStyles2, useTheme2 } from '../../themes/ThemeContext';
@@ -16,12 +9,13 @@ import { getFormattedThresholds } from '../Gauge/utils';
import { RadialBar } from './RadialBar';
import { RadialBarSegmented } from './RadialBarSegmented';
import { RadialColorDefs } from './RadialColorDefs';
import { RadialScaleLabels } from './RadialScaleLabels';
import { RadialSparkline } from './RadialSparkline';
import { RadialText } from './RadialText';
import { ThresholdsBar } from './ThresholdsBar';
import { buildGradientColors } from './colors';
import { GlowGradient, MiddleCircleGlow, SpotlightGradient } from './effects';
import { RadialShape, RadialTextMode } from './types';
import { calculateDimensions, getValueAngleForValue } from './utils';
export interface RadialGaugeProps {
@@ -32,7 +26,7 @@ export interface RadialGaugeProps {
* Circle or gauge (partial circle)
*/
shape?: RadialShape;
gradient?: RadialGradientMode;
gradient?: boolean;
/**
* Bar width is always relative to size of the gauge.
* But this gives you control over the width relative to size.
@@ -40,12 +34,14 @@ export interface RadialGaugeProps {
* Defaults to 0.4
**/
barWidthFactor?: number;
/** Adds a white spotlight for the end position */
spotlight?: boolean;
glowBar?: boolean;
glowCenter?: boolean;
roundedBars?: boolean;
thresholdsBar?: boolean;
/**
* Specify if an endpoint marker should be shown at the end of the bar
*/
endpointMarker?: 'point' | 'glow';
/**
* Number of segments depends on size of gauge but this
* factor 1-10 gives you relative control
@@ -75,10 +71,6 @@ export interface RadialGaugeProps {
timeRange?: TimeRange;
}
export type RadialGradientMode = 'none' | 'auto';
export type RadialTextMode = 'auto' | 'value_and_name' | 'value' | 'name' | 'none';
export type RadialShape = 'circle' | 'gauge';
/**
* https://developers.grafana.com/ui/latest/index.html?path=/docs/plugins-radialgauge--docs
*/
@@ -87,9 +79,8 @@ export function RadialGauge(props: RadialGaugeProps) {
width = 256,
height = 256,
shape = 'circle',
gradient = 'none',
gradient = false,
barWidthFactor = 0.4,
spotlight = false,
glowBar = false,
glowCenter = false,
textMode = 'auto',
@@ -99,6 +90,7 @@ export function RadialGauge(props: RadialGaugeProps) {
roundedBars = true,
thresholdsBar = false,
showScaleLabels = false,
endpointMarker,
onClick,
values,
} = props;
@@ -121,7 +113,8 @@ export function RadialGauge(props: RadialGaugeProps) {
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 gradientStops = buildGradientColors(gradient, theme, displayValue);
const color = displayValue.display.color ?? FALLBACK_COLOR;
const dimensions = calculateDimensions(
width,
height,
@@ -134,20 +127,12 @@ export function RadialGauge(props: RadialGaugeProps) {
showScaleLabels
);
const displayProcessor = getFieldDisplayProcessor(displayValue);
// FIXME: I want to move the ids for these filters into a context which the children
// can reference via a hook, rather than passing them down as props
const spotlightGradientId = `spotlight-${barIndex}-${gaugeId}`;
const glowFilterId = `glow-${gaugeId}`;
const colorDefs = new RadialColorDefs({
gradient,
fieldDisplay: displayValue,
theme,
dimensions,
shape,
gaugeId,
displayProcessor,
});
if (spotlight && theme.isDark) {
if (endpointMarker === 'glow') {
defs.push(
<SpotlightGradient
key={spotlightGradientId}
@@ -171,7 +156,8 @@ export function RadialGauge(props: RadialGaugeProps) {
glowFilter={`url(#${glowFilterId})`}
segmentCount={segmentCount}
segmentSpacing={segmentSpacing}
colorDefs={colorDefs}
shape={shape}
gradient={gradientStops}
/>
);
} else {
@@ -179,13 +165,16 @@ export function RadialGauge(props: RadialGaugeProps) {
<RadialBar
key={`radial-bar-${barIndex}-${gaugeId}`}
dimensions={dimensions}
colorDefs={colorDefs}
angle={angle}
angleRange={angleRange}
startAngle={startAngle}
roundedBars={roundedBars}
spotlightStroke={`url(#${spotlightGradientId})`}
glowFilter={`url(#${glowFilterId})`}
endpointMarkerGlowFilter={`url(#${spotlightGradientId})`}
shape={shape}
gradient={gradientStops}
fieldDisplay={displayValue}
endpointMarker={endpointMarker}
/>
);
}
@@ -245,7 +234,8 @@ export function RadialGauge(props: RadialGaugeProps) {
angleRange={angleRange}
roundedBars={roundedBars}
glowFilter={`url(#${glowFilterId})`}
colorDefs={colorDefs}
shape={shape}
gradient={gradientStops}
/>
);
}
@@ -291,17 +281,6 @@ export function RadialGauge(props: RadialGaugeProps) {
);
}
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({
@@ -1,87 +1,84 @@
import { memo } from 'react';
import { FieldDisplay, GrafanaTheme2, Threshold } from '@grafana/data';
import { t } from '@grafana/i18n';
import { measureText } from '../../utils/measureText';
import { GaugeDimensions, toCartesian } from './utils';
import { RadialGaugeDimensions } from './types';
import { getFieldConfigMinMax, toCartesian } from './utils';
interface RadialScaleLabelsProps {
fieldDisplay: FieldDisplay;
theme: GrafanaTheme2;
thresholds: Threshold[];
dimensions: GaugeDimensions;
dimensions: RadialGaugeDimensions;
startAngle: number;
endAngle: number;
angleRange: number;
}
export function RadialScaleLabels({
fieldDisplay,
thresholds,
theme,
dimensions,
startAngle,
endAngle,
angleRange,
}: RadialScaleLabelsProps) {
const { centerX, centerY, scaleLabelsFontSize, scaleLabelsRadius } = dimensions;
const LINE_HEIGHT_FACTOR = 1.2;
const fieldConfig = fieldDisplay.field;
const min = fieldConfig.min ?? 0;
const max = fieldConfig.max ?? 100;
export const RadialScaleLabels = memo(
({ fieldDisplay, thresholds, theme, dimensions, startAngle, endAngle, angleRange }: RadialScaleLabelsProps) => {
const { centerX, centerY, scaleLabelsFontSize, scaleLabelsRadius } = dimensions;
const [min, max] = getFieldConfigMinMax(fieldDisplay);
const fontSize = scaleLabelsFontSize;
const textLineHeight = scaleLabelsFontSize * 1.2;
const radius = scaleLabelsRadius - textLineHeight;
const fontSize = scaleLabelsFontSize;
const textLineHeight = scaleLabelsFontSize * LINE_HEIGHT_FACTOR;
const radius = scaleLabelsRadius - textLineHeight;
function getTextPosition(text: string, value: number, index: number) {
const isLast = index === thresholds.length - 1;
const isFirst = index === 0;
function getTextPosition(text: string, value: number, index: number) {
const isLast = index === thresholds.length - 1;
const isFirst = index === 0;
let valueDeg = ((value - min) / (max - min)) * angleRange;
let finalAngle = startAngle + valueDeg;
let valueDeg = ((value - min) / (max - min)) * angleRange;
let finalAngle = startAngle + valueDeg;
// Now adjust the final angle based on the label text width and the labels position on the arc
let measure = measureText(text, fontSize, theme.typography.fontWeightMedium);
let textWidthAngle = (measure.width / (2 * Math.PI * radius)) * angleRange;
// Now adjust the final angle based on the label text width and the labels position on the arc
let measure = measureText(text, fontSize, theme.typography.fontWeightMedium);
let textWidthAngle = (measure.width / (2 * Math.PI * radius)) * angleRange;
// the centering is different for gauge or circle shapes for some reason
finalAngle -= endAngle < 180 ? textWidthAngle : textWidthAngle / 2;
// the centering is different for gauge or circle shapes for some reason
finalAngle -= endAngle < 180 ? textWidthAngle : textWidthAngle / 2;
// For circle gauges we need to shift the first label more
if (isFirst) {
finalAngle += textWidthAngle;
// For circle gauges we need to shift the first label more
if (isFirst) {
finalAngle += textWidthAngle;
}
// For circle gauges we need to shift the last label more
if (isLast && endAngle === 360) {
finalAngle -= textWidthAngle;
}
const position = toCartesian(centerX, centerY, radius, finalAngle);
return { ...position, transform: `rotate(${finalAngle}, ${position.x}, ${position.y})` };
}
// For circle gauges we need to shift the last label more
if (isLast && endAngle === 360) {
finalAngle -= textWidthAngle;
}
const position = toCartesian(centerX, centerY, radius, finalAngle);
return { ...position, transform: `rotate(${finalAngle}, ${position.x}, ${position.y})` };
return (
<g>
{thresholds.map((threshold, index) => {
const labelPos = getTextPosition(String(threshold.value), threshold.value, index);
return (
<text
key={index}
x={labelPos.x}
y={labelPos.y}
fontSize={fontSize}
fill={theme.colors.text.primary}
transform={labelPos.transform}
aria-label={t(`gauge.threshold`, 'Threshold {{value}}', { value: threshold.value })}
>
{threshold.value}
</text>
);
})}
</g>
);
}
);
return (
<g>
{thresholds.map((threshold, index) => {
const labelPos = getTextPosition(String(threshold.value), threshold.value, index);
return (
<text
key={index}
x={labelPos.x}
y={labelPos.y}
fontSize={fontSize}
fill={theme.colors.text.primary}
transform={labelPos.transform}
aria-label={t(`gauge.threshold`, 'Threshold {{value}}', { value: threshold.value })}
>
{threshold.value}
</text>
);
})}
</g>
);
}
RadialScaleLabels.displayName = 'RadialScaleLabels';
@@ -1,49 +1,76 @@
import { memo, useMemo } from 'react';
import { FieldDisplay, GrafanaTheme2, FieldConfig } from '@grafana/data';
import { GraphFieldConfig, GraphGradientMode, LineInterpolation } from '@grafana/schema';
import { Sparkline } from '../Sparkline/Sparkline';
import { RadialShape, RadialTextMode } from './RadialGauge';
import { GaugeDimensions } from './utils';
import { RadialShape, RadialTextMode, RadialGaugeDimensions } from './types';
interface RadialSparklineProps {
sparkline: FieldDisplay['sparkline'];
dimensions: GaugeDimensions;
theme: GrafanaTheme2;
color?: string;
shape?: RadialShape;
dimensions: RadialGaugeDimensions;
shape: RadialShape;
sparkline: FieldDisplay['sparkline'];
textMode: Exclude<RadialTextMode, 'auto'>;
theme: GrafanaTheme2;
}
export function RadialSparkline({ sparkline, dimensions, theme, color, shape, textMode }: RadialSparklineProps) {
const { radius, barWidth } = dimensions;
if (!sparkline) {
return null;
const SPARKLINE_HEIGHT_DIVISOR = 4;
const SPARKLINE_HEIGHT_DIVISOR_NAME_AND_VALUE = 4;
const SPARKLINE_WIDTH_FACTOR_ARC = 1.4;
const SPARKLINE_WIDTH_FACTOR_CIRCLE = 1.6;
const SPARKLINE_TOP_OFFSET_DIVISOR_CIRCLE = 4;
const SPARKLINE_TOP_OFFSET_DIVISOR_CIRCLE_NAME_AND_VALUE = 3.3;
const SPARKLINE_SPACING = 8;
export function getSparklineDimensions(
radius: number,
barWidth: number,
showNameAndValue: boolean,
shape: RadialShape
): { width: number; height: number } {
const height = radius / (showNameAndValue ? SPARKLINE_HEIGHT_DIVISOR_NAME_AND_VALUE : SPARKLINE_HEIGHT_DIVISOR);
const width = radius * (shape === 'gauge' ? SPARKLINE_WIDTH_FACTOR_ARC : SPARKLINE_WIDTH_FACTOR_CIRCLE) - barWidth;
return { width, height };
}
export const RadialSparkline = memo(
({ sparkline, dimensions, theme, color, shape, textMode }: RadialSparklineProps) => {
const { radius, barWidth } = dimensions;
const showNameAndValue = textMode === 'value_and_name';
const { width, height } = getSparklineDimensions(radius, barWidth, showNameAndValue, shape);
const topPos =
shape === 'gauge'
? dimensions.gaugeBottomY - height - SPARKLINE_SPACING
: `calc(50% + ${radius / (showNameAndValue ? SPARKLINE_TOP_OFFSET_DIVISOR_CIRCLE_NAME_AND_VALUE : SPARKLINE_TOP_OFFSET_DIVISOR_CIRCLE)}px)`;
const config: FieldConfig<GraphFieldConfig> = useMemo(
() => ({
color: {
mode: 'fixed',
fixedColor: color ?? 'blue',
},
custom: {
gradientMode: GraphGradientMode.Opacity,
fillOpacity: 40,
lineInterpolation: LineInterpolation.Smooth,
},
}),
[color]
);
if (!sparkline) {
return null;
}
return (
<div style={{ position: 'absolute', top: topPos }}>
<Sparkline height={height} width={width} sparkline={sparkline} theme={theme} config={config} />
</div>
);
}
);
const showNameAndValue = textMode === 'value_and_name';
const height = radius / (showNameAndValue ? 4 : 3);
const width = radius * (shape === 'gauge' ? 1.6 : 1.4) - barWidth;
const topPos =
shape === 'gauge'
? `${dimensions.gaugeBottomY - height}px`
: `calc(50% + ${radius / (showNameAndValue ? 3.3 : 4)}px)`;
const config: FieldConfig<GraphFieldConfig> = {
color: {
mode: 'fixed',
fixedColor: color ?? 'blue',
},
custom: {
gradientMode: GraphGradientMode.Opacity,
fillOpacity: 40,
lineInterpolation: LineInterpolation.Smooth,
},
};
return (
<div style={{ position: 'absolute', top: topPos }}>
<Sparkline height={height} width={width} sparkline={sparkline} theme={theme} config={config} />
</div>
);
}
RadialSparkline.displayName = 'RadialSparkline';
@@ -1,4 +1,5 @@
import { css } from '@emotion/css';
import { memo } from 'react';
import {
DisplayValue,
@@ -11,13 +12,12 @@ import {
import { useStyles2 } from '../../themes/ThemeContext';
import { calculateFontSize } from '../../utils/measureText';
import { RadialShape, RadialTextMode } from './RadialGauge';
import { GaugeDimensions } from './utils';
import { RadialShape, RadialTextMode, RadialGaugeDimensions } from './types';
interface RadialTextProps {
displayValue: DisplayValue;
theme: GrafanaTheme2;
dimensions: GaugeDimensions;
dimensions: RadialGaugeDimensions;
textMode: Exclude<RadialTextMode, 'auto'>;
shape: RadialShape;
sparkline?: FieldSparkline;
@@ -26,123 +26,137 @@ interface RadialTextProps {
nameManualFontSize?: number;
}
export function RadialText({
displayValue,
theme,
dimensions,
textMode,
shape,
sparkline,
alignmentFactors,
valueManualFontSize,
nameManualFontSize,
}: RadialTextProps) {
const styles = useStyles2(getStyles);
const { centerX, centerY, radius, barWidth } = dimensions;
const LINE_HEIGHT_FACTOR = 1.21;
const VALUE_WIDTH_TO_RADIUS_FACTOR = 0.82;
const NAME_TO_HEIGHT_FACTOR = 0.45;
const LARGE_RADIUS_SCALING_DECAY = 0.86;
const MAX_TEXT_WIDTH_DIVISOR = 7;
const MAX_NAME_HEIGHT_DIVISOR = 4;
const VALUE_SPACE_PERCENTAGE = 0.7;
const SPARKLINE_SPACING = 8;
const MIN_VALUE_FONT_SIZE = 1;
const MIN_NAME_FONT_SIZE = 10;
const MIN_UNIT_FONT_SIZE = 6;
if (textMode === 'none') {
return null;
}
export const RadialText = memo(
({
displayValue,
theme,
dimensions,
textMode,
shape,
sparkline,
alignmentFactors,
valueManualFontSize,
nameManualFontSize,
}: RadialTextProps) => {
const styles = useStyles2(getStyles);
const { centerX, centerY, radius, barWidth } = dimensions;
const nameToAlignTo = (alignmentFactors ? alignmentFactors.title : displayValue.title) ?? '';
const valueToAlignTo = formattedValueToString(alignmentFactors ? alignmentFactors : displayValue);
if (textMode === 'none') {
return null;
}
const showValue = textMode === 'value' || textMode === 'value_and_name';
const showName = textMode === 'name' || textMode === 'value_and_name';
const maxTextWidth = radius * 2 - barWidth - radius / 7;
const nameToAlignTo = (alignmentFactors ? alignmentFactors.title : displayValue.title) ?? '';
const valueToAlignTo = formattedValueToString(alignmentFactors ? alignmentFactors : displayValue);
// Not sure where this comes from but svg text is not using body line-height
const lineHeight = 1.21;
const valueWidthToRadiusFactor = 0.82;
const nameToHeightFactor = 0.45;
const largeRadiusScalingDecay = 0.86;
const showValue = textMode === 'value' || textMode === 'value_and_name';
const showName = textMode === 'name' || textMode === 'value_and_name';
const maxTextWidth = radius * 2 - barWidth - radius / MAX_TEXT_WIDTH_DIVISOR;
// 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;
// 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 = VALUE_WIDTH_TO_RADIUS_FACTOR * Math.pow(radius, LARGE_RADIUS_SCALING_DECAY);
let maxNameHeight = radius / MAX_NAME_HEIGHT_DIVISOR;
if (showValue && showName) {
maxValueHeight = valueWidthToRadiusFactor * Math.pow(radius, largeRadiusScalingDecay);
maxNameHeight = nameToHeightFactor * Math.pow(radius, largeRadiusScalingDecay);
}
if (showValue && showName) {
maxValueHeight = VALUE_WIDTH_TO_RADIUS_FACTOR * Math.pow(radius, LARGE_RADIUS_SCALING_DECAY);
maxNameHeight = NAME_TO_HEIGHT_FACTOR * Math.pow(radius, LARGE_RADIUS_SCALING_DECAY);
}
const valueFontSize =
valueManualFontSize ??
calculateFontSize(
valueToAlignTo,
maxTextWidth,
maxValueHeight,
lineHeight,
undefined,
theme.typography.body.fontWeight
const valueFontSize = Math.max(
valueManualFontSize ??
calculateFontSize(
valueToAlignTo,
maxTextWidth,
maxValueHeight,
LINE_HEIGHT_FACTOR,
undefined,
theme.typography.body.fontWeight
),
MIN_VALUE_FONT_SIZE
);
const nameFontSize =
nameManualFontSize ??
calculateFontSize(
nameToAlignTo,
maxTextWidth,
maxNameHeight,
lineHeight,
undefined,
theme.typography.body.fontWeight
const nameFontSize = Math.max(
nameManualFontSize ??
calculateFontSize(
nameToAlignTo,
maxTextWidth,
maxNameHeight,
LINE_HEIGHT_FACTOR,
undefined,
theme.typography.body.fontWeight
),
MIN_NAME_FONT_SIZE
);
const unitFontSize = Math.max(valueFontSize * 0.7, 5);
const valueHeight = valueFontSize * lineHeight;
const nameHeight = nameFontSize * lineHeight;
const unitFontSize = Math.max(valueFontSize * VALUE_SPACE_PERCENTAGE, MIN_UNIT_FONT_SIZE);
const valueHeight = valueFontSize * LINE_HEIGHT_FACTOR;
const nameHeight = nameFontSize * LINE_HEIGHT_FACTOR;
const valueY = showName ? centerY - nameHeight * 0.3 : 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;
const valueY = showName ? centerY - nameHeight * (1 - VALUE_SPACE_PERCENTAGE) : centerY;
const nameY = showValue ? valueY + valueHeight * VALUE_SPACE_PERCENTAGE : centerY;
const nameColor = showValue ? theme.colors.text.secondary : theme.colors.text.primary;
const suffixShift = (valueFontSize - unitFontSize * LINE_HEIGHT_FACTOR) / 2;
// adjust the text up on gauges and when sparklines are present
let yOffset = 0;
if (shape === 'gauge') {
// we render from the center of the gauge, so move up by half of half of the total height
yOffset -= (valueHeight + nameHeight) / 4;
}
if (sparkline) {
yOffset -= 8;
// adjust the text up on gauges and when sparklines are present
let yOffset = 0;
if (shape === 'gauge') {
// we render from the center of the gauge, so move up by half of half of the total height
yOffset -= (valueHeight + nameHeight) / 4;
}
if (sparkline) {
yOffset -= SPARKLINE_SPACING;
}
return (
<g transform={`translate(0, ${yOffset})`}>
{showValue && (
<text
x={centerX}
y={valueY}
fontSize={valueFontSize}
fill={theme.colors.text.primary}
className={styles.text}
textAnchor="middle"
dominantBaseline="middle"
>
<tspan fontSize={unitFontSize}>{displayValue.prefix ?? ''}</tspan>
<tspan>{displayValue.text}</tspan>
<tspan className={styles.text} fontSize={unitFontSize} dy={suffixShift}>
{displayValue.suffix ?? ''}
</tspan>
</text>
)}
{showName && (
<text
fontSize={nameFontSize}
x={centerX}
y={nameY}
textAnchor="middle"
dominantBaseline="middle"
fill={nameColor}
>
{displayValue.title}
</text>
)}
</g>
);
}
);
return (
<g transform={`translate(0, ${yOffset})`}>
{showValue && (
<text
x={centerX}
y={valueY}
fontSize={valueFontSize}
fill={theme.colors.text.primary}
className={styles.text}
textAnchor="middle"
dominantBaseline="middle"
>
<tspan fontSize={unitFontSize}>{displayValue.prefix ?? ''}</tspan>
<tspan>{displayValue.text}</tspan>
<tspan className={styles.text} fontSize={unitFontSize} dy={suffixShift}>
{displayValue.suffix ?? ''}
</tspan>
</text>
)}
{showName && (
<text
fontSize={nameFontSize}
x={centerX}
y={nameY}
textAnchor="middle"
dominantBaseline="middle"
fill={nameColor}
>
{displayValue.title}
</text>
)}
</g>
);
}
RadialText.displayName = 'RadialText';
const getStyles = (theme: GrafanaTheme2) => ({
const getStyles = (_theme: GrafanaTheme2) => ({
text: css({
verticalAlign: 'bottom',
}),
@@ -1,20 +1,22 @@
import { FieldDisplay, Threshold } from '@grafana/data';
import { RadialArcPath } from './RadialArcPath';
import { RadialColorDefs } from './RadialColorDefs';
import { GaugeDimensions } from './utils';
import { GradientStop, RadialGaugeDimensions, RadialShape } from './types';
import { getFieldConfigMinMax } from './utils';
export interface Props {
dimensions: GaugeDimensions;
interface ThresholdsBarProps {
dimensions: RadialGaugeDimensions;
angleRange: number;
startAngle: number;
endAngle: number;
shape: RadialShape;
fieldDisplay: FieldDisplay;
roundedBars?: boolean;
glowFilter?: string;
colorDefs: RadialColorDefs;
thresholds: Threshold[];
gradient?: GradientStop[];
}
export function ThresholdsBar({
dimensions,
fieldDisplay,
@@ -22,19 +24,18 @@ export function ThresholdsBar({
angleRange,
roundedBars,
glowFilter,
colorDefs,
thresholds,
}: Props) {
const fieldConfig = fieldDisplay.field;
const min = fieldConfig.min ?? 0;
const max = fieldConfig.max ?? 100;
shape,
gradient,
}: ThresholdsBarProps) {
const thresholdDimensions = {
...dimensions,
barWidth: dimensions.thresholdsBarWidth,
radius: dimensions.thresholdsBarRadius,
};
const [min, max] = getFieldConfigMinMax(fieldDisplay);
let currentStart = startAngle;
let paths: React.ReactNode[] = [];
@@ -48,27 +49,26 @@ export function ThresholdsBar({
valueDeg = 0;
}
let lengthDeg = valueDeg - currentStart + startAngle;
const lengthDeg = valueDeg - currentStart + startAngle;
const colorProps = gradient ? { gradient } : { color: threshold.color };
paths.push(
<RadialArcPath
key={i}
startAngle={currentStart}
arcLengthDeg={lengthDeg}
barEndcaps={shape === 'circle' && roundedBars}
dimensions={thresholdDimensions}
roundedBars={roundedBars}
fieldDisplay={fieldDisplay}
glowFilter={glowFilter}
color={colorDefs.getColor(threshold.color, true)}
roundedBars={roundedBars}
shape={shape}
startAngle={currentStart}
{...colorProps}
/>
);
currentStart += lengthDeg;
}
return (
<>
<g>{paths}</g>
<defs>{colorDefs.getDefs()}</defs>
</>
);
return <g>{paths}</g>;
}
@@ -0,0 +1,144 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RadialGauge color utils buildGradientColors should map threshold colors correctly (with baseColor if displayProcessor does not return colors) 1`] = `
[
{
"color": "#444444",
"percent": 0,
},
{
"color": "#FADE2A",
"percent": 0.5,
},
{
"color": "#F2495C",
"percent": 0.8,
},
{
"color": "#444444",
"percent": 1,
},
]
`;
exports[`RadialGauge color utils buildGradientColors should map threshold colors correctly (with baseColor if displayProcessor does not return colors) 2`] = `
[
{
"color": "#FF0000",
"percent": 0,
},
{
"color": "#FADE2A",
"percent": 0.5,
},
{
"color": "#F2495C",
"percent": 0.8,
},
{
"color": "#FF0000",
"percent": 1,
},
]
`;
exports[`RadialGauge color utils buildGradientColors should return gradient colors for by-value color mode in dark theme 1`] = `
[
{
"color": "#181b1f",
"percent": 0,
},
{
"color": "#1F60C4",
"percent": 1,
},
]
`;
exports[`RadialGauge color utils buildGradientColors should return gradient colors for by-value color mode in light theme 1`] = `
[
{
"color": "#ffffff",
"percent": 0,
},
{
"color": "#1250B0",
"percent": 1,
},
]
`;
exports[`RadialGauge color utils buildGradientColors should return gradient colors for continuous color modes 1`] = `
[
{
"color": "rgb(0, 32, 81)",
"percent": 0,
},
{
"color": "rgb(17, 54, 108)",
"percent": 0.125,
},
{
"color": "rgb(60, 77, 110)",
"percent": 0.25,
},
{
"color": "rgb(98, 100, 111)",
"percent": 0.375,
},
{
"color": "rgb(127, 124, 117)",
"percent": 0.5,
},
{
"color": "rgb(154, 148, 120)",
"percent": 0.625,
},
{
"color": "rgb(187, 175, 113)",
"percent": 0.75,
},
{
"color": "rgb(226, 203, 92)",
"percent": 0.875,
},
{
"color": "rgb(253, 234, 69)",
"percent": 1,
},
]
`;
exports[`RadialGauge color utils buildGradientColors should return gradient colors for fixed color mode in dark theme 1`] = `
[
{
"color": "#37237a",
"percent": 0,
},
{
"color": "#a146da",
"percent": 0.75,
},
{
"color": "#a146da",
"percent": 1,
},
]
`;
exports[`RadialGauge color utils buildGradientColors should return gradient colors for fixed color mode in light theme 1`] = `
[
{
"color": "#a146da",
"percent": 0,
},
{
"color": "#3e2b9a",
"percent": 0.75,
},
{
"color": "#3e2b9a",
"percent": 1,
},
]
`;
@@ -0,0 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`RadialGauge utils drawRadialArcPath should draw correct path for center x and y 1`] = `"M 150 110 A 90 90 0 1 1 149.98429203681178 110.00000137077838 A 10 10 0 0 1 149.98778269529805 130.00000106616096 A 70 70 0 1 0 150 130 A 10 10 0 0 1 150 110 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for half arc 1`] = `"M 100 10 A 90 90 0 0 1 100 190 L 100 170 A 70 70 0 0 0 100 30 L 100 10 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for narrow bar width 1`] = `"M 100 17.5 A 82.5 82.5 0 0 1 100 182.5 L 100 177.5 A 77.5 77.5 0 0 0 100 22.5 L 100 17.5 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for narrow radius 1`] = `"M 100 40 A 60 60 0 0 1 100 160 L 100 140 A 40 40 0 0 0 100 60 L 100 40 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for quarter arc 1`] = `"M 100 10 A 90 90 0 0 1 190 100 L 170 100 A 70 70 0 0 0 100 30 L 100 10 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for rounded bars 1`] = `"M 100 10 A 90 90 0 1 1 10 100.00000000000001 A 10 10 0 0 1 30 100.00000000000001 A 70 70 0 1 0 100 30 A 10 10 0 0 1 100 10 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for three quarter arc 1`] = `"M 100 10 A 90 90 0 1 1 10 100.00000000000001 L 30 100.00000000000001 A 70 70 0 1 0 100 30 L 100 10 Z"`;
exports[`RadialGauge utils drawRadialArcPath should draw correct path for wide bar width 1`] = `"M 100 -5 A 105 105 0 0 1 100 205 L 100 155 A 55 55 0 0 0 100 45 L 100 -5 Z"`;
@@ -0,0 +1,306 @@
import { defaultsDeep } from 'lodash';
import { createTheme, FALLBACK_COLOR, Field, FieldDisplay, FieldType, ThresholdsMode } from '@grafana/data';
import { FieldColorModeId } from '@grafana/schema';
import {
buildGradientColors,
colorAtGradientPercent,
getBarEndcapColors,
getEndpointMarkerColors,
getGradientCss,
getGradientStopsForPercent,
} from './colors';
export type DeepPartial<T> = {
[P in keyof T]?: DeepPartial<T[P]>;
};
describe('RadialGauge color utils', () => {
describe('buildGradientColors', () => {
const createField = (colorMode: FieldColorModeId): Field =>
({
type: FieldType.number,
name: 'Test Field',
config: {
color: {
mode: colorMode,
},
thresholds: {
mode: ThresholdsMode.Absolute,
steps: [
{ value: -Infinity, color: 'green' },
{ value: 50, color: 'yellow' },
{ value: 80, color: 'red' },
],
},
},
values: [70, 40, 30, 90, 55],
}) satisfies Field;
const buildFieldDisplay = (field: Field, part = {}): FieldDisplay =>
defaultsDeep(part, {
field: field.config,
colIndex: 0,
view: {
getFieldDisplayProcessor: jest.fn(() => jest.fn(() => ({ color: undefined }))),
},
display: {
numeric: 75,
},
});
it('should return the baseColor if gradient is false-y', () => {
expect(
buildGradientColors(false, createTheme(), buildFieldDisplay(createField(FieldColorModeId.Fixed)), '#FF0000')
).toEqual([
{ color: '#FF0000', percent: 0 },
{ color: '#FF0000', percent: 1 },
]);
expect(
buildGradientColors(undefined, createTheme(), buildFieldDisplay(createField(FieldColorModeId.Fixed)), '#FF0000')
).toEqual([
{ color: '#FF0000', percent: 0 },
{ color: '#FF0000', percent: 1 },
]);
});
it('uses the fallback color if no baseColor is set', () => {
expect(buildGradientColors(false, createTheme(), buildFieldDisplay(createField(FieldColorModeId.Fixed)))).toEqual(
[
{ color: FALLBACK_COLOR, percent: 0 },
{ color: FALLBACK_COLOR, percent: 1 },
]
);
});
it('should map threshold colors correctly (with baseColor if displayProcessor does not return colors)', () => {
expect(
buildGradientColors(
true,
createTheme(),
buildFieldDisplay(createField(FieldColorModeId.Thresholds), {
view: { getFieldDisplayProcessor: jest.fn(() => jest.fn(() => ({ color: '#444444' }))) },
})
)
).toMatchSnapshot();
});
it('should map threshold colors correctly (with baseColor if displayProcessor does not return colors)', () => {
expect(
buildGradientColors(true, createTheme(), buildFieldDisplay(createField(FieldColorModeId.Thresholds)), '#FF0000')
).toMatchSnapshot();
});
it('should return gradient colors for continuous color modes', () => {
expect(
buildGradientColors(
true,
createTheme(),
buildFieldDisplay(createField(FieldColorModeId.ContinuousCividis)),
'#00FF00'
)
).toMatchSnapshot();
});
it.each(['dark', 'light'] as const)('should return gradient colors for by-value color mode in %s theme', (mode) => {
expect(
buildGradientColors(
true,
createTheme({ colors: { mode } }),
buildFieldDisplay(createField(FieldColorModeId.ContinuousBlues))
)
).toMatchSnapshot();
});
it.each(['dark', 'light'] as const)('should return gradient colors for fixed color mode in %s theme', (mode) => {
expect(
buildGradientColors(
true,
createTheme({ colors: { mode } }),
buildFieldDisplay(createField(FieldColorModeId.Fixed)),
'#442299'
)
).toMatchSnapshot();
});
});
describe('colorAtGradientPercent', () => {
it('should calculate the color at a given percent in a gradient of two colors', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#0000ff', percent: 1 },
];
expect(colorAtGradientPercent(gradient, 0).toHexString()).toBe('#ff0000');
expect(colorAtGradientPercent(gradient, 0.25).toHexString()).toBe('#bf0040');
expect(colorAtGradientPercent(gradient, 0.5).toHexString()).toBe('#800080');
expect(colorAtGradientPercent(gradient, 0.75).toHexString()).toBe('#4000bf');
expect(colorAtGradientPercent(gradient, 1).toHexString()).toBe('#0000ff');
});
it('should calculate the color at a given percent in a gradient of multiple colors', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
expect(colorAtGradientPercent(gradient, 0).toHexString()).toBe('#ff0000');
expect(colorAtGradientPercent(gradient, 0.25).toHexString()).toBe('#808000');
expect(colorAtGradientPercent(gradient, 0.5).toHexString()).toBe('#00ff00');
expect(colorAtGradientPercent(gradient, 0.75).toHexString()).toBe('#008080');
expect(colorAtGradientPercent(gradient, 1).toHexString()).toBe('#0000ff');
});
it('will still work if unsorted', () => {
const gradient = [
{ color: '#0000ff', percent: 1 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#ff0000', percent: 0 },
];
expect(colorAtGradientPercent(gradient, 0).toHexString()).toBe('#ff0000');
expect(colorAtGradientPercent(gradient, 0.25).toHexString()).toBe('#808000');
expect(colorAtGradientPercent(gradient, 0.5).toHexString()).toBe('#00ff00');
expect(colorAtGradientPercent(gradient, 0.75).toHexString()).toBe('#008080');
expect(colorAtGradientPercent(gradient, 1).toHexString()).toBe('#0000ff');
});
it('should not throw an error when percent is outside 0-1 range', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#0000ff', percent: 1 },
];
expect(colorAtGradientPercent(gradient, -0.5).toHexString()).toBe('#ff0000');
expect(colorAtGradientPercent(gradient, 1.5).toHexString()).toBe('#0000ff');
});
it('should throw an error when less than two stops are provided', () => {
expect(() => {
colorAtGradientPercent([], 0.5);
}).toThrow('colorAtGradientPercent requires at least two color stops');
expect(() => {
colorAtGradientPercent([{ color: '#ff0000', percent: 0 }], 0.5);
}).toThrow('colorAtGradientPercent requires at least two color stops');
});
});
describe('getBarEndcapColors', () => {
it('should return the first and last colors in the gradient', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
const [startColor, endColor] = getBarEndcapColors(gradient);
expect(startColor).toBe('#ff0000');
expect(endColor).toBe('#0000ff');
});
it('should return the correct end color based on percent', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
const [startColor, endColor] = getBarEndcapColors(gradient, 0.25);
expect(startColor).toBe('#ff0000');
expect(endColor).toBe('#808000');
});
it('should handle gradients with only one colors', () => {
const gradient = [{ color: '#ff0000', percent: 0 }];
const [startColor, endColor] = getBarEndcapColors(gradient);
expect(startColor).toBe('#ff0000');
expect(endColor).toBe('#ff0000');
});
it('should throw an error when no colors are provided', () => {
expect(() => {
getBarEndcapColors([]);
}).toThrow('getBarEndcapColors requires at least one color stop');
});
});
describe('getGradientCss', () => {
it('should return conic-gradient CSS for circle shape', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
const css = getGradientCss(gradient, 'circle');
expect(css).toBe('conic-gradient(from 0deg, #ff0000 0.00%, #00ff00 50.00%, #0000ff 100.00%)');
});
it('should return linear-gradient CSS for arc shape', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
const css = getGradientCss(gradient, 'gauge');
expect(css).toBe('linear-gradient(90deg, #ff0000 0.00%, #00ff00 50.00%, #0000ff 100.00%)');
});
});
describe('getEndpointMarkerColors', () => {
it('should return contrasting guide dot colors based on the gradient endpoints and percent', () => {
const gradient = [
{ color: '#000000', percent: 0 },
{ color: '#ffffff', percent: 0.5 },
{ color: '#ffffff', percent: 1 },
];
const [startDotColor, endDotColor] = getEndpointMarkerColors(gradient, 0.35);
expect(startDotColor).toBe('#fbfbfb');
expect(endDotColor).toBe('#111217');
});
});
describe('getGradientStopsForPercent', () => {
it('should return the correct gradient stops for a given percent', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
const [left, right] = getGradientStopsForPercent(gradient, 0.25);
expect(left).toEqual({ color: '#ff0000', percent: 0 });
expect(right).toEqual({ color: '#00ff00', percent: 0.5 });
});
it('should handle edge cases where percent is at the boundaries', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
let [left, right] = getGradientStopsForPercent(gradient, 0);
expect(left).toEqual({ color: '#ff0000', percent: 0 });
expect(right).toEqual({ color: '#ff0000', percent: 0 });
[left, right] = getGradientStopsForPercent(gradient, 1);
expect(left).toEqual({ color: '#0000ff', percent: 1 });
expect(right).toEqual({ color: '#0000ff', percent: 1 });
});
it('should return the same stop if there is one that is equal to the percentage', () => {
const gradient = [
{ color: '#ff0000', percent: 0 },
{ color: '#00ff00', percent: 0.5 },
{ color: '#0000ff', percent: 1 },
];
let [left, right] = getGradientStopsForPercent(gradient, 0);
expect(left).toEqual({ color: '#ff0000', percent: 0 });
expect(right).toEqual({ color: '#ff0000', percent: 0 });
[left, right] = getGradientStopsForPercent(gradient, 0.5);
expect(left).toEqual({ color: '#00ff00', percent: 0.5 });
expect(right).toEqual({ color: '#00ff00', percent: 0.5 });
[left, right] = getGradientStopsForPercent(gradient, 1);
expect(left).toEqual({ color: '#0000ff', percent: 1 });
expect(right).toEqual({ color: '#0000ff', percent: 1 });
});
});
});
@@ -0,0 +1,195 @@
import tinycolor from 'tinycolor2';
import { colorManipulator, FALLBACK_COLOR, FieldDisplay, getFieldColorMode, GrafanaTheme2 } from '@grafana/data';
import { FieldColorModeId } from '@grafana/schema';
import { GradientStop, RadialShape } from './types';
import { getFieldConfigMinMax, getFieldDisplayProcessor, getValuePercentageForValue } from './utils';
export function buildGradientColors(
gradient = false,
theme: GrafanaTheme2,
fieldDisplay: FieldDisplay,
baseColor = fieldDisplay.display.color ?? FALLBACK_COLOR
): GradientStop[] {
if (!gradient) {
return [
{ color: baseColor, percent: 0 },
{ color: baseColor, percent: 1 },
];
}
const colorMode = getFieldColorMode(fieldDisplay.field.color?.mode);
// thresholds get special handling
if (colorMode.id === FieldColorModeId.Thresholds) {
const displayProcessor = getFieldDisplayProcessor(fieldDisplay);
const [min, max] = getFieldConfigMinMax(fieldDisplay);
const thresholds = fieldDisplay.field.thresholds?.steps ?? [];
const result: Array<{ color: string; percent: number }> = [
{ color: displayProcessor(min).color ?? baseColor, percent: 0 },
];
for (const threshold of thresholds) {
if (threshold.value > min && threshold.value < max) {
const percent = (threshold.value - min) / (max - min);
result.push({ color: theme.visualization.getColorByName(threshold.color), percent });
}
}
result.push({ color: displayProcessor(max).color ?? baseColor, percent: 1 });
return result;
}
// Handle continuous color modes before other by-value modes
if (colorMode.isContinuous && colorMode.getColors) {
const colors = colorMode.getColors(theme);
return colors.map((color, idx) => ({ color, percent: idx / (colors.length - 1) }));
}
// 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 darkerColor = tinycolor(baseColor).darken(5);
const lighterColor = tinycolor(baseColor).spin(20).lighten(10);
const color1 = theme.isDark ? lighterColor : darkerColor;
const color2 = theme.isDark ? darkerColor : lighterColor;
return [
{ color: color1.toString(), percent: 0 },
{ color: color2.toString(), percent: 0.6 },
{ color: color2.toString(), percent: 1 },
];
}
// For fixed / palette based color scales we can create a more hue and light
// based linear gradient that we rotate with the value
const darkerColor = tinycolor(baseColor)
.spin(-20)
.darken(theme.isDark ? 15 : 5);
const lighterColor = tinycolor(baseColor).saturate(20).spin(20).brighten(10).lighten(10);
const underlyingGradient = [
{ color: theme.isDark ? darkerColor.toString() : lighterColor.toString(), percent: 0 },
{ color: theme.isDark ? lighterColor.toString() : darkerColor.toString(), percent: 1 },
];
// rotate the gradient so that the highest contrasting point is the value, depending on theme.
const valuePercent = getValuePercentageForValue(fieldDisplay);
const startColor = theme.isDark
? colorAtGradientPercent(underlyingGradient, 1 - valuePercent).toHexString()
: underlyingGradient[0].color;
const endColor = theme.isDark
? underlyingGradient[1].color
: colorAtGradientPercent(underlyingGradient, valuePercent).toHexString();
return [
{ color: startColor, percent: 0 },
{ color: endColor, percent: valuePercent },
{ color: endColor, percent: 1 },
];
}
/**
* get the relevant gradient stops surrounding a given percentage. could be same stop if the
* percent matches a stop exactly.
*
* @param sortedGradientStops - gradient stops sorted by percent
* @param percent - percentage 0..1
* @returns {[GradientStop, GradientStop]} - the two gradient stops surrounding the given percentage
*/
export function getGradientStopsForPercent(
sortedGradientStops: GradientStop[],
percent: number
): [GradientStop, GradientStop] {
if (percent <= 0) {
return [sortedGradientStops[0], sortedGradientStops[0]];
}
if (percent >= 1) {
const last = sortedGradientStops.length - 1;
return [sortedGradientStops[last], sortedGradientStops[last]];
}
// find surrounding stops using binary search
let lo = 0;
let hi = sortedGradientStops.length - 1;
while (lo + 1 < hi) {
const mid = (lo + hi) >> 1;
if (percent === sortedGradientStops[mid].percent) {
return [sortedGradientStops[mid], sortedGradientStops[mid]];
}
if (percent < sortedGradientStops[mid].percent) {
hi = mid;
} else {
lo = mid;
}
}
return [sortedGradientStops[lo], sortedGradientStops[hi]];
}
/**
* @alpha - perhaps this should go in colorManipulator.ts
* Given color stops (each with a color and percentage 0..1) returns the color at a given percentage.
* Uses tinycolor.mix for interpolation.
* @params stops - array of color stops (percentages 0..1)
* @params percent - percentage 0..1
* @returns color at the given percentage
*/
export function colorAtGradientPercent(stops: GradientStop[], percent: number): tinycolor.Instance {
if (!stops || stops.length < 2) {
throw new Error('colorAtGradientPercent requires at least two color stops');
}
const sorted = stops
.map((s: GradientStop): GradientStop => ({ color: s.color, percent: Math.min(Math.max(0, s.percent), 1) }))
.sort((a: GradientStop, b: GradientStop) => a.percent - b.percent);
const [left, right] = getGradientStopsForPercent(sorted, percent);
const range = right.percent - left.percent;
const t = range === 0 ? 0 : (percent - left.percent) / range; // 0..1
return tinycolor.mix(left.color, right.color, t * 100);
}
export function getBarEndcapColors(gradientStops: GradientStop[], percent = 1): [string, string] {
if (gradientStops.length === 0) {
throw new Error('getBarEndcapColors requires at least one color stop');
}
const startColor = gradientStops[0].color;
let endColor = gradientStops[gradientStops.length - 1].color;
// if we have a percentageFilled, use it to get a the correct end color based on where the bar terminates
if (gradientStops.length >= 2) {
const endColorByPercentage = colorAtGradientPercent(gradientStops, percent);
endColor =
endColorByPercentage.getAlpha() === 1 ? endColorByPercentage.toHexString() : endColorByPercentage.toHex8String();
}
return [startColor, endColor];
}
export function getGradientCss(gradientStops: GradientStop[], shape: RadialShape): string {
const colorStrings = gradientStops.map((stop) => `${stop.color} ${(stop.percent * 100).toFixed(2)}%`);
if (shape === 'circle') {
return `conic-gradient(from 0deg, ${colorStrings.join(', ')})`;
}
return `linear-gradient(90deg, ${colorStrings.join(', ')})`;
}
// the theme does not make the full palette available to us, and we
// don't want transparent colors which our grays usually have.
const GRAY_05 = '#111217';
const GRAY_90 = '#fbfbfb';
const CONTRAST_THRESHOLD_MAX = 4.5;
const getGuideDotColor = (color: string): string => {
const darkColor = GRAY_05;
const lightColor = GRAY_90;
return colorManipulator.getContrastRatio(darkColor, color) >= CONTRAST_THRESHOLD_MAX ? darkColor : lightColor;
};
export function getEndpointMarkerColors(gradientStops: GradientStop[], percent = 1): [string, string] {
const [startColor, endColor] = getBarEndcapColors(gradientStops, percent);
return [getGuideDotColor(startColor), getGuideDotColor(endColor)];
}
@@ -1,15 +1,18 @@
import { GrafanaTheme2 } from '@grafana/data';
import { GaugeDimensions } from './utils';
import { RadialGaugeDimensions } from './types';
export interface GlowGradientProps {
id: string;
barWidth: number;
}
const MIN_GLOW_SIZE = 0.75;
const GLOW_FACTOR = 0.08;
export function GlowGradient({ id, barWidth }: GlowGradientProps) {
// 0.75 is the minimum glow size, and it scales with bar width
const glowSize = 0.75 + barWidth * 0.08;
const glowSize = MIN_GLOW_SIZE + barWidth * GLOW_FACTOR;
return (
<filter id={id} filterUnits="userSpaceOnUse">
@@ -22,56 +25,19 @@ export function GlowGradient({ id, barWidth }: GlowGradientProps) {
);
}
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 (
<linearGradient x1={x1} y1={y1} x2={x2} y2={y2} id={id} gradientUnits="userSpaceOnUse">
<stop offset="0%" stopColor={'black'} stopOpacity={0.0} />
<stop offset="90%" stopColor={'black'} stopOpacity={0.0} />
<stop offset="91%" stopColor={'black'} stopOpacity={1} />
</linearGradient>
);
}
return (
<linearGradient x1={x1} y1={y1} x2={x2} y2={y2} id={id} gradientUnits="userSpaceOnUse">
<stop offset="0%" stopColor={'white'} stopOpacity={0.0} />
<stop offset="95%" stopColor={'white'} stopOpacity={0.5} />
{roundedBars && <stop offset="100%" stopColor={'white'} stopOpacity={roundedBars ? 0.7 : 1} />}
</linearGradient>
);
}
const CENTER_GLOW_OPACITY = 0.15;
export function CenterGlowGradient({ gaugeId, color }: { gaugeId: string; color: string }) {
return (
<radialGradient id={`circle-glow-${gaugeId}`} r={'50%'} fr={'0%'}>
<stop offset="0%" stopColor={color} stopOpacity={0.2} />
<radialGradient id={`circle-glow-${gaugeId}`} r="50%" fr="0%">
<stop offset="0%" stopColor={color} stopOpacity={CENTER_GLOW_OPACITY} />
<stop offset="90%" stopColor={color} stopOpacity={0} />
</radialGradient>
);
}
export interface CenterGlowProps {
dimensions: GaugeDimensions;
dimensions: RadialGaugeDimensions;
gaugeId: string;
color?: string;
}
@@ -82,8 +48,8 @@ export function MiddleCircleGlow({ dimensions, gaugeId, color }: CenterGlowProps
return (
<>
<defs>
<radialGradient id={gradientId} r={'50%'} fr={'0%'}>
<stop offset="0%" stopColor={color} stopOpacity={0.15} />
<radialGradient id={gradientId} r="50%" fr="0%">
<stop offset="0%" stopColor={color} stopOpacity={CENTER_GLOW_OPACITY} />
<stop offset="90%" stopColor={color} stopOpacity={0} />
</radialGradient>
</defs>
@@ -93,3 +59,36 @@ export function MiddleCircleGlow({ dimensions, gaugeId, color }: CenterGlowProps
</>
);
}
export function SpotlightGradient({
id,
dimensions,
roundedBars,
angle,
theme,
}: {
id: string;
dimensions: RadialGaugeDimensions;
angle: number;
roundedBars: boolean;
theme: GrafanaTheme2;
}) {
if (theme.isLight) {
return null;
}
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);
return (
<linearGradient x1={x1} y1={y1} x2={x2} y2={y2} id={id} gradientUnits="userSpaceOnUse">
<stop offset="0%" stopColor={'white'} stopOpacity={0.0} />
<stop offset="95%" stopColor={'white'} stopOpacity={0.5} />
{roundedBars && <stop offset="100%" stopColor={'white'} stopOpacity={roundedBars ? 0.7 : 1} />}
</linearGradient>
);
}
@@ -0,0 +1,25 @@
export type RadialTextMode = 'auto' | 'value_and_name' | 'value' | 'name' | 'none';
export type RadialShape = 'circle' | 'gauge';
export interface RadialGaugeDimensions {
margin: number;
radius: number;
centerX: number;
centerY: number;
barWidth: number;
endAngle?: number;
barIndex: number;
thresholdsBarRadius: number;
thresholdsBarWidth: number;
thresholdsBarSpacing: number;
scaleLabelsFontSize: number;
scaleLabelsSpacing: number;
scaleLabelsRadius: number;
gaugeBottomY: number;
}
/** @alpha - perhaps this should go in @grafana/data */
export interface GradientStop {
color: string;
percent: number;
}
@@ -1,24 +1,111 @@
import { FieldDisplay } from '@grafana/data';
import { DataFrameView, FieldDisplay } from '@grafana/data';
import type { RadialGaugeProps } from './RadialGauge';
import { calculateDimensions, toRad, getValueAngleForValue } from './utils';
import { RadialGaugeDimensions } from './types';
import {
calculateDimensions,
toRad,
getValueAngleForValue,
drawRadialArcPath,
getFieldConfigMinMax,
getFieldDisplayProcessor,
getAngleBetweenSegments,
getOptimalSegmentCount,
} from './utils';
describe('RadialGauge utils', () => {
function calc(overrides: Partial<RadialGaugeProps & { barIndex: number }> = {}) {
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,
overrides.showScaleLabels ?? false
);
}
describe('getFieldDisplayProcessor', () => {
it('should return display processor from view when available', () => {
const mockProcessor = jest.fn();
const mockView = {
getFieldDisplayProcessor: jest.fn().mockReturnValue(mockProcessor),
} as unknown as DataFrameView;
const fieldDisplay: FieldDisplay = {
display: { numeric: 50, text: '50', color: 'blue' },
field: {},
view: mockView,
colIndex: 0,
rowIndex: 0,
name: 'test',
getLinks: () => [],
hasLinks: false,
};
const dp = getFieldDisplayProcessor(fieldDisplay);
expect(dp).toBe(mockProcessor);
expect(mockView.getFieldDisplayProcessor).toHaveBeenCalledWith(0);
});
it('should return default display processor when view is not available', () => {
const fieldDisplay: FieldDisplay = {
display: { numeric: 50, text: '50', color: 'blue' },
field: {},
view: undefined,
colIndex: 0,
rowIndex: 0,
name: 'test',
getLinks: () => [],
hasLinks: false,
};
const dp = getFieldDisplayProcessor(fieldDisplay);
expect(dp).toBeDefined();
expect(typeof dp).toBe('function');
});
});
describe('getFieldConfigMinMax', () => {
it('should return min and max from field config when defined', () => {
const fieldDisplay: FieldDisplay = {
display: { numeric: 50, text: '50', color: 'blue' },
field: { min: 10, max: 90 },
view: undefined,
colIndex: 0,
rowIndex: 0,
name: 'test',
getLinks: () => [],
hasLinks: false,
};
const [min, max] = getFieldConfigMinMax(fieldDisplay);
expect(min).toBe(10);
expect(max).toBe(90);
});
it('should return default min and max when not defined in field config', () => {
const fieldDisplay: FieldDisplay = {
display: { numeric: 50, text: '50', color: 'blue' },
field: {},
view: undefined,
colIndex: 0,
rowIndex: 0,
name: 'test',
getLinks: () => [],
hasLinks: false,
};
const [min, max] = getFieldConfigMinMax(fieldDisplay);
expect(min).toBe(0);
expect(max).toBe(100);
});
});
describe('calculateDimensions', () => {
function calc(overrides: Partial<RadialGaugeProps & { barIndex: number }> = {}) {
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,
overrides.showScaleLabels ?? false
);
}
it('should calculate basic dimensions for a square gauge', () => {
const result = calc();
@@ -194,4 +281,84 @@ describe('RadialGauge utils', () => {
expect(result.angle).toBe(240);
});
});
describe('drawRadialArcPath', () => {
const defaultDims: RadialGaugeDimensions = Object.freeze({
centerX: 100,
centerY: 100,
radius: 80,
barWidth: 20,
margin: 0,
barIndex: 0,
thresholdsBarWidth: 0,
thresholdsBarSpacing: 0,
thresholdsBarRadius: 0,
scaleLabelsFontSize: 0,
scaleLabelsSpacing: 0,
scaleLabelsRadius: 0,
gaugeBottomY: 0,
});
it.each([
{ description: 'quarter arc', startAngle: 0, endAngle: 90 },
{ description: 'half arc', startAngle: 0, endAngle: 180 },
{ description: 'three quarter arc', startAngle: 0, endAngle: 270 },
{ description: 'rounded bars', startAngle: 0, endAngle: 270, roundedBars: true },
{ description: 'wide bar width', startAngle: 0, endAngle: 180, dimensions: { barWidth: 50 } },
{ description: 'narrow bar width', startAngle: 0, endAngle: 180, dimensions: { barWidth: 5 } },
{ description: 'narrow radius', startAngle: 0, endAngle: 180, dimensions: { radius: 50 } },
{
description: 'center x and y',
startAngle: 0,
endAngle: 360,
roundedBars: true,
dimensions: { centerX: 150, centerY: 200 },
},
])(`should draw correct path for $description`, ({ startAngle, endAngle, dimensions, roundedBars }) => {
const path = drawRadialArcPath(startAngle, endAngle, { ...defaultDims, ...dimensions }, roundedBars);
expect(path).toMatchSnapshot();
});
describe('edge cases', () => {
it('should adjust 360deg or greater arcs to avoid SVG rendering issues', () => {
expect(drawRadialArcPath(0, 360, defaultDims)).toEqual(drawRadialArcPath(0, 359.99, defaultDims));
expect(drawRadialArcPath(0, 380, defaultDims)).toEqual(drawRadialArcPath(0, 380, defaultDims));
});
it('should return empty string if inner radius collapses to zero or below', () => {
const smallRadiusDims = { ...defaultDims, radius: 5, barWidth: 20 };
expect(drawRadialArcPath(0, 180, smallRadiusDims)).toBe('');
});
});
});
describe('getAngleBetweenSegments', () => {
it('should calculate angle between segments based on spacing and count', () => {
expect(getAngleBetweenSegments(2, 10, 360)).toBe(48);
expect(getAngleBetweenSegments(5, 15, 180)).toBe(40);
});
});
describe('getOptimalSegmentCount', () => {
it('should adjust segment count based on dimensions and spacing', () => {
const dimensions: RadialGaugeDimensions = {
centerX: 100,
centerY: 100,
radius: 80,
barWidth: 20,
margin: 0,
barIndex: 0,
thresholdsBarWidth: 0,
thresholdsBarSpacing: 0,
thresholdsBarRadius: 0,
scaleLabelsFontSize: 0,
scaleLabelsSpacing: 0,
scaleLabelsRadius: 0,
gaugeBottomY: 0,
};
expect(getOptimalSegmentCount(dimensions, 2, 10, 360)).toBe(8);
expect(getOptimalSegmentCount(dimensions, 1, 5, 360)).toBe(5);
});
});
});
@@ -1,11 +1,38 @@
import { FieldDisplay } from '@grafana/data';
import { FieldDisplay, getDisplayProcessor } from '@grafana/data';
export function getValueAngleForValue(fieldDisplay: FieldDisplay, startAngle: number, endAngle: number) {
const angleRange = (360 % (startAngle === 0 ? 1 : startAngle)) + endAngle;
import { RadialGaugeDimensions } from './types';
export function getFieldDisplayProcessor(displayValue: FieldDisplay) {
if (displayValue.view && displayValue.colIndex != null) {
const dp = displayValue.view.getFieldDisplayProcessor(displayValue.colIndex);
if (dp) {
return dp;
}
}
return getDisplayProcessor();
}
export function getFieldConfigMinMax(fieldDisplay: FieldDisplay) {
const min = fieldDisplay.field.min ?? 0;
const max = fieldDisplay.field.max ?? 100;
return [min, max];
}
let angle = ((fieldDisplay.display.numeric - min) / (max - min)) * angleRange;
export function getValuePercentageForValue(fieldDisplay: FieldDisplay, value = fieldDisplay.display.numeric) {
const [min, max] = getFieldConfigMinMax(fieldDisplay);
return (value - min) / (max - min);
}
export function getValueAngleForValue(
fieldDisplay: FieldDisplay,
startAngle: number,
endAngle: number,
value = fieldDisplay.display.numeric
) {
const angleRange = (360 % (startAngle === 0 ? 1 : startAngle)) + endAngle;
let angle = getValuePercentageForValue(fieldDisplay, value) * angleRange;
if (angle > angleRange) {
angle = angleRange;
@@ -26,24 +53,19 @@ 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;
thresholdsBarRadius: number;
thresholdsBarWidth: number;
thresholdsBarSpacing: number;
showScaleLabels?: boolean;
scaleLabelsFontSize: number;
scaleLabelsSpacing: number;
scaleLabelsRadius: number;
gaugeBottomY: number;
}
/**
* returns the calculated dimensions for the radial gauge
* @param width
* @param height
* @param endAngle
* @param glow
* @param roundedBars
* @param barWidthFactor
* @param barIndex
* @param thresholdBar
* @param showScaleLabels
* @returns {RadialGaugeDimensions}
*/
export function calculateDimensions(
width: number,
height: number,
@@ -54,7 +76,7 @@ export function calculateDimensions(
barIndex: number,
thresholdBar?: boolean,
showScaleLabels?: boolean
): GaugeDimensions {
): RadialGaugeDimensions {
const yMaxAngle = endAngle > 180 ? 180 : endAngle;
let margin = 0;
@@ -97,6 +119,7 @@ export function calculateDimensions(
maxRadiusW -= labelsSize;
maxRadiusH -= labelsSize;
// FIXME: needs coverage
// For gauges the max label needs a bit more vertical space so that it does not get clipped
if (maxRadiusIsLimitedByHeight && endAngle < 180) {
const amount = outerRadius * 0.07;
@@ -155,3 +178,105 @@ export function toCartesian(centerX: number, centerY: number, radius: number, an
y: centerY + radius * Math.sin(radian),
};
}
export function drawRadialArcPath(
startAngle: number,
endAngle: number,
dimensions: RadialGaugeDimensions,
roundedBars?: boolean
): string {
const { radius, centerX, centerY, barWidth } = dimensions;
// For some reason a 100% full arc cannot be rendered
if (endAngle >= 360) {
endAngle = 359.99;
}
const startRadians = toRad(startAngle);
const endRadians = toRad(startAngle + endAngle);
const largeArc = endAngle > 180 ? 1 : 0;
const outerR = radius + barWidth / 2;
const innerR = Math.max(0, radius - barWidth / 2);
if (innerR <= 0) {
return ''; // cannot draw arc with 0 inner radius
}
// get points for both an inner and outer arc. we draw
// the arc entirely with a path's fill instead of using stroke
// so that it can be used as a clip-path.
const ox1 = centerX + outerR * Math.cos(startRadians);
const oy1 = centerY + outerR * Math.sin(startRadians);
const ox2 = centerX + outerR * Math.cos(endRadians);
const oy2 = centerY + outerR * Math.sin(endRadians);
const ix1 = centerX + innerR * Math.cos(startRadians);
const iy1 = centerY + innerR * Math.sin(startRadians);
const ix2 = centerX + innerR * Math.cos(endRadians);
const iy2 = centerY + innerR * Math.sin(endRadians);
// calculate the cap width in case we're drawing rounded bars
const capR = barWidth / 2;
const pathParts = [
// start at outer start
'M',
ox1,
oy1,
// outer arc from start to end (clockwise)
'A',
outerR,
outerR,
0,
largeArc,
1,
ox2,
oy2,
];
if (roundedBars) {
// rounded end cap: small arc connecting outer end to inner end
pathParts.push('A', capR, capR, 0, 0, 1, ix2, iy2);
} else {
// straight line to inner end (square butt)
pathParts.push('L', ix2, iy2);
}
// inner arc from end back to start (counter-clockwise)
pathParts.push('A', innerR, innerR, 0, largeArc, 0, ix1, iy1);
if (roundedBars) {
// rounded start cap: small arc connecting inner start back to outer start
pathParts.push('A', capR, capR, 0, 0, 1, ox1, oy1);
} else {
// straight line back to outer start (square butt)
pathParts.push('L', ox1, oy1);
}
pathParts.push('Z');
return pathParts.join(' ');
}
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 / 1.5 / segmentCount, 2);
return segmentSpacing * maxAngleBetweenSegments;
}
export function getOptimalSegmentCount(
dimensions: RadialGaugeDimensions,
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);
}
+2 -1
View File
@@ -11,6 +11,7 @@ import (
"iter"
"strings"
"text/template"
"time"
"github.com/google/uuid"
"github.com/grafana/grafana/pkg/storage/unified/sql/db"
@@ -556,7 +557,7 @@ func (k *sqlKV) BatchDelete(ctx context.Context, section string, keys []string)
}
func (k *sqlKV) UnixTimestamp(ctx context.Context) (int64, error) {
panic("not implemented!")
return time.Now().Unix(), nil
}
func closeRows[T any](rows db.Rows, yield func(T, error) bool) {
+140 -124
View File
@@ -148,13 +148,15 @@ func runTestKVGet(t *testing.T, kv resource.KV, nsPrefix string) {
func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
ctx := testutil.NewTestContext(t, time.Now().Add(30*time.Second))
nsPrefix += "-save"
t.Run("save new key", func(t *testing.T) {
newKey := namespacedKey(nsPrefix, "new-key")
testValue := "new test value"
saveKVHelper(t, kv, ctx, testSection, "new-key", strings.NewReader(testValue))
saveKVHelper(t, kv, ctx, testSection, newKey, strings.NewReader(testValue))
// Verify it was saved
reader, err := kv.Get(ctx, testSection, "new-key")
reader, err := kv.Get(ctx, testSection, newKey)
require.NoError(t, err)
value, err := io.ReadAll(reader)
@@ -165,15 +167,17 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
})
t.Run("save overwrite existing key", func(t *testing.T) {
overwriteKey := namespacedKey(nsPrefix, "overwrite-key")
// First save
saveKVHelper(t, kv, ctx, testSection, "overwrite-key", strings.NewReader("old value"))
saveKVHelper(t, kv, ctx, testSection, overwriteKey, strings.NewReader("old value"))
// Overwrite
newValue := "new value"
saveKVHelper(t, kv, ctx, testSection, "overwrite-key", strings.NewReader(newValue))
saveKVHelper(t, kv, ctx, testSection, overwriteKey, strings.NewReader(newValue))
// Verify it was updated
reader, err := kv.Get(ctx, testSection, "overwrite-key")
reader, err := kv.Get(ctx, testSection, overwriteKey)
require.NoError(t, err)
value, err := io.ReadAll(reader)
@@ -185,15 +189,17 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
t.Run("save overwrite existing key (datastore)", func(t *testing.T) {
section := "unified/data"
overwriteKey := namespacedKey(nsPrefix, "overwrite-key")
// First save
saveKVHelper(t, kv, ctx, section, "overwrite-key", strings.NewReader("old value"))
saveKVHelper(t, kv, ctx, section, overwriteKey, strings.NewReader("old value"))
// Overwrite
newValue := "new value"
saveKVHelper(t, kv, ctx, section, "overwrite-key", strings.NewReader(newValue))
saveKVHelper(t, kv, ctx, section, overwriteKey, strings.NewReader(newValue))
// Verify it was updated
reader, err := kv.Get(ctx, section, "overwrite-key")
reader, err := kv.Get(ctx, section, overwriteKey)
require.NoError(t, err)
value, err := io.ReadAll(reader)
@@ -210,11 +216,13 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
})
t.Run("save binary data", func(t *testing.T) {
binaryKey := namespacedKey(nsPrefix, "binary-key")
binaryData := []byte{0x00, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD}
saveKVHelper(t, kv, ctx, testSection, "binary-key", bytes.NewReader(binaryData))
saveKVHelper(t, kv, ctx, testSection, binaryKey, bytes.NewReader(binaryData))
// Verify binary data
reader, err := kv.Get(ctx, testSection, "binary-key")
reader, err := kv.Get(ctx, testSection, binaryKey)
require.NoError(t, err)
value, err := io.ReadAll(reader)
@@ -225,11 +233,13 @@ func runTestKVSave(t *testing.T, kv resource.KV, nsPrefix string) {
})
t.Run("save key with no data", func(t *testing.T) {
emptyKey := namespacedKey(nsPrefix, "empty-key")
// Save a key with empty data
saveKVHelper(t, kv, ctx, testSection, "empty-key", strings.NewReader(""))
saveKVHelper(t, kv, ctx, testSection, emptyKey, strings.NewReader(""))
// Verify it was saved with empty data
reader, err := kv.Get(ctx, testSection, "empty-key")
reader, err := kv.Get(ctx, testSection, emptyKey)
require.NoError(t, err)
value, err := io.ReadAll(reader)
@@ -495,128 +505,134 @@ func runTestKVKeysWithSort(t *testing.T, kv resource.KV, nsPrefix string) {
func runTestKVConcurrent(t *testing.T, kv resource.KV, nsPrefix string) {
ctx := testutil.NewTestContext(t, time.Now().Add(60*time.Second))
section := nsPrefix + "-concurrent"
nsPrefix += "-concurrent"
t.Run("concurrent save and get operations", func(t *testing.T) {
const numGoroutines = 10
const numOperations = 20
// Test concurrent operations for both sections, as they have different behaviours
// in the sqlkv implementation.
for _, testSection := range []string{"unified/data", "unified/events"} {
t.Run(testSection, func(t *testing.T) {
t.Run("concurrent save and get operations", func(t *testing.T) {
const numGoroutines = 10
const numOperations = 20
done := make(chan error, numGoroutines)
done := make(chan error, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(goroutineID int) {
var err error
defer func() { done <- err }()
for goroutineID := range numGoroutines {
go func() {
var err error
defer func() { done <- err }()
for j := 0; j < numOperations; j++ {
key := fmt.Sprintf("concurrent-key-%d-%d", goroutineID, j)
value := fmt.Sprintf("concurrent-value-%d-%d", goroutineID, j)
for j := range numOperations {
key := namespacedKey(nsPrefix, fmt.Sprintf("concurrent-key-%d-%d", goroutineID, j))
value := fmt.Sprintf("concurrent-value-%d-%d", goroutineID, j)
// Save
writer, err := kv.Save(ctx, section, key)
if err != nil {
return
}
defer func() {
err := writer.Close()
require.NoError(t, err)
// Save
writer, err := kv.Save(ctx, testSection, key)
if err != nil {
return
}
defer func() {
err := writer.Close()
require.NoError(t, err)
}()
_, err = io.Copy(writer, strings.NewReader(value))
if err != nil {
return
}
err = writer.Close()
if err != nil {
return
}
// Get immediately
reader, err := kv.Get(ctx, testSection, key)
if err != nil {
return
}
readValue, err := io.ReadAll(reader)
require.NoError(t, err)
err = reader.Close()
require.NoError(t, err)
assert.Equal(t, value, string(readValue))
}
}()
_, err = io.Copy(writer, strings.NewReader(value))
if err != nil {
return
}
err = writer.Close()
if err != nil {
return
}
}
// Get immediately
reader, err := kv.Get(ctx, section, key)
if err != nil {
return
}
readValue, err := io.ReadAll(reader)
// Wait for all goroutines to complete
for range numGoroutines {
err := <-done
require.NoError(t, err)
err = reader.Close()
}
})
t.Run("concurrent save, delete, and list operations", func(t *testing.T) {
const numGoroutines = 5
done := make(chan error, numGoroutines)
for i := range numGoroutines {
go func(goroutineID int) {
var err error
defer func() { done <- err }()
key := namespacedKey(nsPrefix, fmt.Sprintf("concurrent-ops-key-%d", goroutineID))
value := fmt.Sprintf("concurrent-ops-value-%d", goroutineID)
// Save
writer, err := kv.Save(ctx, testSection, key)
if err != nil {
return
}
defer func() {
err := writer.Close()
require.NoError(t, err)
}()
_, err = io.Copy(writer, strings.NewReader(value))
if err != nil {
return
}
err = writer.Close()
if err != nil {
return
}
// List to verify it exists
found := false
for k, err := range kv.Keys(ctx, testSection, resource.ListOptions{}) {
if err != nil {
return
}
if k == key {
found = true
break
}
}
if !found {
err = fmt.Errorf("key %s not found in list", key)
return
}
// Delete
err = kv.Delete(ctx, testSection, key)
if err != nil {
return
}
// Verify it's deleted
_, err = kv.Get(ctx, testSection, key)
require.ErrorIs(t, resource.ErrNotFound, err)
err = nil // Expected error, so clear it
}(i)
}
// Wait for all goroutines to complete
for range numGoroutines {
err := <-done
require.NoError(t, err)
assert.Equal(t, value, string(readValue))
}
}(i)
}
// Wait for all goroutines to complete
for i := 0; i < numGoroutines; i++ {
err := <-done
require.NoError(t, err)
}
})
t.Run("concurrent save, delete, and list operations", func(t *testing.T) {
const numGoroutines = 5
done := make(chan error, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(goroutineID int) {
var err error
defer func() { done <- err }()
key := fmt.Sprintf("concurrent-ops-key-%d", goroutineID)
value := fmt.Sprintf("concurrent-ops-value-%d", goroutineID)
// Save
writer, err := kv.Save(ctx, section, key)
if err != nil {
return
}
defer func() {
err := writer.Close()
require.NoError(t, err)
}()
_, err = io.Copy(writer, strings.NewReader(value))
if err != nil {
return
}
err = writer.Close()
if err != nil {
return
}
// List to verify it exists
found := false
for k, err := range kv.Keys(ctx, section, resource.ListOptions{}) {
if err != nil {
return
}
if k == key {
found = true
break
}
}
if !found {
err = fmt.Errorf("key %s not found in list", key)
return
}
// Delete
err = kv.Delete(ctx, section, key)
if err != nil {
return
}
// Verify it's deleted
_, err = kv.Get(ctx, section, key)
require.ErrorIs(t, resource.ErrNotFound, err)
err = nil // Expected error, so clear it
}(i)
}
// Wait for all goroutines to complete
for i := 0; i < numGoroutines; i++ {
err := <-done
require.NoError(t, err)
}
})
})
})
}
}
func runTestKVUnixTimestamp(t *testing.T, kv resource.KV, nsPrefix string) {
+1 -7
View File
@@ -44,11 +44,5 @@ func TestSQLKV(t *testing.T) {
kv, err := resource.NewSQLKV(eDB)
require.NoError(t, err)
return kv
}, &KVTestOptions{
NSPrefix: "sql-kv-test",
SkipTests: map[string]bool{
TestKVConcurrent: true,
TestKVUnixTimestamp: true,
},
})
}, &KVTestOptions{NSPrefix: "sql-kv-test"})
}
@@ -1,25 +1,24 @@
import { css } from '@emotion/css';
import { useState, useMemo } from 'react';
import { useMemo, useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data';
import { t, Trans } from '@grafana/i18n';
import {
Stack,
Tab,
TabsBar,
TabContent,
Icon,
Alert,
Badge,
Text,
useStyles2,
Icon,
InteractiveTable,
ScrollContainer,
Alert,
Spinner,
IconButton,
Stack,
Tab,
TabContent,
TabsBar,
Text,
useStyles2,
} from '@grafana/ui';
import { SQLSchemas, SQLSchemaField, SQLSchemaData } from '../hooks/useSQLSchemas';
import { SQLSchemaData, SQLSchemaField, SQLSchemas } from '../hooks/useSQLSchemas';
import { getFieldTypeIcon } from './utils';
@@ -33,10 +32,9 @@ interface SchemaInspectorPanelProps {
schemas: SQLSchemas | null;
loading: boolean;
error: Error | null;
onClose: () => void;
}
export const SchemaInspectorPanel = ({ schemas, loading, error, onClose }: SchemaInspectorPanelProps) => {
export const SchemaInspectorPanel = ({ schemas, loading, error }: SchemaInspectorPanelProps) => {
const styles = useStyles2(getStyles);
const schemaResponse: SQLSchemas = schemas ?? {};
@@ -192,32 +190,21 @@ export const SchemaInspectorPanel = ({ schemas, loading, error, onClose }: Schem
};
return (
<div className={styles.schemaInspector}>
<div className={styles.tabsBarWrapper}>
{refIds.length > 0 && (
<TabsBar>
{refIds.map((refId) => (
<Tab
key={refId}
label={refId}
active={activeSchemaTab === refId}
onChangeTab={() => setSelectedTab(refId)}
/>
))}
</TabsBar>
)}
<IconButton
name="times"
onClick={onClose}
tooltip={t('expressions.sql-schema.close-schema-inspector', 'Close schema inspector')}
aria-label={t(
'expressions.schema-inspector-panel.aria-label-close-schema-inspector',
'Close schema inspector'
)}
/>
</div>
<>
{refIds.length > 0 && (
<TabsBar>
{refIds.map((refId) => (
<Tab
key={refId}
label={refId}
active={activeSchemaTab === refId}
onChangeTab={() => setSelectedTab(refId)}
/>
))}
</TabsBar>
)}
{renderContent()}
</div>
</>
);
};
@@ -225,21 +212,6 @@ const getStyles = (theme: GrafanaTheme2) => ({
schemaInfoContainer: css({
padding: theme.spacing(1),
}),
schemaInspector: css({
height: '100%',
display: 'flex',
flexDirection: 'column',
}),
// Unfortunate hack to get the close button to align with the tabs since we need to
// override the default styles of the TabsBar component.
tabsBarWrapper: css({
flexShrink: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
padding: `0 ${theme.spacing(1)}`,
}),
tableCell: css({
fontSize: theme.typography.bodySmall.fontSize,
fontWeight: theme.typography.fontWeightMedium,
@@ -247,10 +219,8 @@ const getStyles = (theme: GrafanaTheme2) => ({
}),
tableContainer: css({
margin: theme.spacing(1),
flex: 1,
overflowY: 'auto',
overflowX: 'auto',
minHeight: 0, // Allow flex child to shrink
border: `1px solid ${theme.colors.border.medium}`,
borderRadius: theme.shape.radius.default,
backgroundColor: theme.colors.background.primary,
@@ -1,4 +1,4 @@
import { render, waitFor, fireEvent, act, testWithFeatureToggles } from 'test/test-utils';
import { act, fireEvent, render, testWithFeatureToggles } from 'test/test-utils';
import { ExpressionQuery, ExpressionQueryType } from '../../types';
@@ -127,78 +127,6 @@ describe('SqlExpr with GenAI features', () => {
queries: [],
};
it('renders GenAI buttons with empty expression', async () => {
const customProps = { ...defaultProps, query: { ...defaultProps.query, expression: '' } };
const { findByText } = render(<SqlExpr {...customProps} />);
expect(await findByText('Generate suggestion')).toBeInTheDocument();
expect(await findByText('Explain query')).toBeInTheDocument();
});
it('renders GenAI buttons with non-empty expression', async () => {
const { findByText } = render(<SqlExpr {...defaultProps} />);
expect(await findByText('Improve query')).toBeInTheDocument();
expect(await findByText('Explain query')).toBeInTheDocument();
});
it('renders "Improve query" when currentQuery differs from initialQuery', async () => {
const customProps = {
...defaultProps,
query: { ...defaultProps.query, expression: 'SELECT * FROM A WHERE value > 10' },
};
const { findByText } = render(<SqlExpr {...customProps} />);
expect(await findByText('Improve query')).toBeInTheDocument();
});
it('renders View explanation button when shouldShowViewExplanation is true', async () => {
const { useSQLExplanations } = require('./GenAI/hooks/useSQLExplanations');
useSQLExplanations.mockImplementation((currentExpression: string) => ({
shouldShowViewExplanation: true,
}));
const { findByText } = render(<SqlExpr {...defaultProps} />);
expect(await findByText('View explanation')).toBeInTheDocument();
});
it('renders Explain query button when shouldShowViewExplanation is false', async () => {
const { useSQLExplanations } = require('./GenAI/hooks/useSQLExplanations');
useSQLExplanations.mockImplementation((currentExpression: string) => ({
shouldShowViewExplanation: false,
}));
const { findByText } = render(<SqlExpr {...defaultProps} />);
expect(await findByText('Explain query')).toBeInTheDocument();
});
it('renders SuggestionsDrawerButton when there are suggestions', async () => {
const { useSQLSuggestions } = require('./GenAI/hooks/useSQLSuggestions');
useSQLSuggestions.mockImplementation(() => ({ suggestions: ['suggestion1', 'suggestion2'] }));
const { findByTestId } = render(<SqlExpr {...defaultProps} />);
expect(await findByTestId('suggestions-badge')).toBeInTheDocument();
});
it('does not render SuggestionsDrawerButton when there are no suggestions', async () => {
const { useSQLSuggestions } = require('./GenAI/hooks/useSQLSuggestions');
useSQLSuggestions.mockImplementation(() => ({ suggestions: [] }));
const { queryByTestId } = render(<SqlExpr {...defaultProps} />);
expect(await waitFor(() => queryByTestId('suggestions-badge'))).not.toBeInTheDocument();
});
it('calls handleOpenExplanation when View explanation is clicked', async () => {
const { useSQLExplanations } = require('./GenAI/hooks/useSQLExplanations');
const mockHandleOpen = jest.fn();
useSQLExplanations.mockImplementation(() => ({
shouldShowViewExplanation: true,
handleOpenExplanation: mockHandleOpen,
}));
const { findByText } = render(<SqlExpr {...defaultProps} />);
const button = await findByText('View explanation');
fireEvent.click(button);
expect(mockHandleOpen).toHaveBeenCalled();
});
it('renders suggestions drawer when isDrawerOpen is true', async () => {
const { useSQLSuggestions } = require('./GenAI/hooks/useSQLSuggestions');
useSQLSuggestions.mockImplementation(() => ({
@@ -245,26 +173,26 @@ describe('Schema Inspector feature toggle', () => {
});
it('closes panel and shows reopen button when close button clicked', async () => {
const { queryByText, getByLabelText, findByText } = render(<SqlExpr {...defaultProps} />);
const { queryByText, getByText, findByText } = render(<SqlExpr {...defaultProps} />);
expect(queryByText('No schema information available')).toBeInTheDocument();
const closeButton = getByLabelText('Close schema inspector');
const closeButton = getByText('Schema inspector');
await act(async () => fireEvent.click(closeButton));
expect(queryByText('No schema information available')).not.toBeInTheDocument();
expect(await findByText('Inspect schema')).toBeInTheDocument();
expect(await findByText('Schema inspector')).toBeInTheDocument();
});
it('reopens panel when inspect schema button clicked after closing', async () => {
const { queryByText, getByLabelText, getByText } = render(<SqlExpr {...defaultProps} />);
it('reopens panel when Open schema inspector button clicked after closing', async () => {
const { queryByText, getByText } = render(<SqlExpr {...defaultProps} />);
const closeButton = getByLabelText('Close schema inspector');
const closeButton = getByText('Schema inspector');
await act(async () => fireEvent.click(closeButton));
expect(queryByText('No schema information available')).not.toBeInTheDocument();
const reopenButton = getByText('Inspect schema');
const reopenButton = getByText('Schema inspector');
await act(async () => fireEvent.click(reopenButton));
expect(queryByText('No schema information available')).toBeInTheDocument();
@@ -300,7 +228,7 @@ describe('Schema Inspector feature toggle', () => {
it('does not render panel or button', () => {
const { queryByText } = render(<SqlExpr {...defaultProps} />);
expect(queryByText('Inspect schema')).not.toBeInTheDocument();
expect(queryByText('Schema inspector')).not.toBeInTheDocument();
expect(queryByText('No schema information available')).not.toBeInTheDocument();
});
});
@@ -1,15 +1,15 @@
import { css, cx } from '@emotion/css';
import { useMemo, useRef, useEffect, useState, lazy, Suspense, useCallback } from 'react';
import { lazy, Suspense, useCallback, useEffect, useMemo, useState } from 'react';
import { useMeasure } from 'react-use';
import AutoSizer from 'react-virtualized-auto-sizer';
import { SelectableValue, GrafanaTheme2 } from '@grafana/data';
import { t, Trans } from '@grafana/i18n';
import { SQLEditor, CompletionItemKind, LanguageDefinition, TableIdentifier } from '@grafana/plugin-ui';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { Trans } from '@grafana/i18n';
import { CompletionItemKind, LanguageDefinition, SQLEditor, TableIdentifier } from '@grafana/plugin-ui';
import { reportInteraction } from '@grafana/runtime';
import { DataQuery } from '@grafana/schema/dist/esm/index';
import { formatSQL } from '@grafana/sql';
import { useStyles2, Stack, Button, Modal } from '@grafana/ui';
import { Button, Stack, useStyles2 } from '@grafana/ui';
import { ExpressionQueryEditorProps } from '../../ExpressionQueryEditor';
import { SqlExpressionQuery } from '../../types';
@@ -20,27 +20,10 @@ import { getSqlCompletionProvider } from './CompletionProvider/sqlCompletionProv
import { useSQLExplanations } from './GenAI/hooks/useSQLExplanations';
import { useSQLSuggestions } from './GenAI/hooks/useSQLSuggestions';
import { SchemaInspectorPanel } from './SchemaInspector/SchemaInspectorPanel';
import { SqlExprContextValue, SqlExprProvider } from './SqlExprContext';
import { SqlQueryActions } from './SqlQueryActions';
import { useSQLSchemas } from './hooks/useSQLSchemas';
// Lazy load the GenAI components to avoid circular dependencies
const GenAISQLSuggestionsButton = lazy(() =>
import('./GenAI/GenAISQLSuggestionsButton').then((module) => ({
default: module.GenAISQLSuggestionsButton,
}))
);
const GenAISQLExplainButton = lazy(() =>
import('./GenAI/GenAISQLExplainButton').then((module) => ({
default: module.GenAISQLExplainButton,
}))
);
const SuggestionsDrawerButton = lazy(() =>
import('./GenAI/SuggestionsDrawerButton').then((module) => ({
default: module.SuggestionsDrawerButton,
}))
);
const GenAISuggestionsDrawer = lazy(() =>
import('./GenAI/GenAISuggestionsDrawer').then((module) => ({
default: module.GenAISuggestionsDrawer,
@@ -55,7 +38,6 @@ const GenAIExplanationDrawer = lazy(() =>
// Account for Monaco editor's border to prevent clipping
const EDITOR_BORDER_ADJUSTMENT = 2; // 1px border on top and bottom
const EDITOR_HEIGHT = 300;
export interface SqlExprProps {
refIds: Array<SelectableValue<string>>;
@@ -93,14 +75,10 @@ FROM
LIMIT
10`;
const [dimensions, setDimensions] = useState({ height: 0 });
const styles = useStyles2((theme) => getStyles(theme, dimensions.height || EDITOR_HEIGHT));
const containerRef = useRef<HTMLDivElement>(null);
const [toolboxRef, toolboxMeasure] = useMeasure<HTMLDivElement>();
const [isExpanded, setIsExpanded] = useState(false);
const [isSchemaInspectorOpen, setIsSchemaInspectorOpen] = useState(true);
const { handleApplySuggestion, handleHistoryUpdate, handleCloseDrawer, handleOpenDrawer, isDrawerOpen, suggestions } =
const styles = useStyles2((theme) => getStyles(theme));
const { handleApplySuggestion, handleCloseDrawer, handleHistoryUpdate, handleOpenDrawer, isDrawerOpen, suggestions } =
useSQLSuggestions();
const {
@@ -195,21 +173,6 @@ LIMIT
}
}, [onRunQuery, refetchSchemas, isSchemaInspectorOpen]);
// Set up resize observer to handle container resizing
useEffect(() => {
if (!containerRef.current) {
return;
}
const resizeObserver = new ResizeObserver((entries) => {
const { height } = entries[0].contentRect;
setDimensions({ height });
});
resizeObserver.observe(containerRef.current);
return () => resizeObserver.disconnect();
}, []);
useEffect(() => {
// Call the onChange method once so we have access to the initial query in consuming components
// But only if expression is empty
@@ -236,168 +199,122 @@ LIMIT
return () => document.removeEventListener('keydown', handleKeyDown, true);
}, [executeQuery]);
const renderToolbox = (formatQuery: () => void) => (
<div ref={toolboxRef}>
<QueryToolbox query={query} onFormatCode={formatQuery} onExpand={setIsExpanded} isExpanded={isExpanded} />
</div>
);
const contextValue: SqlExprContextValue = {
// Explanations
explanation,
isExplanationOpen,
shouldShowViewExplanation,
handleExplain,
handleOpenExplanation,
handleCloseExplanation,
// Suggestions
suggestions,
isDrawerOpen,
handleHistoryUpdate,
handleApplySuggestion,
handleOpenDrawer,
handleCloseDrawer,
};
const renderSQLButtons = () => (
<div className={styles.sqlButtons}>
<Stack direction="row" gap={1} alignItems="center" justifyContent="end">
{isSchemasFeatureEnabled && !isSchemaInspectorOpen && (
<Button
icon="table-expand-all"
onClick={() => setIsSchemaInspectorOpen(true)}
size="sm"
variant="secondary"
fill="outline"
>
<Trans i18nKey="expressions.sql-schema.inspect-button">Inspect schema</Trans>
</Button>
)}
<Button icon="play" onClick={executeQuery} size="sm">
{t('expressions.sql-expr.button-run-query', 'Run query')}
</Button>
<Suspense fallback={null}>
{shouldShowViewExplanation ? (
<Button
fill="outline"
icon="gf-movepane-right"
onClick={handleOpenExplanation}
size="sm"
variant="secondary"
>
<Trans i18nKey="sql-expressions.view-explanation">View explanation</Trans>
</Button>
) : (
<GenAISQLExplainButton
currentQuery={query.expression || ''}
onExplain={handleExplain}
queryContext={queryContext}
refIds={vars}
// schemas={schemas} // Will be added when schema extraction is implemented
/>
)}
</Suspense>
<Suspense fallback={null}>
<GenAISQLSuggestionsButton
currentQuery={query.expression || ''}
initialQuery={initialQuery}
onGenerate={() => {}} // Noop - history is managed via onHistoryUpdate
onHistoryUpdate={handleHistoryUpdate}
queryContext={queryContext}
refIds={vars}
errorContext={errorContext} // Will be added when error tracking is implemented
// schemas={schemas} // Will be added when schema extraction is implemented
/>
</Suspense>
</Stack>
{suggestions.length > 0 && (
<Suspense fallback={null}>
<SuggestionsDrawerButton handleOpenDrawer={handleOpenDrawer} suggestions={suggestions} />
</Suspense>
)}
</div>
);
const renderSQLEditor = (width?: number, height?: number) => (
<>
<div className={styles.sqlContainer}>
{renderSQLButtons()}
<div
className={cx(styles.contentContainer, {
[styles.contentContainerWithSchema]: isSchemaInspectorOpen && isSchemasFeatureEnabled,
})}
const renderButtons = () => (
<Stack direction="row" alignItems="center" justifyContent="space-between" wrap>
<SqlQueryActions
executeQuery={executeQuery}
currentQuery={query.expression || ''}
queryContext={queryContext}
refIds={vars}
initialQuery={initialQuery}
errorContext={errorContext}
/>
{isSchemasFeatureEnabled && (
<Button
icon={isSchemaInspectorOpen ? 'eye' : 'eye-slash'}
onClick={() => setIsSchemaInspectorOpen(!isSchemaInspectorOpen)}
size="sm"
variant="secondary"
fill="outline"
>
<div ref={containerRef} className={styles.editorContainer}>
<Trans i18nKey="expressions.sql-schema.schema-inspector">Schema inspector</Trans>
</Button>
)}
</Stack>
);
const renderMainContent = () => (
<div
className={cx(styles.contentContainer, {
[styles.contentContainerWithSchema]: isSchemaInspectorOpen && isSchemasFeatureEnabled,
})}
>
<div className={styles.editorContainer}>
<AutoSizer>
{({ width, height }) => (
<SQLEditor
query={query.expression || initialQuery}
onChange={onEditorChange}
width={width}
height={height ?? dimensions.height - EDITOR_BORDER_ADJUSTMENT - toolboxMeasure.height}
language={EDITOR_LANGUAGE_DEFINITION}
width={width}
height={height - EDITOR_BORDER_ADJUSTMENT - toolboxMeasure.height}
>
{({ formatQuery }) => renderToolbox(formatQuery)}
{({ formatQuery }) => (
<div ref={toolboxRef}>
<QueryToolbox query={query} onFormatCode={formatQuery} />
</div>
)}
</SQLEditor>
</div>
{isSchemaInspectorOpen && isSchemasFeatureEnabled && (
<div className={`${styles.schemaInspector} ${isSchemaInspectorOpen ? styles.schemaInspectorOpen : ''}`}>
<SchemaInspectorPanel
schemas={schemas?.sqlSchemas ?? null}
loading={schemasLoading}
error={schemasError}
onClose={() => setIsSchemaInspectorOpen(false)}
/>
</div>
)}
</div>
</AutoSizer>
</div>
<Suspense fallback={null}>
<GenAISuggestionsDrawer
isOpen={isDrawerOpen}
onApplySuggestion={onApplySuggestion}
onClose={handleCloseDrawer}
suggestions={suggestions}
/>
</Suspense>
<Suspense fallback={null}>
<GenAIExplanationDrawer isOpen={isExplanationOpen} onClose={handleCloseExplanation} explanation={explanation} />
</Suspense>
</>
{isSchemaInspectorOpen && isSchemasFeatureEnabled && (
<div className={styles.schemaInspector}>
<SchemaInspectorPanel schemas={schemas?.sqlSchemas ?? null} loading={schemasLoading} error={schemasError} />
</div>
)}
</div>
);
const renderStandaloneEditor = () => (
<AutoSizer>
{({ width, height }) => (
<SQLEditor
query={query.expression || initialQuery}
onChange={onEditorChange}
width={width}
height={height ? height - EDITOR_BORDER_ADJUSTMENT - toolboxMeasure.height : undefined}
language={EDITOR_LANGUAGE_DEFINITION}
>
{({ formatQuery }) => renderToolbox(formatQuery)}
</SQLEditor>
)}
</AutoSizer>
const renderSQLEditor = () => (
<Stack direction="column" gap={1}>
{renderButtons()}
{renderMainContent()}
</Stack>
);
return (
<>
{renderSQLEditor()}
{isExpanded && (
<Modal
title={t('expressions.sql-expr.modal-title', 'SQL Editor')}
closeOnBackdropClick={false}
closeOnEscape={false}
className={styles.modal}
contentClassName={styles.modalContent}
isOpen={isExpanded}
onDismiss={() => setIsExpanded(false)}
>
{renderStandaloneEditor()}
</Modal>
)}
</>
<SqlExprProvider value={contextValue}>
<div className={styles.mainContainer}>
{renderSQLEditor()}
<Suspense fallback={null}>
<GenAISuggestionsDrawer
isOpen={isDrawerOpen}
onApplySuggestion={onApplySuggestion}
onClose={handleCloseDrawer}
suggestions={suggestions}
/>
</Suspense>
<Suspense fallback={null}>
<GenAIExplanationDrawer
isOpen={isExplanationOpen}
onClose={handleCloseExplanation}
explanation={explanation}
/>
</Suspense>
</div>
</SqlExprProvider>
);
};
const getStyles = (theme: GrafanaTheme2, editorHeight: number) => ({
sqlContainer: css({
display: 'grid',
gap: theme.spacing(1),
gridTemplateRows: 'auto 1fr',
gridTemplateAreas: `
"buttons"
"content"
`,
const getStyles = (theme: GrafanaTheme2) => ({
mainContainer: css({
marginTop: theme.spacing(0.5),
}),
contentContainer: css({
gridArea: 'content',
minHeight: '250px',
height: '100%',
resize: 'vertical',
overflow: 'hidden',
display: 'grid',
gap: theme.spacing(1),
gridTemplateColumns: '1fr 0fr',
gridTemplateAreas: '"editor schema"',
[theme.transitions.handleMotion('no-preference')]: {
@@ -408,67 +325,22 @@ const getStyles = (theme: GrafanaTheme2, editorHeight: number) => ({
}),
contentContainerWithSchema: css({
gridTemplateColumns: '1fr 1fr',
gap: theme.spacing(1),
}),
editorContainer: css({
gridArea: 'editor',
height: editorHeight, // Use dynamic height from ResizeObserver
resize: 'vertical',
overflow: 'auto',
minHeight: '100px',
}),
modal: css({
width: '95vw',
height: '95vh',
}),
modalContent: css({
height: '100%',
paddingTop: 0,
}),
// This is NOT ideal. The alternative is to expose SQL buttons as a separate component,
// Then consume them in ExpressionQueryEditor. This requires a lot of refactoring and
// can be prioritized later.
sqlButtons: css({
gridArea: 'buttons',
justifySelf: 'end',
transform: `translateY(${theme.spacing(-4)})`,
marginBottom: theme.spacing(-4), // Prevent affecting editor position
zIndex: 10, // Ensure buttons appear above other elements
position: 'relative', // Required for z-index to work
display: 'flex',
alignItems: 'center',
gap: theme.spacing(1),
width: '100%',
overflow: 'auto',
}),
schemaInspector: css({
gridArea: 'schema',
height: editorHeight,
height: '100%',
overflow: 'hidden',
minWidth: 0,
}),
schemaInspectorOpen: css({
border: `1px solid ${theme.colors.border.weak}`,
borderRadius: theme.shape.radius.default,
}),
schemaFields: css({
display: 'flex',
flexWrap: 'wrap',
gap: theme.spacing(1),
padding: theme.spacing(1),
maxHeight: '120px',
overflowY: 'auto',
}),
fieldItem: css({
display: 'flex',
alignItems: 'center',
gap: theme.spacing(0.5),
padding: theme.spacing(1),
backgroundColor: theme.colors.background.secondary,
borderRadius: theme.shape.radius.default,
border: `1px solid ${theme.colors.border.weak}`,
fontSize: theme.typography.bodySmall.fontSize,
}),
responseContainer: css({
padding: theme.spacing(2),
}),
});
async function fetchFields(identifier: TableIdentifier, queries: DataQuery[]) {
@@ -0,0 +1,88 @@
import { render, screen } from 'test/test-utils';
import { SqlExprContextValue, SqlExprProvider, useSqlExprContext } from './SqlExprContext';
describe('SqlExprContext', () => {
const mockContextValue: SqlExprContextValue = {
explanation: 'Test explanation',
isExplanationOpen: false,
shouldShowViewExplanation: false,
handleExplain: jest.fn(),
handleOpenExplanation: jest.fn(),
handleCloseExplanation: jest.fn(),
suggestions: ['suggestion1', 'suggestion2'],
isDrawerOpen: false,
handleHistoryUpdate: jest.fn(),
handleApplySuggestion: jest.fn(),
handleOpenDrawer: jest.fn(),
handleCloseDrawer: jest.fn(),
};
describe('SqlExprProvider', () => {
it('renders children correctly', () => {
render(
<SqlExprProvider value={mockContextValue}>
<div>Test Child</div>
</SqlExprProvider>
);
expect(screen.getByText('Test Child')).toBeInTheDocument();
});
it('provides context value to children', () => {
const TestConsumer = () => {
const context = useSqlExprContext();
return <div>{context.explanation}</div>;
};
render(
<SqlExprProvider value={mockContextValue}>
<TestConsumer />
</SqlExprProvider>
);
expect(screen.getByText('Test explanation')).toBeInTheDocument();
});
});
describe('useSqlExprContext', () => {
it('throws error when used outside provider', () => {
const TestComponent = () => {
useSqlExprContext();
return <div>Should not render</div>;
};
// Suppress console.error for this test
const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
expect(() => {
render(<TestComponent />);
}).toThrow('useSqlExprContext must be used within SqlExprProvider');
consoleSpy.mockRestore();
});
it('returns context value when used inside provider', () => {
const TestComponent = () => {
const context = useSqlExprContext();
return (
<div>
<span>Explanation: {context.explanation}</span>
<span>Suggestions: {context.suggestions.length}</span>
<span>Is Drawer Open: {context.isDrawerOpen.toString()}</span>
</div>
);
};
render(
<SqlExprProvider value={mockContextValue}>
<TestComponent />
</SqlExprProvider>
);
expect(screen.getByText('Explanation: Test explanation')).toBeInTheDocument();
expect(screen.getByText('Suggestions: 2')).toBeInTheDocument();
expect(screen.getByText('Is Drawer Open: false')).toBeInTheDocument();
});
});
});
@@ -0,0 +1,38 @@
import { createContext, useContext, ReactNode } from 'react';
export interface SqlExprContextValue {
// Explanations
explanation: string;
isExplanationOpen: boolean;
shouldShowViewExplanation: boolean;
handleExplain: (explanation: string) => void;
handleOpenExplanation: () => void;
handleCloseExplanation: () => void;
// Suggestions
suggestions: string[];
isDrawerOpen: boolean;
handleHistoryUpdate: (suggestions: string[]) => void;
handleApplySuggestion: (suggestion: string) => string;
handleOpenDrawer: () => void;
handleCloseDrawer: () => void;
}
const SqlExprContext = createContext<SqlExprContextValue | null>(null);
export const useSqlExprContext = () => {
const context = useContext(SqlExprContext);
if (!context) {
throw new Error('useSqlExprContext must be used within SqlExprProvider');
}
return context;
};
interface SqlExprProviderProps {
children: ReactNode;
value: SqlExprContextValue;
}
export const SqlExprProvider = ({ children, value }: SqlExprProviderProps) => {
return <SqlExprContext.Provider value={value}>{children}</SqlExprContext.Provider>;
};
@@ -0,0 +1,137 @@
import { fireEvent, render, waitFor } from 'test/test-utils';
import { SqlExprContextValue } from './SqlExprContext';
import { SqlQueryActions, SqlQueryActionsProps } from './SqlQueryActions';
jest.mock('@grafana/ui', () => ({
...jest.requireActual('@grafana/ui'),
useStyles2: jest.fn().mockImplementation(() => ({})),
}));
// Mock lazy loaded GenAI components
jest.mock('./GenAI/GenAISQLSuggestionsButton', () => ({
GenAISQLSuggestionsButton: ({ currentQuery, initialQuery }: { currentQuery: string; initialQuery: string }) => {
const text = !currentQuery || currentQuery === initialQuery ? 'Generate suggestion' : 'Improve query';
return <div data-testid="suggestions-button">{text}</div>;
},
}));
jest.mock('./GenAI/GenAISQLExplainButton', () => ({
GenAISQLExplainButton: () => <div data-testid="explain-button">Explain query</div>,
}));
jest.mock('./GenAI/SuggestionsDrawerButton', () => ({
SuggestionsDrawerButton: () => <div data-testid="suggestions-badge">Suggestions Badge</div>,
}));
// Mock SqlExprContext
const mockContextValue: SqlExprContextValue = {
handleOpenExplanation: jest.fn(),
shouldShowViewExplanation: false,
handleExplain: jest.fn(),
handleHistoryUpdate: jest.fn(),
handleOpenDrawer: jest.fn(),
suggestions: [],
explanation: '',
isExplanationOpen: false,
isDrawerOpen: false,
handleApplySuggestion: jest.fn(),
handleCloseDrawer: jest.fn(),
handleCloseExplanation: jest.fn(),
};
jest.mock('./SqlExprContext', () => ({
useSqlExprContext: () => mockContextValue,
SqlExprProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));
describe('SqlQueryActions', () => {
const defaultProps: SqlQueryActionsProps = {
executeQuery: jest.fn(),
currentQuery: `SELECT * FROM A LIMIT 10`,
queryContext: {},
refIds: ['A'],
initialQuery: `SELECT * FROM A LIMIT 10`,
errorContext: [],
};
beforeEach(() => {
jest.clearAllMocks();
// Reset mock context to default values
Object.assign(mockContextValue, {
handleOpenExplanation: jest.fn(),
shouldShowViewExplanation: false,
handleExplain: jest.fn(),
handleHistoryUpdate: jest.fn(),
handleOpenDrawer: jest.fn(),
suggestions: [],
explanation: '',
isExplanationOpen: false,
isDrawerOpen: false,
handleApplySuggestion: jest.fn(),
handleCloseDrawer: jest.fn(),
handleCloseExplanation: jest.fn(),
});
});
it('renders GenAI buttons with empty expression', async () => {
const customProps = { ...defaultProps, currentQuery: '' };
const { findByText } = render(<SqlQueryActions {...customProps} />);
expect(await findByText('Generate suggestion')).toBeInTheDocument();
expect(await findByText('Explain query')).toBeInTheDocument();
});
it('renders GenAI buttons with non-empty expression', async () => {
const { findByText } = render(<SqlQueryActions {...defaultProps} />);
expect(await findByText('Generate suggestion')).toBeInTheDocument();
expect(await findByText('Explain query')).toBeInTheDocument();
});
it('renders "Improve query" when currentQuery differs from initialQuery', async () => {
const customProps = {
...defaultProps,
currentQuery: 'SELECT * FROM A WHERE value > 10',
};
const { findByText } = render(<SqlQueryActions {...customProps} />);
expect(await findByText('Improve query')).toBeInTheDocument();
});
it('renders View explanation button when shouldShowViewExplanation is true', async () => {
mockContextValue.shouldShowViewExplanation = true;
const { findByText } = render(<SqlQueryActions {...defaultProps} />);
expect(await findByText('View explanation')).toBeInTheDocument();
});
it('renders Explain query button when shouldShowViewExplanation is false', async () => {
mockContextValue.shouldShowViewExplanation = false;
const { findByText } = render(<SqlQueryActions {...defaultProps} />);
expect(await findByText('Explain query')).toBeInTheDocument();
});
it('renders SuggestionsDrawerButton when there are suggestions', async () => {
mockContextValue.suggestions = ['suggestion1', 'suggestion2'];
const { findByTestId } = render(<SqlQueryActions {...defaultProps} />);
expect(await findByTestId('suggestions-badge')).toBeInTheDocument();
});
it('does not render SuggestionsDrawerButton when there are no suggestions', async () => {
mockContextValue.suggestions = [];
const { queryByTestId } = render(<SqlQueryActions {...defaultProps} />);
expect(await waitFor(() => queryByTestId('suggestions-badge'))).not.toBeInTheDocument();
});
it('calls handleOpenExplanation when View explanation is clicked', async () => {
const mockHandleOpen = jest.fn();
mockContextValue.shouldShowViewExplanation = true;
mockContextValue.handleOpenExplanation = mockHandleOpen;
const { findByText } = render(<SqlQueryActions {...defaultProps} />);
const button = await findByText('View explanation');
fireEvent.click(button);
expect(mockHandleOpen).toHaveBeenCalled();
});
});
@@ -0,0 +1,91 @@
import { lazy, Suspense } from 'react';
import { t, Trans } from '@grafana/i18n';
import { Button, Stack } from '@grafana/ui';
import { useSqlExprContext } from './SqlExprContext';
// Lazy load the GenAI components to avoid circular dependencies
const GenAISQLSuggestionsButton = lazy(() =>
import('./GenAI/GenAISQLSuggestionsButton').then((module) => ({
default: module.GenAISQLSuggestionsButton,
}))
);
const GenAISQLExplainButton = lazy(() =>
import('./GenAI/GenAISQLExplainButton').then((module) => ({
default: module.GenAISQLExplainButton,
}))
);
const SuggestionsDrawerButton = lazy(() =>
import('./GenAI/SuggestionsDrawerButton').then((module) => ({
default: module.SuggestionsDrawerButton,
}))
);
export interface SqlQueryActionsProps {
executeQuery: () => void;
currentQuery: string;
queryContext: Record<string, unknown>;
refIds: string[];
initialQuery: string;
errorContext: string[];
}
export const SqlQueryActions = ({
executeQuery,
currentQuery,
queryContext,
refIds,
initialQuery,
errorContext,
}: SqlQueryActionsProps) => {
const {
handleOpenExplanation,
shouldShowViewExplanation,
handleExplain,
handleHistoryUpdate,
handleOpenDrawer,
suggestions,
} = useSqlExprContext();
return (
<Stack direction="row" gap={1} alignItems="center" justifyContent="start" wrap>
<Button icon="play" onClick={executeQuery} size="sm">
{t('expressions.sql-expr.button-run-query', 'Run query')}
</Button>
<Suspense fallback={null}>
{shouldShowViewExplanation ? (
<Button fill="outline" icon="gf-movepane-right" onClick={handleOpenExplanation} size="sm" variant="secondary">
<Trans i18nKey="sql-expressions.view-explanation">View explanation</Trans>
</Button>
) : (
<GenAISQLExplainButton
currentQuery={currentQuery}
onExplain={handleExplain}
queryContext={queryContext}
refIds={refIds}
// schemas={schemas} // Will be added when schema extraction is implemented
/>
)}
</Suspense>
<Suspense fallback={null}>
<GenAISQLSuggestionsButton
currentQuery={currentQuery}
initialQuery={initialQuery}
onGenerate={() => {}} // Noop - history is managed via onHistoryUpdate
onHistoryUpdate={handleHistoryUpdate}
queryContext={queryContext}
refIds={refIds}
errorContext={errorContext} // Will be added when error tracking is implemented
// schemas={schemas} // Will be added when schema extraction is implemented
/>
</Suspense>
{suggestions.length > 0 && (
<Suspense fallback={null}>
<SuggestionsDrawerButton handleOpenDrawer={handleOpenDrawer} suggestions={suggestions} />
</Suspense>
)}
</Stack>
);
};
@@ -44,11 +44,6 @@ export function EffectsEditor(props: StandardEditorProps<GaugePanelEffects>) {
value={!!props.value?.gradient}
onChange={(e) => props.onChange({ ...props.value, gradient: e.currentTarget.checked })}
/>
<EffectsEditorInput
label={t('radialbar.config.effects.rounded-bars', 'Rounded bars')}
value={!!props.value?.rounded}
onChange={(e) => props.onChange({ ...props.value, rounded: e.currentTarget.checked })}
/>
<EffectsEditorInput
label={t('radialbar.config.effects.bar-glow', 'Bar glow')}
value={!!props.value?.barGlow}
@@ -59,12 +54,6 @@ export function EffectsEditor(props: StandardEditorProps<GaugePanelEffects>) {
value={!!props.value?.centerGlow}
onChange={(e) => props.onChange({ ...props.value, centerGlow: e.currentTarget.checked })}
/>
<EffectsEditorInput
label={t('radialbar.config.effects.spotlight', 'Spotlight')}
tooltip={t('radialbar.config.effects.spotlight-tooltip', 'Only visible in dark themes')}
value={!!props.value?.spotlight}
onChange={(e) => props.onChange({ ...props.value, spotlight: e.currentTarget.checked })}
/>
</Grid>
);
}
@@ -37,11 +37,10 @@ export function RadialBarPanel({
width={width}
height={height}
barWidthFactor={options.barWidthFactor}
gradient={options.effects?.gradient ? 'auto' : 'none'}
spotlight={options.effects?.spotlight}
gradient={options.effects?.gradient}
glowBar={options.effects?.barGlow}
glowCenter={options.effects?.centerGlow}
roundedBars={options.effects?.rounded}
roundedBars={options.barShape === 'rounded'}
vizCount={valueProps.count}
shape={options.shape}
segmentCount={options.segmentCount}
@@ -51,6 +50,7 @@ export function RadialBarPanel({
alignmentFactors={valueProps.alignmentFactors}
valueManualFontSize={options.text?.valueSize}
nameManualFontSize={options.text?.titleSize}
endpointMarker={options.endpointMarker !== 'none' ? options.endpointMarker : undefined}
onClick={menuProps.openMenu}
/>
);
@@ -69,6 +69,36 @@ export const plugin = new PanelPlugin<Options>(RadialBarPanel)
},
});
builder.addRadio({
path: 'barShape',
name: t('radialbar.config.bar-shape', 'Bar Style'),
category,
defaultValue: defaultOptions.barShape,
settings: {
options: [
{ value: 'flat', label: t('radialbar.config.bar-shape-flat', 'Flat') },
{ value: 'rounded', label: t('radialbar.config.bar-shape-rounded', 'Rounded') },
],
},
showIf: (options) => options.segmentCount === 1,
});
builder.addRadio({
path: 'endpointMarker',
name: t('radialbar.config.endpoint-marker', 'Endpoint marker'),
description: t('radialbar.config.endpoint-marker-description', 'Glow is only supported in dark mode'),
category,
defaultValue: defaultOptions.endpointMarker,
settings: {
options: [
{ value: 'point', label: t('radialbar.config.endpoint-marker-point', 'Point') },
{ value: 'glow', label: t('radialbar.config.endpoint-marker-glow', 'Glow') },
{ value: 'none', label: t('radialbar.config.endpoint-marker-none', 'None') },
],
},
showIf: (options) => options.barShape === 'rounded' && options.segmentCount === 1,
});
builder.addBooleanSwitch({
path: 'sparkline',
name: t('radialbar.config.sparkline', 'Show sparkline'),
@@ -27,10 +27,8 @@ composableKinds: PanelCfg: {
schema: {
GaugePanelEffects: {
barGlow?: bool | *false
spotlight?: bool | *false
rounded?: bool | *false
centerGlow?: bool | *false
gradient?: bool | *true
gradient?: bool | *true
} @cuetsy(kind="interface")
Options: {
@@ -42,6 +40,8 @@ composableKinds: PanelCfg: {
sparkline?: bool | *true
shape: "circle" | *"gauge"
barWidthFactor: number | *0.5
barShape: "flat" | "rounded" | *"flat"
endpointMarker?: "point" | "glow" | "none" | *"point"
effects: GaugePanelEffects | *{}
} @cuetsy(kind="interface")
}
+4 -4
View File
@@ -14,21 +14,19 @@ export interface GaugePanelEffects {
barGlow?: boolean;
centerGlow?: boolean;
gradient?: boolean;
rounded?: boolean;
spotlight?: boolean;
}
export const defaultGaugePanelEffects: Partial<GaugePanelEffects> = {
barGlow: false,
centerGlow: false,
gradient: true,
rounded: false,
spotlight: false,
};
export interface Options extends common.SingleStatBaseOptions {
barShape: ('flat' | 'rounded');
barWidthFactor: number;
effects: GaugePanelEffects;
endpointMarker?: ('point' | 'glow' | 'none');
segmentCount: number;
segmentSpacing: number;
shape: ('circle' | 'gauge');
@@ -38,8 +36,10 @@ export interface Options extends common.SingleStatBaseOptions {
}
export const defaultOptions: Partial<Options> = {
barShape: 'flat',
barWidthFactor: 0.5,
effects: {},
endpointMarker: 'point',
segmentCount: 1,
segmentSpacing: 0.3,
shape: 'gauge',
@@ -18,19 +18,6 @@ const withDefaults = (
}
},
},
// styles: [{
// name: t('gauge.suggestions.style.circular', 'Glowing'),
// options: {
// effects: {
// rounded: true,
// barGlow: true,
// centerGlow: true,
// spotlight: true,
// },
// },
// }, {
// name: t('gauge.suggestions.style.simple', 'Simple'),
// }]
} satisfies VisualizationSuggestion<Options, GraphFieldConfig>);
const MAX_GAUGES = 10;
+82 -53
View File
@@ -2688,6 +2688,35 @@
"text-federated": "Federované",
"text-provisioned": "Zajištěno"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Zdroj dat",
@@ -3749,6 +3778,7 @@
"text": "Nebyly nalezeny žádné výsledky pro váš dotaz"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6222,7 +6252,7 @@
"title-someone-else-has-updated-this-dashboard": "Tuto nástěnku aktualizoval jiný uživatel",
"would-still-dashboard": "Chcete přesto tuto nástěnku uložit?"
},
"save-and-overwrite": "Uložit a přepsat"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7211,6 +7241,9 @@
"time-range-label": "Zamknout časový rozsah"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Pro tip: {{proTip}}"
},
@@ -7691,39 +7724,6 @@
},
"share-span": "Sdílet"
},
"span-filters": {
"aria-label-select-max-span-operator": "Vyberte operátor max. rozsahu",
"aria-label-select-min-span-operator": "Vyberte operátor min. rozsahu",
"aria-label-select-service-name": "Vyberte název služby",
"aria-label-select-service-name-operator": "Vyberte operátor názvu služby",
"aria-label-select-span-name": "Vyberte název rozsahu",
"aria-label-select-span-name-operator": "Vyberte operátor názvu rozsahu",
"ariaLabel-select-max-span-duration": "Vyberte maximální dobu trvání",
"ariaLabel-select-min-span-duration": "Vyberte minimální dobu trvání rozsahu",
"label-collapse": "Filtry rozsahu",
"label-duration": "Doba trvání",
"label-service-name": "Název služby",
"label-span-name": "Název rozsahu",
"label-tags": "Tagy",
"placeholder-all-service-names": "Všechny názvy služby",
"placeholder-all-span-names": "Všechny názvy rozsahu",
"tooltip-collapse": "Filtrujte svá rozpětí níže. Filtry můžete používat tak dlouho, dokud nezúžíte výsledná rozpětí na několik vybraných, které vás nejvíce zajímají.",
"tooltip-duration": "Filtrovat podle doby trvání. Akceptované jednotky jsou {{units}}",
"tooltip-tags": "Filtrujte podle tagů, tagů procesů nebo polí protokolu ve vybraném rozpětí."
},
"span-filters-tags": {
"aria-label-add-tag": "Přidat tag",
"aria-label-input-tag-value": "Vstupní hodnota tagu",
"aria-label-remove-tag": "Odebrat tag",
"aria-label-select-tag-key": "Vyberte klíč tagu",
"aria-label-select-tag-operator": "Vyberte operátor tagu",
"aria-label-select-tag-value": "Vyberte hodnotu tagu",
"placeholder-select-tag": "Vyberte tag",
"placeholder-select-value": "Vyberte hodnotu",
"placeholder-tag-value": "Hodnota tagu",
"tooltip-add-tag": "Přidat tag",
"tooltip-remove-tag": "Odebrat tag"
},
"span-flame-graph": {
"flame-graph": "Graf plamene"
},
@@ -7922,23 +7922,18 @@
"label-upsample": "Zvýšit vzorkovací frekvenci",
"tooltip-s-m-h": "10s, 1m, 30m, 1h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Spustit dotaz",
"modal-title": "Editor SQL",
"tooltip-experimental": "Integrace LLM pro výrazy jazyka SQL je experimentální. Jakékoli problémy nahlaste týmu Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Vstup"
@@ -8013,11 +8008,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9463,6 +9454,7 @@
"name-unit": "Jednotka",
"name-value-name": "Název hodnoty",
"name-y-axis-scale": "Měřítko osy Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Auto",
"placeholder-axis-width": "Auto",
"placeholder-decimals": "Auto",
@@ -9492,6 +9484,18 @@
"label-all": "Vše",
"label-hidden": "Skryté",
"label-single": "Jednorázový"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11295,9 +11299,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -12074,7 +12083,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Zobrazit podrobnosti",
"loading-finished-job": "Načítání dokončené úlohy…",
@@ -12180,6 +12192,9 @@
"webhook-last-event": "Poslední událost:",
"webhook-url": "Zobrazit webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Zpět na úložiště",
"cleaning-up-resources": "Čištění zdrojů úložiště",
@@ -12284,6 +12299,9 @@
"tooltip-unhealthy-repository": "Nelze stáhnout nezdravé úložiště"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12303,10 +12321,15 @@
},
"warning-title-default": "Varování",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Doba trvání tohoto procesu závisí na počtu zapojených zdrojů.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12325,6 +12348,7 @@
"step-finish": "Vybrat další nastavení",
"step-synchronize": "Synchronizovat s externím úložištěm",
"sync-description": "Synchronizujte zdroje s externím úložištěm. Po tomto jednorázovém kroku budou všechny budoucí aktualizace automaticky uloženy do úložiště a zajištěny zpět do instance.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Vyberte, co chcete synchronizovat",
"title-connect": "Připojit k externímu úložišti",
"title-finish": "Vybrat další nastavení",
@@ -12564,16 +12588,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Verbunden",
"text-provisioned": "Bereitgestellt"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Datenquelle",
@@ -3717,6 +3746,7 @@
"text": "Keine Ergebnisse für deine Abfrage gefunden"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Eine andere Person hat dieses Dashboard aktualisiert",
"would-still-dashboard": "Möchten Sie dieses Dashboard trotzdem speichern?"
},
"save-and-overwrite": "'Speichern und überschreiben'"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Zeitbereich sperren"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Profitipp: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Teilen"
},
"span-filters": {
"aria-label-select-max-span-operator": "Höchstspannen-Operator auswählen",
"aria-label-select-min-span-operator": "Mindestspannen-Operator auswählen",
"aria-label-select-service-name": "Dienstname auswählen",
"aria-label-select-service-name-operator": "Dienstnamen-Operator auswählen",
"aria-label-select-span-name": "Spannen-Name auswählen",
"aria-label-select-span-name-operator": "Spannen-Namen-Operator auswählen",
"ariaLabel-select-max-span-duration": "Dauer der max. Spanne auswählen",
"ariaLabel-select-min-span-duration": "Dauer der min. Spanne auswählen",
"label-collapse": "Spannenfilter",
"label-duration": "Dauer",
"label-service-name": "Dienstname",
"label-span-name": "Spannen-Name",
"label-tags": "Tags",
"placeholder-all-service-names": "Alle Dienstnamen",
"placeholder-all-span-names": "Alle Spannen-Namen",
"tooltip-collapse": "Filtern Sie unten Ihre Spannen. Sie können weiterhin Filter anwenden, bis Sie Ihre resultierenden Spannen auf die wenigen eingegrenzt haben, die für Sie am meisten von Interesse sind.",
"tooltip-duration": "Nach Dauer filtern. Zulässige Einheiten sind {{units}}",
"tooltip-tags": "Filtern Sie in Ihren Spannen nach Tags, Prozess-Tags oder Log-Feldern."
},
"span-filters-tags": {
"aria-label-add-tag": "Tag hinzufügen",
"aria-label-input-tag-value": "Tag-Wert eingeben",
"aria-label-remove-tag": "Tag entfernen",
"aria-label-select-tag-key": "Tag-Key auswählen",
"aria-label-select-tag-operator": "Tag-Operator auswählen",
"aria-label-select-tag-value": "Tag-Wert auswählen",
"placeholder-select-tag": "Tag auswählen",
"placeholder-select-value": "Wert auswählen",
"placeholder-tag-value": "Tag-Wert",
"tooltip-add-tag": "Tag hinzufügen",
"tooltip-remove-tag": "Tag entfernen"
},
"span-flame-graph": {
"flame-graph": "Flammendiagramm"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Upsample",
"tooltip-s-m-h": "10 s, 1 min., 30 min., 1 h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Abfrage ausführen",
"modal-title": "SQL Editor",
"tooltip-experimental": "Die Integration des SQL Expressions LLM ist experimentell. Bitte melden Sie Probleme dem Grafana-Team."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Eingabe"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Einheit",
"name-value-name": "Wertname",
"name-y-axis-scale": "Y-Achsenskala",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Auto",
"placeholder-axis-width": "Auto",
"placeholder-decimals": "Auto",
@@ -9422,6 +9414,18 @@
"label-all": "Alles",
"label-hidden": "Ausgeblendet",
"label-single": "Einzeln"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Details anzeigen",
"loading-finished-job": "Fertiger Auftrag wird geladen …",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Letztes Ereignis:",
"webhook-url": "Webhook anzeigen"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Zurück zu den Repositorys",
"cleaning-up-resources": "Bereinigen von Repository-Ressourcen",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Ein fehlerhaftes Repository kann nicht abgerufen werden"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Warnung",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Die Dauer dieses Prozesses hängt von der Anzahl der betroffenen Ressourcen ab.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Zusätzliche Einstellungen auswählen",
"step-synchronize": "Mit externem Speicher synchronisieren",
"sync-description": "Synchronisieren Sie Ressourcen mit externem Speicher. Nach diesem einmaligen Schritt werden alle zukünftigen Updates automatisch im Repository gespeichert und wieder in der Instanz bereitgestellt.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Wählen Sie aus, was synchronisiert wird",
"title-connect": "Mit externem Speicher verbinden",
"title-finish": "Zusätzliche Einstellungen auswählen",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+12 -16
View File
@@ -7872,23 +7872,18 @@
"label-upsample": "Upsample",
"tooltip-s-m-h": "10s, 1m, 30m, 1h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": "Close schema inspector"
},
"sql-expr": {
"button-run-query": "Run query",
"modal-title": "SQL Editor",
"tooltip-experimental": "SQL Expressions LLM integration is experimental. Please report any issues to the Grafana team."
},
"sql-schema": {
"close-schema-inspector": "Close schema inspector",
"error-title": "Error",
"inspect-button": "Inspect schema",
"loading": "Loading schema information...",
"no-data-title": "No schema information available",
"no-fields-desc": "This query returned no schema information.",
"no-fields-title": "No schema information",
"query-error-title": "Query error"
"query-error-title": "Query error",
"schema-inspector": "Schema inspector"
},
"threshold": {
"label-input": "Input"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "Gauge",
"circular": "Circular gauge",
"no-thresholds": "Gauge - no thresholds",
"style": {
"circular": "Glowing",
"simple": "Simple"
}
"no-thresholds": "Gauge - no thresholds"
},
"threshold": "Threshold {{value}}"
},
@@ -12493,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "Bar Style",
"bar-shape-flat": "Flat",
"bar-shape-rounded": "Rounded",
"bar-width": "Bar width",
"effects": {
"bar-glow": "Bar glow",
"center-glow": "Center glow",
"gradient": "Gradient",
"label": "Effects",
"rounded-bars": "Rounded bars",
"spotlight": "Spotlight",
"spotlight-tooltip": "Only visible in dark themes"
"label": "Effects"
},
"endpoint-marker": "Endpoint marker",
"endpoint-marker-description": "Glow is only supported in dark mode",
"endpoint-marker-glow": "Glow",
"endpoint-marker-none": "None",
"endpoint-marker-point": "Point",
"segment-count": "Segments",
"segment-spacing": "Segment spacing",
"shape": "Style",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Federado",
"text-provisioned": "Provisionado"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Fuente de datos",
@@ -3717,6 +3746,7 @@
"text": "No se han encontrado resultados para tu consulta"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Otra persona ha actualizado este dashboard",
"would-still-dashboard": "¿Seguro que quieres guardar este dashboard?"
},
"save-and-overwrite": "«Guardar y sobrescribir»"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Bloquear el intervalo de tiempo"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Consejo profesional: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Compartir"
},
"span-filters": {
"aria-label-select-max-span-operator": "Seleccionar operador de intervalo máximo",
"aria-label-select-min-span-operator": "Seleccionar operador de intervalo mínimo",
"aria-label-select-service-name": "Seleccionar nombre de servicio",
"aria-label-select-service-name-operator": "Seleccionar operador de nombre de servicio",
"aria-label-select-span-name": "Seleccionar nombre de intervalo",
"aria-label-select-span-name-operator": "Seleccionar operador de nombre de intervalo",
"ariaLabel-select-max-span-duration": "Seleccionar duración máxima del intervalo",
"ariaLabel-select-min-span-duration": "Seleccionar duración mínima del intervalo",
"label-collapse": "Filtros de intervalo",
"label-duration": "Duración",
"label-service-name": "Nombre de servicio",
"label-span-name": "Nombre del intervalo",
"label-tags": "Etiquetas",
"placeholder-all-service-names": "Todos los nombres de servicios",
"placeholder-all-span-names": "Todos los nombres de intervalo",
"tooltip-collapse": "Filtre sus intervalos a continuación. Puede seguir aplicando filtros hasta que haya reducido los intervalos resultantes a los que más le interesen.",
"tooltip-duration": "Filtra por duración. Las unidades aceptadas son {{units}}",
"tooltip-tags": "Filtra por etiquetas, etiquetas de proceso o campos de logs en tus intervalos."
},
"span-filters-tags": {
"aria-label-add-tag": "Añadir etiqueta",
"aria-label-input-tag-value": "Introducir valor de la etiqueta",
"aria-label-remove-tag": "Quitar etiqueta",
"aria-label-select-tag-key": "Seleccionar clave de etiqueta",
"aria-label-select-tag-operator": "Seleccionar operador de etiqueta",
"aria-label-select-tag-value": "Seleccionar valor de etiqueta",
"placeholder-select-tag": "Seleccionar etiqueta",
"placeholder-select-value": "Seleccionar valor",
"placeholder-tag-value": "Valor de etiqueta",
"tooltip-add-tag": "Añadir etiqueta",
"tooltip-remove-tag": "Quitar etiqueta"
},
"span-flame-graph": {
"flame-graph": "Gráfico de llama"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Aumentar tamaño",
"tooltip-s-m-h": "10 s, 1 m, 30 m, 1 h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Ejecutar consulta",
"modal-title": "Editor de SQL",
"tooltip-experimental": "La integración de LLM de expresiones SQL es experimental. Avisa de cualquier problema al equipo de Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Entrada"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Unidad",
"name-value-name": "Nombre del valor",
"name-y-axis-scale": "Escala del eje Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Auto",
"placeholder-axis-width": "Auto",
"placeholder-decimals": "Auto",
@@ -9422,6 +9414,18 @@
"label-all": "Todo",
"label-hidden": "Oculto",
"label-single": "Único"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Ver detalles",
"loading-finished-job": "Cargando trabajo finalizado...",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Último evento:",
"webhook-url": "Ver webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Volver a los repositorios",
"cleaning-up-resources": "Limpiando los recursos del repositorio",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "No se puede extraer un repositorio que no está en buen estado"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Advertencia",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "La duración de este proceso depende del número de recursos involucrados.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Elegir ajustes adicionales",
"step-synchronize": "Sincronizar con un almacenamiento externo",
"sync-description": "Sincroniza los recursos con un almacenamiento externo. Después de este paso único, todas las actualizaciones futuras se guardarán automáticamente en el repositorio y se aprovisionarán de nuevo en la instancia.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Elegir qué sincronizar",
"title-connect": "Conectar a un almacenamiento externo",
"title-finish": "Elegir ajustes adicionales",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Fédéré",
"text-provisioned": "Mis en service"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Source de données",
@@ -3717,6 +3746,7 @@
"text": "Aucun résultat n'a été trouvé pour votre requête"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Quelqu’un d’autre a mis à jour ce tableau de bord",
"would-still-dashboard": "Voulez-vous toujours enregistrer ce tableau de bord ?"
},
"save-and-overwrite": "« Enregistrer et écraser »"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Verrouiller la période temporelle"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Conseil de pro : {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Partager"
},
"span-filters": {
"aria-label-select-max-span-operator": "Sélectionner l’opérateur de la durée maximale",
"aria-label-select-min-span-operator": "Sélectionner l’opérateur de la durée minimale",
"aria-label-select-service-name": "Sélectionner le nom de service",
"aria-label-select-service-name-operator": "Sélectionner l’opérateur de nom de service",
"aria-label-select-span-name": "Sélectionner le nom de la durée",
"aria-label-select-span-name-operator": "Sélectionner l’opérateur de nom de la durée",
"ariaLabel-select-max-span-duration": "Sélectionner la durée maximale de segment",
"ariaLabel-select-min-span-duration": "Sélectionner la durée minimale de segment",
"label-collapse": "Filtres de durée",
"label-duration": "Durée",
"label-service-name": "Nom du service",
"label-span-name": "Nom de la durée",
"label-tags": "Étiquettes",
"placeholder-all-service-names": "Tous les noms de service",
"placeholder-all-span-names": "Tous les noms de durée",
"tooltip-collapse": "Filtrez vos plages ci-dessous. Vous pouvez continuer à appliquer des filtres jusqu’à ce que vous ayez réduit votre plage de résultats à ceux qui vous intéressent le plus.",
"tooltip-duration": "Filtrer par durée. Les unités acceptées sont {{units}}",
"tooltip-tags": "Filtrez par balises, balises de processus ou champs de journal dans vos durées."
},
"span-filters-tags": {
"aria-label-add-tag": "Ajouter une balise",
"aria-label-input-tag-value": "Saisir la valeur de la balise",
"aria-label-remove-tag": "Supprimer la balise",
"aria-label-select-tag-key": "Sélectionner la clé de la balise",
"aria-label-select-tag-operator": "Sélectionner l’opérateur de la balise",
"aria-label-select-tag-value": "Sélectionner la valeur de la balise",
"placeholder-select-tag": "Choisir une balise",
"placeholder-select-value": "Sélectionner une valeur",
"placeholder-tag-value": "Valeur de la balise",
"tooltip-add-tag": "Ajouter une balise",
"tooltip-remove-tag": "Supprimer la balise"
},
"span-flame-graph": {
"flame-graph": "Graphique de flamme"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Sur-échantillonner",
"tooltip-s-m-h": "10 s, 1 m, 30 m, 1 h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Exécuter la requête",
"modal-title": "Éditeur SQL",
"tooltip-experimental": "L’intégration des expressions SQL avec les LLM est expérimentale. Merci de signaler tout problème à l’équipe Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Entrée"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Unité",
"name-value-name": "Nom de la valeur",
"name-y-axis-scale": "Échelle de l’axe Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Auto",
"placeholder-axis-width": "Auto",
"placeholder-decimals": "Auto",
@@ -9422,6 +9414,18 @@
"label-all": "Tous",
"label-hidden": "Masqué",
"label-single": "Unique"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Afficher les détails",
"loading-finished-job": "Chargement de mission terminée…",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Dernier événement :",
"webhook-url": "Voir le webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Retour aux référentiels",
"cleaning-up-resources": "Nettoyage des ressources du référentiel",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Impossible de fusionner un référentiel en mauvais état"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Avertissement",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "La durée de ce processus dépend du nombre de ressources impliquées.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Choisir des paramètres supplémentaires",
"step-synchronize": "Synchroniser avec un stockage externe",
"sync-description": "Synchronisez les ressources avec un stockage externe. Après cette étape unique, toutes les futures mises à jour seront automatiquement enregistrées dans le référentiel et mises en service dans l’instance.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Choisir ce qui doit être synchronisé",
"title-connect": "Se connecter à un stockage externe",
"title-finish": "Choisir des paramètres supplémentaires",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Összevont",
"text-provisioned": "Kiépítve"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Adatforrás",
@@ -3717,6 +3746,7 @@
"text": "Nincs találat a lekérdezésre"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Valaki más frissítette ezt az irányítópultot",
"would-still-dashboard": "Biztosan menti ezt az irányítópultot?"
},
"save-and-overwrite": "„Mentés és felülírás”"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Időtartomány zárolása"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "ProTip: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Megosztás"
},
"span-filters": {
"aria-label-select-max-span-operator": "Max. terjedelem operátor kijelölése",
"aria-label-select-min-span-operator": "Válasszon operátort a min. terjedelemhez",
"aria-label-select-service-name": "Válasszon szolgáltatásnevet",
"aria-label-select-service-name-operator": "Válasszon operátort a szolgáltatásnévhez",
"aria-label-select-span-name": "Válasszon terjedelemnevet",
"aria-label-select-span-name-operator": "Terjedelemnév operátor kijelölése",
"ariaLabel-select-max-span-duration": "Maximális időtartam kiválasztása",
"ariaLabel-select-min-span-duration": "Minimális időtartam kiválasztása",
"label-collapse": "Terjedelemszűrők",
"label-duration": "Időtartam",
"label-service-name": "Szolgáltatásnév",
"label-span-name": "Terjedelemnév",
"label-tags": "Címkék",
"placeholder-all-service-names": "Összes szolgáltatásnév",
"placeholder-all-span-names": "Összes terjedelemnév",
"tooltip-collapse": "Alább szűrheti a terjedelmeket. Folytathatja a szűrők alkalmazását, amíg a kapott terjedelmeket a leginkább keresett néhány terjedelemre nem szűkíti.",
"tooltip-duration": "Szűrés időtartam szerint. Elfogadott mértékegységek: {{units}}",
"tooltip-tags": "Szűrés címkék, folyamatcímkék vagy naplómezők alapján a terjedelmeiben."
},
"span-filters-tags": {
"aria-label-add-tag": "Címke hozzáadása",
"aria-label-input-tag-value": "Bemeneti címke értéke",
"aria-label-remove-tag": "Címke eltávolítása",
"aria-label-select-tag-key": "Címkekulcs kijelölése",
"aria-label-select-tag-operator": "Címkeoperátor kijelölése",
"aria-label-select-tag-value": "Címkeérték kijelölése",
"placeholder-select-tag": "Címke kijelölése",
"placeholder-select-value": "Érték kijelölése",
"placeholder-tag-value": "Címke értéke",
"tooltip-add-tag": "Címke hozzáadása",
"tooltip-remove-tag": "Címke eltávolítása"
},
"span-flame-graph": {
"flame-graph": "Lángdiagram"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Mintasűrűség növelése",
"tooltip-s-m-h": "10 mp., 1 p., 30 p., 1 ó."
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Lekérdezés futtatása",
"modal-title": "SQL-szerkesztő",
"tooltip-experimental": "Az SQL-kifejezések LLM-integrációja kísérleti jellegű. Kérjük, jelentse az esetleges problémákat a Grafana csapatának."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Bemenet"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Egység",
"name-value-name": "Érték neve",
"name-y-axis-scale": "Y tengely skálája",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Automatikus",
"placeholder-axis-width": "Automatikus",
"placeholder-decimals": "Automatikus",
@@ -9422,6 +9414,18 @@
"label-all": "Összes",
"label-hidden": "Rejtett",
"label-single": "Különálló"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Részletek megtekintése",
"loading-finished-job": "Befejezett feladat betöltése…",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Legutóbbi esemény:",
"webhook-url": "Webkapocs megtekintése"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Vissza az adattárakhoz",
"cleaning-up-resources": "Adattári erőforrások tisztítása",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Nem lehet beolvasni egy nem megfelelő állapotú adattárat"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Figyelmeztetés",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "A folyamat időtartama az érintett erőforrások számától függ.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Válassza ki a további beállításokat",
"step-synchronize": "Szinkronizálás külső tárolóval",
"sync-description": "Erőforrások szinkronizálása külső tárolóval. Ezután az egyszeri lépés után az összes jövőbeli frissítés automatikusan mentve lesz az adattárba, és vissza lesz építve a példányba.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Válassza ki, mit szeretne szinkronizálni",
"title-connect": "Csatlakozás külső tárolóhoz",
"title-finish": "Válassza ki a további beállításokat",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2655,6 +2655,35 @@
"text-federated": "Federasi",
"text-provisioned": "Disediakan"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Sumber data",
@@ -3701,6 +3730,7 @@
"text": "Hasil untuk kueri Anda tidak ditemukan"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6159,7 +6189,7 @@
"title-someone-else-has-updated-this-dashboard": "Orang lain telah memperbarui dasbor ini",
"would-still-dashboard": "Ingin tetap menyimpan dasbor ini?"
},
"save-and-overwrite": "'Simpan dan timpa'"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7142,6 +7172,9 @@
"time-range-label": "Kunci rentang waktu"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Kiat Pro: {{proTip}}"
},
@@ -7616,39 +7649,6 @@
},
"share-span": "Bagikan"
},
"span-filters": {
"aria-label-select-max-span-operator": "Pilih operator rentang maksimum",
"aria-label-select-min-span-operator": "Pilih operator rentang min",
"aria-label-select-service-name": "Pilih nama layanan",
"aria-label-select-service-name-operator": "Pilih operator nama layanan",
"aria-label-select-span-name": "Pilih nama rentang",
"aria-label-select-span-name-operator": "Pilih operator nama rentang",
"ariaLabel-select-max-span-duration": "Pilih durasi rentang maksimum",
"ariaLabel-select-min-span-duration": "Pilih durasi rentang minimum",
"label-collapse": "Filter Rentang",
"label-duration": "Durasi",
"label-service-name": "Nama layanan",
"label-span-name": "Nama rentang",
"label-tags": "Tag",
"placeholder-all-service-names": "Semua nama layanan",
"placeholder-all-span-names": "Semua nama rentang",
"tooltip-collapse": "Filter rentang Anda di bawah ini. Anda dapat terus menerapkan filter hingga Anda mempersempit rentang hasil Anda menjadi beberapa pilihan yang paling Anda minati.",
"tooltip-duration": "Filter menurut durasi. Unit yang diterima adalah {{units}}",
"tooltip-tags": "Filter berdasarkan tag, tag proses, atau bidang log di rentang Anda."
},
"span-filters-tags": {
"aria-label-add-tag": "Tambah tag",
"aria-label-input-tag-value": "Masukkan nilai tag",
"aria-label-remove-tag": "Hapus tag",
"aria-label-select-tag-key": "Pilih kunci tag",
"aria-label-select-tag-operator": "Pilih operator tag",
"aria-label-select-tag-value": "Pilih nilai tag",
"placeholder-select-tag": "Pilih tag",
"placeholder-select-value": "Pilih nilai",
"placeholder-tag-value": "Nilai tag",
"tooltip-add-tag": "Tambah tag",
"tooltip-remove-tag": "Hapus tag"
},
"span-flame-graph": {
"flame-graph": "Grafik api"
},
@@ -7847,23 +7847,18 @@
"label-upsample": "Tingkatkan sample",
"tooltip-s-m-h": "10 dtk, 1 mnt, 30 mnt, 1 jm"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Jalankan kueri",
"modal-title": "Editor SQL",
"tooltip-experimental": "Integrasi LLM Ekspresi SQL bersifat eksperimental. Harap laporkan masalah apa pun kepada tim Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Input"
@@ -7938,11 +7933,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9358,6 +9349,7 @@
"name-unit": "Unit",
"name-value-name": "Nama nilai",
"name-y-axis-scale": "Skala sumbu Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Otomatis",
"placeholder-axis-width": "Otomatis",
"placeholder-decimals": "Otomatis",
@@ -9387,6 +9379,18 @@
"label-all": "Semua",
"label-hidden": "Tersembunyi",
"label-single": "Tunggal"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11157,9 +11161,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11921,7 +11930,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Lihat detail",
"loading-finished-job": "Memuat pekerjaan yang sudah selesai...",
@@ -12027,6 +12039,9 @@
"webhook-last-event": "Peristiwa Terakhir:",
"webhook-url": "Lihat Webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Kembali ke repositori",
"cleaning-up-resources": "Membersihkan sumber daya repositori",
@@ -12131,6 +12146,9 @@
"tooltip-unhealthy-repository": "Tidak dapat menerapkan pull pada repositori yang tidak sehat"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12150,10 +12168,15 @@
},
"warning-title-default": "Peringatan",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Durasi proses ini bergantung pada jumlah sumber daya yang terlibat.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12172,6 +12195,7 @@
"step-finish": "Pilih pengaturan tambahan",
"step-synchronize": "Sinkronkan dengan penyimpanan eksternal",
"sync-description": "Sinkronkan sumber daya dengan penyimpanan eksternal. Setelah langkah satu kali ini, semua pembaruan mendatang akan disimpan secara otomatis ke repositori dan disediakan kembali ke instans.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Pilih item yang akan disinkronkan",
"title-connect": "Hubungkan ke penyimpanan eksternal",
"title-finish": "Pilih pengaturan tambahan",
@@ -12408,16 +12432,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Federato",
"text-provisioned": "Fornito"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Sorgente dati",
@@ -3717,6 +3746,7 @@
"text": "Nessun risultato trovato per la ricerca"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Qualcun altro ha aggiornato questa dashboard",
"would-still-dashboard": "Desideri comunque salvare questa dashboard?"
},
"save-and-overwrite": "\"Salva e sovrascrivi\""
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Blocca intervallo di tempo"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Suggerimento pro: {{proTip}} "
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Condividi"
},
"span-filters": {
"aria-label-select-max-span-operator": "Seleziona l'operatore di intervallo massimo",
"aria-label-select-min-span-operator": "Seleziona operatore di intervallo minimo",
"aria-label-select-service-name": "Seleziona nome servizio",
"aria-label-select-service-name-operator": "Seleziona operatore nome servizio",
"aria-label-select-span-name": "Seleziona nome intervallo",
"aria-label-select-span-name-operator": "Seleziona l'operatore del nome dell'intervallo",
"ariaLabel-select-max-span-duration": "Seleziona la durata dell'intervallo massimo",
"ariaLabel-select-min-span-duration": "Seleziona la durata dell'intervallo minimo",
"label-collapse": "Filtri intervallo",
"label-duration": "Durata",
"label-service-name": "Nome del servizio",
"label-span-name": "Nome intervallo",
"label-tags": "Tag",
"placeholder-all-service-names": "Tutti i nomi dei servizi",
"placeholder-all-span-names": "Tutti i nomi degli intervalli",
"tooltip-collapse": "Filtra i tuoi intervalli qui sotto. Puoi continuare ad applicare i filtri fino a quando non avrai ristretto gli intervalli risultanti ai pochi selezionati a cui sei più interessato.",
"tooltip-duration": "Filtra per durata. Le unità accettate sono {{units}}",
"tooltip-tags": "Filtra per tag, tag di processo o campi di registro nei tuoi intervalli."
},
"span-filters-tags": {
"aria-label-add-tag": "Aggiungi tag",
"aria-label-input-tag-value": "Valore del tag di inserimento",
"aria-label-remove-tag": "Rimuovi tag",
"aria-label-select-tag-key": "Seleziona chiave tag",
"aria-label-select-tag-operator": "Seleziona operatore tag",
"aria-label-select-tag-value": "Seleziona valore tag",
"placeholder-select-tag": "Seleziona tag",
"placeholder-select-value": "Seleziona valore",
"placeholder-tag-value": "Valore del tag",
"tooltip-add-tag": "Aggiungi tag",
"tooltip-remove-tag": "Rimuovi tag"
},
"span-flame-graph": {
"flame-graph": "Grafico a fiamma"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Aumenta campionamento",
"tooltip-s-m-h": "10 s, 1 m, 30 m, 1 h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Esegui query",
"modal-title": "Editor SQL",
"tooltip-experimental": "L'integrazione LLM delle Espressioni SQL è sperimentale. Segnala eventuali problemi al team Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Inserisci"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Unità",
"name-value-name": "Nome del valore",
"name-y-axis-scale": "Scala asse Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Automatico",
"placeholder-axis-width": "Automatico",
"placeholder-decimals": "Automatico",
@@ -9422,6 +9414,18 @@
"label-all": "Tutti",
"label-hidden": "Nascosto",
"label-single": "Singolo"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Visualizza dettagli",
"loading-finished-job": "Caricamento attività terminata in corso...",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Ultimo evento:",
"webhook-url": "Visualizza webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Torna ai repository",
"cleaning-up-resources": "Pulizia delle risorse del repository",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Impossibile eseguire il pull di un repository non integro"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Attenzione",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "La durata di questo processo dipende dal numero di risorse coinvolte.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Scegli impostazioni aggiuntive",
"step-synchronize": "Sincronizza con la memoria esterna",
"sync-description": "Sincronizza le risorse con la memoria esterna. Dopo questo passaggio una tantum, tutti gli aggiornamenti futuri verranno salvati automaticamente nel repository e ripristinati nell'istanza.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Scegli cosa sincronizzare",
"title-connect": "Connetti a una memoria esterna",
"title-finish": "Scegli impostazioni aggiuntive",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2655,6 +2655,35 @@
"text-federated": "フェデレーション",
"text-provisioned": "プロビジョニング済み"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "データソース",
@@ -3701,6 +3730,7 @@
"text": "クエリに一致する結果が見つかりませんでした。"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6159,7 +6189,7 @@
"title-someone-else-has-updated-this-dashboard": "他のユーザーがこのダッシュボードを更新しました",
"would-still-dashboard": "このダッシュボードの保存を続行しますか?"
},
"save-and-overwrite": "「保存して上書き」"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7142,6 +7172,9 @@
"time-range-label": "時間範囲をロック"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "プロのヒント:{{proTip}} "
},
@@ -7616,39 +7649,6 @@
},
"share-span": "共有"
},
"span-filters": {
"aria-label-select-max-span-operator": "最大スパン演算子を選択",
"aria-label-select-min-span-operator": "最小スパン演算子を選択",
"aria-label-select-service-name": "サービス名を選択",
"aria-label-select-service-name-operator": "サービス名演算子を選択",
"aria-label-select-span-name": "スパン名を選択",
"aria-label-select-span-name-operator": "スパン名演算子を選択",
"ariaLabel-select-max-span-duration": "最大スパン期間を選択",
"ariaLabel-select-min-span-duration": "最小スパン期間を選択",
"label-collapse": "スパンフィルター",
"label-duration": "継続時間",
"label-service-name": "サービス名",
"label-span-name": "スパン名",
"label-tags": "タグ",
"placeholder-all-service-names": "すべてのサービス名",
"placeholder-all-span-names": "すべてのスパン名",
"tooltip-collapse": "以下のスパンを絞り込みます。最も関心のある少数のスパンに絞り込むまで、フィルターを適用し続けることができます。",
"tooltip-duration": "期間で絞り込みます。使用可能な単位:{{units}}",
"tooltip-tags": "スパン内のタグ、プロセスタグ、またはログフィールドで絞り込みます。"
},
"span-filters-tags": {
"aria-label-add-tag": "タグを追加",
"aria-label-input-tag-value": "タグ値を入力",
"aria-label-remove-tag": "タグを削除",
"aria-label-select-tag-key": "タグキーを選択",
"aria-label-select-tag-operator": "タグ演算子を選択",
"aria-label-select-tag-value": "タグ値を選択",
"placeholder-select-tag": "タグを選択",
"placeholder-select-value": "値を選択",
"placeholder-tag-value": "タグ値",
"tooltip-add-tag": "タグを追加",
"tooltip-remove-tag": "タグを削除"
},
"span-flame-graph": {
"flame-graph": "フレームグラフ"
},
@@ -7847,23 +7847,18 @@
"label-upsample": "アップサンプリング",
"tooltip-s-m-h": "10秒、1分、30分、1時間"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "クエリを実行",
"modal-title": "SQLエディター",
"tooltip-experimental": "SQL Expressions LLMの統合は実験的です。問題が発生した場合は、Grafanaチームに報告してください。"
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "入力"
@@ -7938,11 +7933,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9358,6 +9349,7 @@
"name-unit": "単位",
"name-value-name": "値の名前",
"name-y-axis-scale": "Y軸スケール",
"name-y-bucket-scale": "",
"placeholder-axis-label": "自動",
"placeholder-axis-width": "自動",
"placeholder-decimals": "自動",
@@ -9387,6 +9379,18 @@
"label-all": "すべて",
"label-hidden": "非表示",
"label-single": "単体"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11157,9 +11161,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11921,7 +11930,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "詳細を表示",
"loading-finished-job": "完了したジョブを読み込み中...",
@@ -12027,6 +12039,9 @@
"webhook-last-event": "最新イベント:",
"webhook-url": "Webhookを表示"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "リポジトリに戻る",
"cleaning-up-resources": "リポジトリリソースのクリーンアップ中",
@@ -12131,6 +12146,9 @@
"tooltip-unhealthy-repository": "問題のあるリポジトリをプルできません"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12150,10 +12168,15 @@
},
"warning-title-default": "警告",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "このプロセスの所要時間は、関連するリソースの数によって異なります。",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12172,6 +12195,7 @@
"step-finish": "追加設定を選択",
"step-synchronize": "外部ストレージと同期",
"sync-description": "リソースを外部ストレージと同期します。この一度限りの手順が完了すると、その後のすべての更新は自動的にリポジトリに保存され、インスタンスにプロビジョニングされます。",
"sync-option-migrate-resources": "",
"title-bootstrap": "同期する内容を選択",
"title-connect": "外部ストレージに接続",
"title-finish": "追加設定を選択",
@@ -12408,16 +12432,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2655,6 +2655,35 @@
"text-federated": "연합됨",
"text-provisioned": "프로비저닝됨"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "데이터 소스",
@@ -3701,6 +3730,7 @@
"text": "쿼리에 대해 찾은 결과 없음"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6159,7 +6189,7 @@
"title-someone-else-has-updated-this-dashboard": "다른 사람이 이 대시보드를 업데이트했습니다",
"would-still-dashboard": "그래도 이 대시보드를 저장하시겠어요?"
},
"save-and-overwrite": "'저장 및 덮어쓰기'"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7142,6 +7172,9 @@
"time-range-label": "시간 범위 잠그기"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "유용한 팁: {{proTip}} "
},
@@ -7616,39 +7649,6 @@
},
"share-span": "공유"
},
"span-filters": {
"aria-label-select-max-span-operator": "최대 스팬 연산자 선택",
"aria-label-select-min-span-operator": "최소 스팬 연산자 선택",
"aria-label-select-service-name": "서비스 이름 선택",
"aria-label-select-service-name-operator": "서비스 이름 연산자 선택",
"aria-label-select-span-name": "스팬 이름 선택",
"aria-label-select-span-name-operator": "스팬 이름 연산자 선택",
"ariaLabel-select-max-span-duration": "최대 스팬 기간 선택",
"ariaLabel-select-min-span-duration": "최소 스팬 기간 선택",
"label-collapse": "스팬 필터",
"label-duration": "지속 시간",
"label-service-name": "서비스 이름",
"label-span-name": "스팬 이름",
"label-tags": "태그",
"placeholder-all-service-names": "모든 서비스 이름",
"placeholder-all-span-names": "모든 스팬 이름",
"tooltip-collapse": "아래에서 스팬을 필터링합니다. 결과 스팬을 가장 관심 있는 몇 가지로 좁힐 때까지 필터를 계속 적용할 수 있습니다.",
"tooltip-duration": "지속 시간을 기준으로 필터링합니다. 허용되는 단위는 {{units}}입니다",
"tooltip-tags": "스팬의 태그, 프로세스 태그 또는 로그 필드를 기준으로 필터링합니다."
},
"span-filters-tags": {
"aria-label-add-tag": "태그 추가",
"aria-label-input-tag-value": "태그 값 입력",
"aria-label-remove-tag": "태그 제거",
"aria-label-select-tag-key": "태그 키 선택",
"aria-label-select-tag-operator": "태그 연산자 선택",
"aria-label-select-tag-value": "태그 값 선택",
"placeholder-select-tag": "태그 선택",
"placeholder-select-value": "값 선택",
"placeholder-tag-value": "태그 값",
"tooltip-add-tag": "태그 추가",
"tooltip-remove-tag": "태그 제거"
},
"span-flame-graph": {
"flame-graph": "불꽃 그래프"
},
@@ -7847,23 +7847,18 @@
"label-upsample": "업샘플",
"tooltip-s-m-h": "10초, 1분, 30분, 1시간"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "쿼리 실행",
"modal-title": "SQL 편집기",
"tooltip-experimental": "SQL 표현식 LLM 통합 기능은 실험 단계입니다. 문제가 발생하면 Grafana 팀에 보고해 주세요."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "입력"
@@ -7938,11 +7933,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9358,6 +9349,7 @@
"name-unit": "단위",
"name-value-name": "값 이름",
"name-y-axis-scale": "Y축 스케일",
"name-y-bucket-scale": "",
"placeholder-axis-label": "자동",
"placeholder-axis-width": "자동",
"placeholder-decimals": "자동",
@@ -9387,6 +9379,18 @@
"label-all": "전체",
"label-hidden": "숨김",
"label-single": "단일"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11157,9 +11161,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11921,7 +11930,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "세부 정보 보기",
"loading-finished-job": "완료된 작업 로딩 중...",
@@ -12027,6 +12039,9 @@
"webhook-last-event": "마지막 이벤트:",
"webhook-url": "웹훅 보기"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "리포지토리로 돌아가기",
"cleaning-up-resources": "리포지토리 리소스 정리 및 삭제 중",
@@ -12131,6 +12146,9 @@
"tooltip-unhealthy-repository": "상태가 좋지 않은 리포지토리를 가져올 수 없습니다"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12150,10 +12168,15 @@
},
"warning-title-default": "경고",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "이 프로세스의 지속 시간은 관련된 리소스 수에 따라 달라집니다.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12172,6 +12195,7 @@
"step-finish": "추가 설정 선택",
"step-synchronize": "외부 스토리지와 동기화",
"sync-description": "외부 스토리지와 리소스를 동기화합니다. 이 일회성 단계 후에는 향후 모든 업데이트가 자동으로 리포지토리에 저장되고 인스턴스에 다시 프로비저닝됩니다.",
"sync-option-migrate-resources": "",
"title-bootstrap": "동기화할 항목 선택",
"title-connect": "외부 스토리지에 연결",
"title-finish": "추가 설정 선택",
@@ -12408,16 +12432,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Federatief",
"text-provisioned": "Provisioned"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Gegevensbron",
@@ -3717,6 +3746,7 @@
"text": "Geen resultaten gevonden voor je zoekopdracht"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Iemand anders heeft dit dashboard bijgewerkt",
"would-still-dashboard": "Wil je dit dashboard nog steeds opslaan?"
},
"save-and-overwrite": "'Opslaan en overschrijven'"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Tijdsbereik vergrendelen"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "ProTip: {{proTip}} "
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Delen"
},
"span-filters": {
"aria-label-select-max-span-operator": "Operator voor maximale span selecteren",
"aria-label-select-min-span-operator": "Selecteer een operator voor een minimumspan",
"aria-label-select-service-name": "Servicenaam selecteren",
"aria-label-select-service-name-operator": "Servicenaam operator selecteren",
"aria-label-select-span-name": "Selecteer een spannaam",
"aria-label-select-span-name-operator": " Operator voor een spannaam selecteren",
"ariaLabel-select-max-span-duration": "Selecteer een maximale spanduur",
"ariaLabel-select-min-span-duration": "Selecteer een minimale spanduur",
"label-collapse": "Spanfilters",
"label-duration": "Duur",
"label-service-name": "Servicenaam",
"label-span-name": "Spannaam",
"label-tags": "Labels",
"placeholder-all-service-names": "Alle servicenamen",
"placeholder-all-span-names": "Alle spannamen",
"tooltip-collapse": "Filter je bereik hieronder. Je kunt filters blijven toepassen totdat je het resulterende bereik hebt beperkt tot de resultaten die het meest interessant voor je zijn.",
"tooltip-duration": "Filteren op duur. Geaccepteerde eenheden zijn {{units}}",
"tooltip-tags": "Filteren op labels, labels verwerken of logvelden in je spans."
},
"span-filters-tags": {
"aria-label-add-tag": "Label toevoegen",
"aria-label-input-tag-value": "Labelwaarde invoeren",
"aria-label-remove-tag": "Label verwijderen",
"aria-label-select-tag-key": "Labelsleutel selecteren",
"aria-label-select-tag-operator": "Labeloperator selecteren",
"aria-label-select-tag-value": "Labelwaarde selecteren",
"placeholder-select-tag": "Label selecteren",
"placeholder-select-value": "Waarde selecteren",
"placeholder-tag-value": "Labelwaarde",
"tooltip-add-tag": "Label toevoegen",
"tooltip-remove-tag": "Label verwijderen"
},
"span-flame-graph": {
"flame-graph": "Vlamgrafiek"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Upsample",
"tooltip-s-m-h": "10s, 1m, 30m, 1u"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Query uitvoeren",
"modal-title": "SQL-editor",
"tooltip-experimental": "SQL Expressions LLM-integratie is experimenteel. Meld eventuele problemen aan het Grafana-team."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Invoer"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Eenheid",
"name-value-name": "Naam van waarde:",
"name-y-axis-scale": "Y-as schaal",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Automatisch",
"placeholder-axis-width": "Automatisch",
"placeholder-decimals": "Automatisch",
@@ -9422,6 +9414,18 @@
"label-all": "Alle",
"label-hidden": "Verborgen",
"label-single": "Enkel"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Details bekijken",
"loading-finished-job": "Voltooide taak laden...",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Laatste gebeurtenis:",
"webhook-url": "Webhook bekijken"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Terug naar repositories",
"cleaning-up-resources": "Bronnen van repository opschonen",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Kan geen ongezonde repository ophalen"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Waarschuwing",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "De duur van dit proces is afhankelijk van het aantal betrokken bronnen.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Kies aanvullende instellingen",
"step-synchronize": "Synchroniseren met externe opslag",
"sync-description": "Bronnen synchroniseren met externe opslag. Na deze eenmalige stap worden alle toekomstige updates automatisch opgeslagen in de repository en opnieuw ingericht in de instantie.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Kies wat je wilt synchroniseren",
"title-connect": "Verbinden met externe opslag",
"title-finish": "Kies aanvullende instellingen",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2688,6 +2688,35 @@
"text-federated": "Federacja",
"text-provisioned": "Po aprowizacji"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Źródło danych",
@@ -3749,6 +3778,7 @@
"text": "Nie znaleziono wyników dla tego zapytania"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6222,7 +6252,7 @@
"title-someone-else-has-updated-this-dashboard": "Ktoś inny zaktualizował ten pulpit",
"would-still-dashboard": "Czy nadal chcesz zapisać ten pulpit?"
},
"save-and-overwrite": "„Zapisz i zastąp”"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7211,6 +7241,9 @@
"time-range-label": "Zablokuj zakres czasu"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Wskazówka: {{proTip}}"
},
@@ -7691,39 +7724,6 @@
},
"share-span": "Udostępnij"
},
"span-filters": {
"aria-label-select-max-span-operator": "Wybierz operator maksymalnego zakresu",
"aria-label-select-min-span-operator": "Wybierz operator minimalnego zakresu",
"aria-label-select-service-name": "Wybierz nazwę usługi",
"aria-label-select-service-name-operator": "Wybierz operator nazwy usługi",
"aria-label-select-span-name": "Wybierz nazwę zakresu",
"aria-label-select-span-name-operator": "Wybierz operator nazwy zakresu",
"ariaLabel-select-max-span-duration": "Wybierz maksymalny czas trwania zakresu",
"ariaLabel-select-min-span-duration": "Wybierz minimalny czas trwania zakresu",
"label-collapse": "Filtry zakresu",
"label-duration": "Czas trwania",
"label-service-name": "Nazwa usługi",
"label-span-name": "Nazwa zakresu",
"label-tags": "Znaczniki",
"placeholder-all-service-names": "Wszystkie nazwy usług",
"placeholder-all-span-names": "Wszystkie nazwy zakresów",
"tooltip-collapse": "Odfiltruj zakresy poniżej. Możesz dodawać filtry, aż zawęzisz zakres wyników do kilku najbardziej interesujących.",
"tooltip-duration": "Filtrowanie według czasu trwania. Akceptowane jednostki: {{units}}",
"tooltip-tags": "Filtruj według tagów, tagów procesu lub pól logów w swoich zakresach."
},
"span-filters-tags": {
"aria-label-add-tag": "Dodaj tag",
"aria-label-input-tag-value": "Wpisz wartość tagu",
"aria-label-remove-tag": "Usuń tag",
"aria-label-select-tag-key": "Wybierz klucz tagu",
"aria-label-select-tag-operator": "Wybierz operator tagu",
"aria-label-select-tag-value": "Wybierz wartość tagu",
"placeholder-select-tag": "Wybierz tag",
"placeholder-select-value": "Wybierz wartość",
"placeholder-tag-value": "Wartość tagu",
"tooltip-add-tag": "Dodaj tag",
"tooltip-remove-tag": "Usuń tag"
},
"span-flame-graph": {
"flame-graph": "Wykres płomienia"
},
@@ -7922,23 +7922,18 @@
"label-upsample": "Zwiększ próbkowanie",
"tooltip-s-m-h": "10s, 1m, 30m, 1h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Uruchom zapytanie",
"modal-title": "Edytor SQL",
"tooltip-experimental": "Integracja LLM z wyrażeniami SQL jest eksperymentalna. Wszelkie problemy zgłaszaj zespołowi Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Wejście"
@@ -8013,11 +8008,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9463,6 +9454,7 @@
"name-unit": "Jednostka",
"name-value-name": "Nazwa wartości",
"name-y-axis-scale": "Skala osi Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Automatycznie",
"placeholder-axis-width": "Automatycznie",
"placeholder-decimals": "Automatycznie",
@@ -9492,6 +9484,18 @@
"label-all": "Wszystkie",
"label-hidden": "Ukryte",
"label-single": "Pojedyncza"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11295,9 +11299,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -12074,7 +12083,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Zobacz szczegóły",
"loading-finished-job": "Wczytywanie zakończonego zadania…",
@@ -12180,6 +12192,9 @@
"webhook-last-event": "Ostatnie zdarzenie:",
"webhook-url": "Wyświetl element webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Wróć do repozytoriów",
"cleaning-up-resources": "Sprzątanie zasobów repozytorium",
@@ -12284,6 +12299,9 @@
"tooltip-unhealthy-repository": "Nie można pobrać danych z niesprawnego repozytorium"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12303,10 +12321,15 @@
},
"warning-title-default": "Ostrzeżenie",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Czas trwania tego procesu zależy od liczby zasobów.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12325,6 +12348,7 @@
"step-finish": "Wybierz dodatkowe ustawienia",
"step-synchronize": "Synchronizuj z zewnętrzną pamięcią masową",
"sync-description": "Zsynchronizuj zasoby z zewnętrzną pamięcią masową. Po tym jednorazowym kroku wszystkie przyszłe aktualizacje zostaną automatycznie zapisane w repozytorium i ponownie aprowizowane w instancji.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Wybierz, co chcesz zsynchronizować",
"title-connect": "Połączenie z zewnętrzną pamięcią masową",
"title-finish": "Wybierz dodatkowe ustawienia",
@@ -12564,16 +12588,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Federado",
"text-provisioned": "Provisionado"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Fonte de dados",
@@ -3717,6 +3746,7 @@
"text": "Nenhum resultado encontrado para sua consulta"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Outra pessoa atualizou este painel",
"would-still-dashboard": "Deseja salvar este painel mesmo assim?"
},
"save-and-overwrite": "\"Salvar e substituir\""
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Bloquear intervalo de tempo"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Dica de especialistas: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Compartilhar"
},
"span-filters": {
"aria-label-select-max-span-operator": "Selecionar operador de intervalo máximo",
"aria-label-select-min-span-operator": "Selecionar operador de intervalo mínimo",
"aria-label-select-service-name": "Selecionar nome do serviço",
"aria-label-select-service-name-operator": "Selecionar operador de nome do serviço",
"aria-label-select-span-name": "Selecionar nome do intervalo",
"aria-label-select-span-name-operator": "Selecione o operador de nome de intervalo",
"ariaLabel-select-max-span-duration": "Selecionar duração máxima do intervalo",
"ariaLabel-select-min-span-duration": "Selecionar duração mínima do intervalo",
"label-collapse": "Filtros de intervalo",
"label-duration": "Duração",
"label-service-name": "Nome do serviço",
"label-span-name": "Nome do intervalo",
"label-tags": "Tags",
"placeholder-all-service-names": "Todos os nomes de serviço",
"placeholder-all-span-names": "Todos os nomes de intervalo",
"tooltip-collapse": "Filtre seus intervalos abaixo. Você pode continuar aplicando filtros até restringir seus intervalos resultantes a um grupo pequeno que contenha aqueles que forem mais pertinentes para você.",
"tooltip-duration": "Filtrar por duração. As unidades aceitas são {{units}}",
"tooltip-tags": "Filtrar por tags, tags de processo ou campos de logs nos seus intervalos."
},
"span-filters-tags": {
"aria-label-add-tag": "Adicionar tag",
"aria-label-input-tag-value": "Valor da tag de entrada",
"aria-label-remove-tag": "Remover tag",
"aria-label-select-tag-key": "Selecionar chave de tag",
"aria-label-select-tag-operator": "Selecionar operador de tag",
"aria-label-select-tag-value": "Selecionar valor da tag",
"placeholder-select-tag": "Selecionar tag",
"placeholder-select-value": "Selecionar valor",
"placeholder-tag-value": "Valor da tag",
"tooltip-add-tag": "Adicionar tag",
"tooltip-remove-tag": "Remover tag"
},
"span-flame-graph": {
"flame-graph": "Gráfico de chama"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Aumentar taxa de amostragem",
"tooltip-s-m-h": "10s, 1m, 30m, 1h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Executar consulta",
"modal-title": "Editor de SQL",
"tooltip-experimental": "A integração do LLM de expressões SQL está em fase de testes. Informe à equipe do Grafana se surgir algum problema."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Entrada"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Unidade",
"name-value-name": "Nome do valor",
"name-y-axis-scale": "Escala do eixo Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Automático",
"placeholder-axis-width": "Automático",
"placeholder-decimals": "Automático",
@@ -9422,6 +9414,18 @@
"label-all": "Tudo",
"label-hidden": "Oculto",
"label-single": "Única"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Veja os detalhes",
"loading-finished-job": "Carregando tarefa concluída…",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Último evento:",
"webhook-url": "Visualizar Webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Voltar para os repositórios",
"cleaning-up-resources": "Limpando recursos do repositório",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Não é possível fazer extração de um repositório instável"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Aviso",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "A duração deste processo depende da quantidade de recursos envolvidos.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Escolha configurações adicionais",
"step-synchronize": "Sincronizar com armazenamento externo",
"sync-description": "Sincronize recursos com armazenamento externo. Após esta única etapa, todas as atualizações futuras serão salvas automaticamente no repositório e provisionadas de volta para a instância.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Escolha o que será sincronizado",
"title-connect": "Conectar ao armazenamento externo",
"title-finish": "Escolha configurações adicionais",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Federado",
"text-provisioned": "Aprovisionado"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Origem dos dados",
@@ -3717,6 +3746,7 @@
"text": "Não foram encontrados resultados para a sua consulta"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Outra pessoa atualizou este painel de controlo",
"would-still-dashboard": "Ainda pretende guardar este painel de controlo?"
},
"save-and-overwrite": "\"Guardar e substituir\""
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Bloquear intervalo de tempo"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Dica Pro: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Partilhar"
},
"span-filters": {
"aria-label-select-max-span-operator": "Selecionar o operador de intervalo máximo",
"aria-label-select-min-span-operator": "Selecionar o operador de intervalo mínimo",
"aria-label-select-service-name": "Selecione o nome do serviço",
"aria-label-select-service-name-operator": "Selecionar o operador do nome do serviço",
"aria-label-select-span-name": "Selecionar o nome do intervalo",
"aria-label-select-span-name-operator": "Selecionar o operador do nome do intervalo",
"ariaLabel-select-max-span-duration": "Selecionar a duração máxima do intervalo",
"ariaLabel-select-min-span-duration": "Selecionar a duração mínima do intervalo",
"label-collapse": "Filtros de intervalo",
"label-duration": "Duração",
"label-service-name": "Nome do serviço",
"label-span-name": "Nome do intervalo",
"label-tags": "Etiquetas",
"placeholder-all-service-names": "Todos os nomes de serviços",
"placeholder-all-span-names": "Todos os nomes de intervalos",
"tooltip-collapse": "Filtre os seus intervalos abaixo. Pode continuar a aplicar filtros até ter limitado os seus períodos resultantes para apenas alguns que lhe interessem mais.",
"tooltip-duration": "Filtrar por duração. As unidades aceites são {{units}}",
"tooltip-tags": "Filtrar por etiquetas, etiquetas de processo ou campos de registo nos seus intervalos."
},
"span-filters-tags": {
"aria-label-add-tag": "Adicionar etiqueta",
"aria-label-input-tag-value": "Valor da etiqueta de entrada",
"aria-label-remove-tag": "Remover controlo",
"aria-label-select-tag-key": "Selecionar a chave da etiqueta",
"aria-label-select-tag-operator": "Selecionar o operador da etiqueta",
"aria-label-select-tag-value": "Selecionar o valor da etiqueta",
"placeholder-select-tag": "Selecionar a etiqueta",
"placeholder-select-value": "Selecionar valor",
"placeholder-tag-value": "Valor da etiqueta",
"tooltip-add-tag": "Adicionar etiqueta",
"tooltip-remove-tag": "Remover controlo"
},
"span-flame-graph": {
"flame-graph": "Gráfico de chama"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Aumentar a quantidade de amostras",
"tooltip-s-m-h": "10s, 1m, 30m, 1h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Executar consulta",
"modal-title": "Editor SQL",
"tooltip-experimental": "A integração de LLM de expressões SQL é experimental. Comunique quaisquer problemas à equipa da Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Entrada"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Unidade",
"name-value-name": "Nome do valor",
"name-y-axis-scale": "Escala do eixo Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Automático",
"placeholder-axis-width": "Automático",
"placeholder-decimals": "Automático",
@@ -9422,6 +9414,18 @@
"label-all": "Tudo",
"label-hidden": "Oculto",
"label-single": "Único"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Ver detalhes",
"loading-finished-job": "A carregar o trabalho concluído...",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Último evento:",
"webhook-url": "Visualizar Webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Voltar aos repositórios",
"cleaning-up-resources": "A limpar recursos do repositório",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Não foi possível obter um repositório que não está em bom estado"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Aviso",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "A duração deste processo depende do número de recursos envolvidos.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Escolha as definições adicionais",
"step-synchronize": "Sincronizar com armazenamento externo",
"sync-description": "Sincronize recursos com um armazenamento externo. Após este passo único, todas as atualizações futuras serão guardadas automaticamente no repositório e aprovisionadas novamente para a instância.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Escolha o que sincronizar",
"title-connect": "Ligar a um armazenamento externo",
"title-finish": "Escolha as definições adicionais",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2688,6 +2688,35 @@
"text-federated": "Федеративная",
"text-provisioned": "Подготовлено"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Источник данных",
@@ -3749,6 +3778,7 @@
"text": "По вашему запросу ничего не найдено"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6222,7 +6252,7 @@
"title-someone-else-has-updated-this-dashboard": "Дашборд обновлен другим пользователем",
"would-still-dashboard": "Все равно сохранить дашборд?"
},
"save-and-overwrite": "'Сохранить и перезаписать'"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7211,6 +7241,9 @@
"time-range-label": "Заблокировать временной диапазон"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Совет: {{proTip}}"
},
@@ -7691,39 +7724,6 @@
},
"share-span": "Поделиться"
},
"span-filters": {
"aria-label-select-max-span-operator": "Выбрать оператора макс. диапазона",
"aria-label-select-min-span-operator": "Выбрать оператор мин. диапазона",
"aria-label-select-service-name": "Выбрать название службы",
"aria-label-select-service-name-operator": "Выбрать оператор названия службы",
"aria-label-select-span-name": "Выбрать название диапазона",
"aria-label-select-span-name-operator": "Выбрать оператора названия диапазона",
"ariaLabel-select-max-span-duration": "Выбрать макс. продолжительность интервала",
"ariaLabel-select-min-span-duration": "Выбрать мин. продолжительность интервала",
"label-collapse": "Фильтры диапазонов",
"label-duration": "Длительность",
"label-service-name": "Название службы",
"label-span-name": "Название диапазона",
"label-tags": "Теги",
"placeholder-all-service-names": "Все названия служб",
"placeholder-all-span-names": "Все названия диапазонов",
"tooltip-collapse": "Выполните фильтрацию своих диапазонов ниже. Вы можете продолжать применять фильтры, пока не сузите полученные диапазоны до нескольких наиболее важных для вас вариантов.",
"tooltip-duration": "Фильтр по длительности. Допустимые единицы измерения: {{units}}",
"tooltip-tags": "Фильтр по тегам, тегам процессов или полям журнала в ваших диапазонах."
},
"span-filters-tags": {
"aria-label-add-tag": "Добавить тег",
"aria-label-input-tag-value": "Ввести значение тега",
"aria-label-remove-tag": "Удалить тег",
"aria-label-select-tag-key": "Выбрать ключ тега",
"aria-label-select-tag-operator": "Выбрать оператор тега",
"aria-label-select-tag-value": "Выбрать значение тега",
"placeholder-select-tag": "Выбрать тег",
"placeholder-select-value": "Выбрать значение",
"placeholder-tag-value": "Значение тега",
"tooltip-add-tag": "Добавить тег",
"tooltip-remove-tag": "Удалить тег"
},
"span-flame-graph": {
"flame-graph": "Flame-график"
},
@@ -7922,23 +7922,18 @@
"label-upsample": "Увеличить частоту выборки",
"tooltip-s-m-h": "10 с, 1 мин, 30 мин, 1 ч"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Выполнить запрос",
"modal-title": "Редактор SQL",
"tooltip-experimental": "Интеграция LLM с SQL-выражениями является экспериментальной. При обнаружении проблем свяжитесь с командой Grafana."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Ввод"
@@ -8013,11 +8008,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9463,6 +9454,7 @@
"name-unit": "Единица",
"name-value-name": "Имя значения",
"name-y-axis-scale": "Шкала оси Y",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Авто",
"placeholder-axis-width": "Авто",
"placeholder-decimals": "Авто",
@@ -9492,6 +9484,18 @@
"label-all": "Все",
"label-hidden": "Скрыты",
"label-single": "Один"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11295,9 +11299,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -12074,7 +12083,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Подробнее",
"loading-finished-job": "Загрузка завершенного задания...",
@@ -12180,6 +12192,9 @@
"webhook-last-event": "Последнее событие:",
"webhook-url": "Просмотр веб-перехватчика"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Назад к репозиториям",
"cleaning-up-resources": "Очистка ресурсов репозитория",
@@ -12284,6 +12299,9 @@
"tooltip-unhealthy-repository": "Невозможно внести изменения в неисправный репозиторий"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12303,10 +12321,15 @@
},
"warning-title-default": "Предупреждение",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Длительность процесса зависит от количества задействованных ресурсов.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12325,6 +12348,7 @@
"step-finish": "Выберите дополнительные параметры",
"step-synchronize": "Синхронизировать с внешним хранилищем",
"sync-description": "Синхронизируйте ресурсы с внешним хранилищем. После этого разового шага все будущие обновления будут автоматически сохраняться в репозитории и загружаться обратно в экземпляр.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Выберите, что синхронизировать",
"title-connect": "Подключение к внешнему хранилищу",
"title-finish": "Выбор дополнительных параметров",
@@ -12564,16 +12588,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Federerade",
"text-provisioned": "Provisionerad"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Datakälla",
@@ -3717,6 +3746,7 @@
"text": "Inga resultat hittades för din fråga"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Någon annan har uppdaterat denna instrumentpanel",
"would-still-dashboard": "Vill du fortfarande spara denna instrumentpanel?"
},
"save-and-overwrite": "”Spara och skriv över”"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Lås tidsintervall"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "ProTip: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Dela"
},
"span-filters": {
"aria-label-select-max-span-operator": "Välj operator för maximalt intervall",
"aria-label-select-min-span-operator": "Välj operator för minsta intervall",
"aria-label-select-service-name": "Välj namn för tjänst",
"aria-label-select-service-name-operator": "Välj operator för tjänstnamn",
"aria-label-select-span-name": "Välj namn på intervall",
"aria-label-select-span-name-operator": "Välj operator för intervallnamn",
"ariaLabel-select-max-span-duration": "Välj maximal varaktighet för tidsspann",
"ariaLabel-select-min-span-duration": "Välj minimal varaktighet för tidsspann",
"label-collapse": "Intervallfilter",
"label-duration": "Varaktighet",
"label-service-name": "Namn på tjänst",
"label-span-name": "Intervallnamn",
"label-tags": "Taggar",
"placeholder-all-service-names": "Alla servicenamn",
"placeholder-all-span-names": "Alla namn på intervall",
"tooltip-collapse": "Filtrera dina spann nedan. Du kan fortsätta att tillämpa filter tills du har begränsat dina resulterande spann till det fåtal som du är mest intresserad av.",
"tooltip-duration": "Filtrera per varaktighet. Accepterade enheter är {{units}}",
"tooltip-tags": "Filtrera efter taggar, processtaggar eller loggfält i dina spår."
},
"span-filters-tags": {
"aria-label-add-tag": "Lägg till etikett",
"aria-label-input-tag-value": "Ange taggvärde",
"aria-label-remove-tag": "Ta bort tagg",
"aria-label-select-tag-key": "Välj etikettnyckel",
"aria-label-select-tag-operator": "Välj etikettoperator",
"aria-label-select-tag-value": "Välj etikettvärde",
"placeholder-select-tag": "Välj etikett",
"placeholder-select-value": "Välj värde",
"placeholder-tag-value": "Etikettvärde",
"tooltip-add-tag": "Lägg till etikett",
"tooltip-remove-tag": "Ta bort tagg"
},
"span-flame-graph": {
"flame-graph": "Flamgraf"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Sampla upp",
"tooltip-s-m-h": "10 s, 1 m, 30 m, 1 h"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "Kör fråga",
"modal-title": "SQL-redigerare",
"tooltip-experimental": "Integreringen för SQL Expressions LLM är i ett experimentellt skede. Rapportera alla problem du stöter på till Grafana-teamet."
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Ingång"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Enhet",
"name-value-name": "Värdenamn",
"name-y-axis-scale": "Y-axelns skala",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Auto",
"placeholder-axis-width": "Auto",
"placeholder-decimals": "Auto",
@@ -9422,6 +9414,18 @@
"label-all": "Alla",
"label-hidden": "Dolt",
"label-single": "Singel"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Visa detaljer",
"loading-finished-job": "Läser in färdigt jobb …",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Senaste händelsen:",
"webhook-url": "Visa webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Tillbaka till lagringsplatserna",
"cleaning-up-resources": "Rensa lagringsplatsresurser",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "Det gick inte att hämta en ohälsosam lagringsplats"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Varning",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Hur lång den här processen är beror på hur många resurser som är inblandade.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Välj ytterligare inställningar",
"step-synchronize": "Synkronisera med extern lagring",
"sync-description": "Synkronisera resurser med extern lagring. Efter det här engångssteget sparas alla framtida uppdateringar automatiskt på lagringsplatsen och provisioneras tillbaka till instansen.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Välj vad du vill synkronisera",
"title-connect": "Anslut till extern lagring",
"title-finish": "Välj ytterligare inställningar",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2666,6 +2666,35 @@
"text-federated": "Şirket dışı",
"text-provisioned": "Sağlanan"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "Veri kaynağı",
@@ -3717,6 +3746,7 @@
"text": "Sorgunuz için sonuç bulunamadı"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6180,7 +6210,7 @@
"title-someone-else-has-updated-this-dashboard": "Başka biri bu panoyu güncelledi",
"would-still-dashboard": "Yine de bu panoyu kaydetmek istiyor musunuz?"
},
"save-and-overwrite": "\"Kaydet ve üzerine yaz\""
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7165,6 +7195,9 @@
"time-range-label": "Zaman aralığını kilitle"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "Uzman ipucu: {{proTip}}"
},
@@ -7641,39 +7674,6 @@
},
"share-span": "Paylaş"
},
"span-filters": {
"aria-label-select-max-span-operator": "Maksimum zaman aralığı işlecini seçin",
"aria-label-select-min-span-operator": "Minimum zaman aralığı işlecini seçin",
"aria-label-select-service-name": "Hizmet adını seç",
"aria-label-select-service-name-operator": "Hizmet adını işlecini seç",
"aria-label-select-span-name": "Zaman aralığı adı seç",
"aria-label-select-span-name-operator": "Zaman aralığı adı işleci seçin",
"ariaLabel-select-max-span-duration": "Maksimum zaman aralığı süresini seçin",
"ariaLabel-select-min-span-duration": "Minimum zaman aralığı süresini seçin",
"label-collapse": "Zaman aralığı filtreleri",
"label-duration": "Süre",
"label-service-name": "Hizmet adı",
"label-span-name": "Zaman aralığı adı",
"label-tags": "Etiketler",
"placeholder-all-service-names": "Tüm hizmet adları",
"placeholder-all-span-names": "Tüm zaman aralığı adları",
"tooltip-collapse": "Aşağıdaki zaman aralıklarınızı filtreleyin. İlginizi en çok çeken birkaç sonucu daraltana kadar filtre uygulamaya devam edebilirsiniz.",
"tooltip-duration": "Süreye göre filtreleyin. Kabul edilen birimler: {{units}}",
"tooltip-tags": "Zamanlardaki etiketlere, işlem etiketlerine veya günlük kaydı alanlarına göre filtreleyin."
},
"span-filters-tags": {
"aria-label-add-tag": "Etiket ekle",
"aria-label-input-tag-value": "Etiket değeri girin",
"aria-label-remove-tag": "Etiketi kaldır",
"aria-label-select-tag-key": "Etiket anahtarını seçin",
"aria-label-select-tag-operator": "Etiket işlecini seçin",
"aria-label-select-tag-value": "Etiket değerini seçin",
"placeholder-select-tag": "Etiket seçin",
"placeholder-select-value": "Değer seçin",
"placeholder-tag-value": "Etiket değeri",
"tooltip-add-tag": "Etiket ekle",
"tooltip-remove-tag": "Etiketi kaldır"
},
"span-flame-graph": {
"flame-graph": "Alev grafiği"
},
@@ -7872,23 +7872,18 @@
"label-upsample": "Örneklemeyi artır",
"tooltip-s-m-h": "10 sn, 1 dk, 30 dk, 1 sa"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "",
"modal-title": "",
"tooltip-experimental": ""
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "Girdi"
@@ -7963,11 +7958,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9393,6 +9384,7 @@
"name-unit": "Birim",
"name-value-name": "Değer adı",
"name-y-axis-scale": "Y ekseni ölçeği",
"name-y-bucket-scale": "",
"placeholder-axis-label": "Otomatik",
"placeholder-axis-width": "Otomatik",
"placeholder-decimals": "Otomatik",
@@ -9422,6 +9414,18 @@
"label-all": "Tümü",
"label-hidden": "Gizli",
"label-single": "Tek"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11203,9 +11207,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11972,7 +11981,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "Ayrıntıları görüntüleyin",
"loading-finished-job": "Tamamlanan iş yükleniyor...",
@@ -12078,6 +12090,9 @@
"webhook-last-event": "Son Olay:",
"webhook-url": "Web Kancasını Görüntüle"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "Depolara geri dön",
"cleaning-up-resources": "Depo kaynakları temizleniyor",
@@ -12182,6 +12197,9 @@
"tooltip-unhealthy-repository": "İyi durumda olmayan bir depo çekilemedi"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12201,10 +12219,15 @@
},
"warning-title-default": "Uyarı",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "Bu işlemin süresi dâhil olan kaynakların sayısına bağlıdır.",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12223,6 +12246,7 @@
"step-finish": "Ek ayarlar seçin",
"step-synchronize": "Harici depolama ile senkronize et",
"sync-description": "Kaynakları harici depolama ile senkronize edin. Bu tek seferlik adımdan sonra tüm gelecekteki güncellemeler otomatik olarak depoya kaydedilecek ve örneğe geri sağlanacaktır.",
"sync-option-migrate-resources": "",
"title-bootstrap": "Nelerin senkronize edileceğini seçin",
"title-connect": "Harici depolamaya bağlan",
"title-finish": "Ek ayarlar seçin",
@@ -12460,16 +12484,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2655,6 +2655,35 @@
"text-federated": "联合",
"text-provisioned": "已预置"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "数据源",
@@ -3701,6 +3730,7 @@
"text": "未找到与您的查询相关的结果"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6159,7 +6189,7 @@
"title-someone-else-has-updated-this-dashboard": "其他人已更新此数据面板",
"would-still-dashboard": "您仍然要保存此数据面板吗?"
},
"save-and-overwrite": "‘保存并覆盖’"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7142,6 +7172,9 @@
"time-range-label": "锁定时间范围"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "专业提示:{{proTip}}"
},
@@ -7616,39 +7649,6 @@
},
"share-span": "共享"
},
"span-filters": {
"aria-label-select-max-span-operator": "选择最大跨度运算符",
"aria-label-select-min-span-operator": "选择最小跨度运算符",
"aria-label-select-service-name": "选择服务名称",
"aria-label-select-service-name-operator": "选择服务名称运算符",
"aria-label-select-span-name": "选择跨度名称",
"aria-label-select-span-name-operator": "选择跨度名称运算符",
"ariaLabel-select-max-span-duration": "选择最大跨度持续时间",
"ariaLabel-select-min-span-duration": "选择最小跨度持续时间",
"label-collapse": "跨度筛选器",
"label-duration": "持续时间",
"label-service-name": "服务名称",
"label-span-name": "跨度名称",
"label-tags": "标签",
"placeholder-all-service-names": "所有服务名称",
"placeholder-all-span-names": "所有跨度名称",
"tooltip-collapse": "在下方筛选您的跨度。您可以继续应用筛选条件,直到将结果跨度缩小到您最感兴趣的少数几个。",
"tooltip-duration": "按持续时间筛选。可接受的单位是 {{units}}",
"tooltip-tags": "根据跨度中的标记、流程标记或日志字段进行筛选。"
},
"span-filters-tags": {
"aria-label-add-tag": "添加标记",
"aria-label-input-tag-value": "输入标记值",
"aria-label-remove-tag": "移除标记",
"aria-label-select-tag-key": "选择标记键",
"aria-label-select-tag-operator": "选择标记运算符",
"aria-label-select-tag-value": "选择标记值",
"placeholder-select-tag": "选择标记",
"placeholder-select-value": "选择值",
"placeholder-tag-value": "标记值",
"tooltip-add-tag": "添加标记",
"tooltip-remove-tag": "移除标记"
},
"span-flame-graph": {
"flame-graph": "火焰图"
},
@@ -7847,23 +7847,18 @@
"label-upsample": "升高采样",
"tooltip-s-m-h": "10 秒、1 分钟、30 分钟、1 小时"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "运行查询",
"modal-title": "SQL 编辑器",
"tooltip-experimental": "SQL 表达式 LLM 集成是实验性的。若有任何问题,请向 Grafana 团队报告。"
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "输入"
@@ -7938,11 +7933,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9358,6 +9349,7 @@
"name-unit": "单位",
"name-value-name": "值名称",
"name-y-axis-scale": "Y 轴比例",
"name-y-bucket-scale": "",
"placeholder-axis-label": "自动",
"placeholder-axis-width": "自动",
"placeholder-decimals": "自动",
@@ -9387,6 +9379,18 @@
"label-all": "全部",
"label-hidden": "隐藏",
"label-single": "单一"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11157,9 +11161,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11921,7 +11930,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "查看详情",
"loading-finished-job": "正在加载已完成的作业...",
@@ -12027,6 +12039,9 @@
"webhook-last-event": "最后一个事件:",
"webhook-url": "查看 Webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "回到存储库",
"cleaning-up-resources": "清理存储库资源",
@@ -12131,6 +12146,9 @@
"tooltip-unhealthy-repository": "无法拉取状态不良的存储库"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12150,10 +12168,15 @@
},
"warning-title-default": "警告",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "此过程的持续时间取决于所涉及资源的数量。",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12172,6 +12195,7 @@
"step-finish": "选择附加设置",
"step-synchronize": "与外部存储同步",
"sync-description": "将资源与外部存储同步。完成此一次性步骤后,所有后续更新都将自动保存到存储库中,并预配回实例。",
"sync-option-migrate-resources": "",
"title-bootstrap": "选择要同步的内容",
"title-connect": "连接到外部存储",
"title-finish": "选择附加设置",
@@ -12408,16 +12432,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",
+82 -53
View File
@@ -2655,6 +2655,35 @@
"text-federated": "聯合",
"text-provisioned": "已佈建"
},
"saved-searches": {
"actions-aria-label": "",
"apply-aria-label": "",
"apply-tooltip": "",
"button-label": "",
"cancel": "",
"default-indicator": "",
"delete": "",
"delete-button": "",
"dropdown-aria-label": "",
"empty-state": "",
"error-load-description": "",
"error-load-title": "",
"error-name-duplicate": "",
"error-name-required": "",
"error-rename-description": "",
"error-rename-title": "",
"error-save-description": "",
"error-save-title": "",
"list-aria-label": "",
"name-placeholder": "",
"remove-default": "",
"rename": "",
"rename-button": "",
"save-button": "",
"save-current-search": "",
"save-disabled-tooltip": "",
"set-default": ""
},
"search": {
"property": {
"data-source": "資料來源",
@@ -3701,6 +3730,7 @@
"text": "未找到您的查詢結果"
},
"recently-viewed": {
"clear": "",
"empty": "",
"title": ""
},
@@ -6159,7 +6189,7 @@
"title-someone-else-has-updated-this-dashboard": "其他人已更新此儀表板",
"would-still-dashboard": "仍要儲存此儀表板嗎?"
},
"save-and-overwrite": "「儲存並覆寫」"
"save-and-overwrite": ""
},
"library-viz-panel-info": {
"last-edited": "",
@@ -7142,6 +7172,9 @@
"time-range-label": "鎖定時間範圍"
}
},
"embedded-panel": {
"powered-by": ""
},
"empty-list-cta": {
"pro-tip": "專業提示:{{proTip}}"
},
@@ -7616,39 +7649,6 @@
},
"share-span": "分享"
},
"span-filters": {
"aria-label-select-max-span-operator": "選擇最大範圍的運算子",
"aria-label-select-min-span-operator": "選取最小範圍的運算子",
"aria-label-select-service-name": "選取服務名稱",
"aria-label-select-service-name-operator": "選取服務名稱運算子",
"aria-label-select-span-name": "選取範圍名稱",
"aria-label-select-span-name-operator": "選擇範圍名稱運算子",
"ariaLabel-select-max-span-duration": "選擇最大跨度持續時間",
"ariaLabel-select-min-span-duration": "選取最小跨度持續時間",
"label-collapse": "範圍篩選器",
"label-duration": "持續時間",
"label-service-name": "服務名稱",
"label-span-name": "範圍名稱",
"label-tags": "標籤",
"placeholder-all-service-names": "所有服務名稱",
"placeholder-all-span-names": "所有範圍名稱",
"tooltip-collapse": "篩選下方的範圍。您可以繼續套用篩選條件,直到將結果範圍縮小到您最感興趣的幾個範圍。",
"tooltip-duration": "依持續時間篩選。可接受的單元為 {{units}}",
"tooltip-tags": "按範圍中的標記、流程標記或紀錄欄位篩選。"
},
"span-filters-tags": {
"aria-label-add-tag": "新增標記",
"aria-label-input-tag-value": "輸入標記值",
"aria-label-remove-tag": "移除標記",
"aria-label-select-tag-key": "選擇標記鍵",
"aria-label-select-tag-operator": "選擇標記運算子",
"aria-label-select-tag-value": "選擇標記值",
"placeholder-select-tag": "選擇標記",
"placeholder-select-value": "選擇值",
"placeholder-tag-value": "標記值",
"tooltip-add-tag": "新增標記",
"tooltip-remove-tag": "移除標記"
},
"span-flame-graph": {
"flame-graph": "火焰圖"
},
@@ -7847,23 +7847,18 @@
"label-upsample": "向上取樣",
"tooltip-s-m-h": "10 秒、1 分鐘、30 分鐘、1 小時"
},
"schema-inspector-panel": {
"aria-label-close-schema-inspector": ""
},
"sql-expr": {
"button-run-query": "執行查詢",
"modal-title": "SQL 編輯器",
"tooltip-experimental": "SQL 運算式 LLM 整合為實驗性功能。如有任何問題,請向 Grafana 團隊回報。"
},
"sql-schema": {
"close-schema-inspector": "",
"error-title": "",
"inspect-button": "",
"loading": "",
"no-data-title": "",
"no-fields-desc": "",
"no-fields-title": "",
"query-error-title": ""
"query-error-title": "",
"schema-inspector": ""
},
"threshold": {
"label-input": "輸入"
@@ -7938,11 +7933,7 @@
"suggestions": {
"arc": "",
"circular": "",
"no-thresholds": "",
"style": {
"circular": "",
"simple": ""
}
"no-thresholds": ""
},
"threshold": ""
},
@@ -9358,6 +9349,7 @@
"name-unit": "單位",
"name-value-name": "值名稱",
"name-y-axis-scale": "Y 軸刻度",
"name-y-bucket-scale": "",
"placeholder-axis-label": "自動",
"placeholder-axis-width": "自動",
"placeholder-decimals": "自動",
@@ -9387,6 +9379,18 @@
"label-all": "全部",
"label-hidden": "隱藏",
"label-single": "單一"
},
"y-bucket-scale-editor": {
"linear-threshold-description": "",
"linear-threshold-label": "",
"linear-threshold-placeholder": "",
"log-base-label": "",
"scale-options": {
"label-auto": "",
"label-linear": "",
"label-log": "",
"label-symlog": ""
}
}
},
"help-modal": {
@@ -11157,9 +11161,14 @@
},
"visualization-suggestions": {
"apply-suggestion-aria-label": "",
"error-loading-some-suggestions": {
"message": ""
},
"error-loading-suggestions": {
"message": "",
"title": ""
"plugin-failed": "",
"title": "",
"try-again-button": ""
},
"unknown-viz-type": "",
"use-this-suggestion": ""
@@ -11921,7 +11930,10 @@
"resource-not-found": "",
"unsupported-repository-type": ""
},
"inline-secure-values-warning": "",
"instance-sync-deprecation": {
"message": "",
"title": ""
},
"job-status": {
"label-view-details": "檢視詳細資料",
"loading-finished-job": "正在載入已完成的作業…",
@@ -12027,6 +12039,9 @@
"webhook-last-event": "上次事件:",
"webhook-url": "檢視 Webhook"
},
"repository-status": {
"error": ""
},
"repository-status-page": {
"back-to-repositories": "返回至儲存庫",
"cleaning-up-resources": "清理儲存庫資源",
@@ -12131,6 +12146,9 @@
"tooltip-unhealthy-repository": "無法拉取狀態不佳的儲存庫"
},
"synchronize-step": {
"instance-migrate-resources-description": "",
"migrate-resources-description": "",
"options": "",
"repository-error": "",
"repository-error-message": "",
"repository-unhealthy": ""
@@ -12150,10 +12168,15 @@
},
"warning-title-default": "警告",
"wizard": {
"alert-intro": "",
"alert-point-1": "",
"alert-point-2": "",
"alert-point-3": "此流程的持續時間取決於所涉及的資源數量。",
"alert-point-4": "",
"alert-point-folder-cleanup": "",
"alert-point-folder-structure": "",
"alert-point-instance-alerts": "",
"alert-point-permissions": "",
"alert-point-unsupported": "",
"alert-title": "",
"button-cancel": "",
"button-cancelling": "",
@@ -12172,6 +12195,7 @@
"step-finish": "選擇附加設定",
"step-synchronize": "與外部儲存空間同步",
"sync-description": "將資源與外部儲存空間同步。在此一次性步驟之後,未來的所有更新都將自動儲存到儲存庫中,並佈建回執行個體。",
"sync-option-migrate-resources": "",
"title-bootstrap": "選擇要同步的內容",
"title-connect": "連接到外部儲存空間",
"title-finish": "選擇附加設定",
@@ -12408,16 +12432,21 @@
},
"radialbar": {
"config": {
"bar-shape": "",
"bar-shape-flat": "",
"bar-shape-rounded": "",
"bar-width": "",
"effects": {
"bar-glow": "",
"center-glow": "",
"gradient": "",
"label": "",
"rounded-bars": "",
"spotlight": "",
"spotlight-tooltip": ""
"label": ""
},
"endpoint-marker": "",
"endpoint-marker-description": "",
"endpoint-marker-glow": "",
"endpoint-marker-none": "",
"endpoint-marker-point": "",
"segment-count": "",
"segment-spacing": "",
"shape": "",