mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashbboardScene: RowRepeaterBehavior (#74505)
* Repeating rows start * working * Progress * Progress * Update * up scenes lib * Update * Progress * restore url sync * Progress * Fixes and tests * Update * Adds tests and code to remove repeats from save model * Update * Fix test
This commit is contained in:
parent
fb367bf91d
commit
97d568e60a
@ -1899,9 +1899,17 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||||
],
|
],
|
||||||
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts:5381": [
|
"public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts:5381": [
|
||||||
|
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||||
|
],
|
||||||
|
"public/app/features/dashboard-scene/serialization/transformSaveModelToScene.test.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||||
],
|
],
|
||||||
|
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.test.ts:5381": [
|
||||||
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
|
[0, 0, 0, "Unexpected any. Specify a different type.", "2"]
|
||||||
|
],
|
||||||
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts:5381": [
|
"public/app/features/dashboard-scene/serialization/transformSceneToSaveModel.ts:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||||
],
|
],
|
||||||
|
@ -1,334 +0,0 @@
|
|||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"builtIn": 1,
|
|
||||||
"datasource": "-- Grafana --",
|
|
||||||
"enable": true,
|
|
||||||
"hide": true,
|
|
||||||
"iconColor": "rgba(0, 211, 255, 1)",
|
|
||||||
"name": "Annotations & Alerts",
|
|
||||||
"target": {
|
|
||||||
"limit": 100,
|
|
||||||
"matchAny": false,
|
|
||||||
"tags": [],
|
|
||||||
"type": "dashboard"
|
|
||||||
},
|
|
||||||
"type": "dashboard"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"fiscalYearStartMonth": 0,
|
|
||||||
"graphTooltip": 0,
|
|
||||||
"iteration": 1640181176989,
|
|
||||||
"links": [],
|
|
||||||
"liveNow": false,
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"collapsed": false,
|
|
||||||
"gridPos": {
|
|
||||||
"h": 1,
|
|
||||||
"w": 24,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 2,
|
|
||||||
"panels": [],
|
|
||||||
"repeat": "row",
|
|
||||||
"title": "Row title $row",
|
|
||||||
"type": "row"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": {
|
|
||||||
"type": "testdata"
|
|
||||||
},
|
|
||||||
"description": "",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 0,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"legend": false,
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "auto",
|
|
||||||
"spanNulls": false,
|
|
||||||
"stacking": {
|
|
||||||
"group": "A",
|
|
||||||
"mode": "none"
|
|
||||||
},
|
|
||||||
"thresholdsStyle": {
|
|
||||||
"mode": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 80
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 8,
|
|
||||||
"x": 0,
|
|
||||||
"y": 1
|
|
||||||
},
|
|
||||||
"id": 4,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "single",
|
|
||||||
"sort": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Panel Title",
|
|
||||||
"type": "timeseries"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": {
|
|
||||||
"type": "testdata"
|
|
||||||
},
|
|
||||||
"description": "",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 0,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"legend": false,
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "auto",
|
|
||||||
"spanNulls": false,
|
|
||||||
"stacking": {
|
|
||||||
"group": "A",
|
|
||||||
"mode": "none"
|
|
||||||
},
|
|
||||||
"thresholdsStyle": {
|
|
||||||
"mode": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 80
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 8,
|
|
||||||
"x": 8,
|
|
||||||
"y": 1
|
|
||||||
},
|
|
||||||
"id": 9,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "single",
|
|
||||||
"sort": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"repeat": "vertical",
|
|
||||||
"repeatDirection": "v",
|
|
||||||
"title": "Vertical repeating $vertical",
|
|
||||||
"type": "timeseries"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"schemaVersion": 34,
|
|
||||||
"tags": [],
|
|
||||||
"templating": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "vertical",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "horizontal",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "row",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-6h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"timepicker": {},
|
|
||||||
"timezone": "utc",
|
|
||||||
"title": "Repeating a row with a non-repeating panel and vertical repeating panel",
|
|
||||||
"uid": "7lS-ojt7z",
|
|
||||||
"version": 2,
|
|
||||||
"weekStart": ""
|
|
||||||
}
|
|
@ -1,257 +0,0 @@
|
|||||||
{
|
|
||||||
"annotations": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"builtIn": 1,
|
|
||||||
"datasource": "-- Grafana --",
|
|
||||||
"enable": true,
|
|
||||||
"hide": true,
|
|
||||||
"iconColor": "rgba(0, 211, 255, 1)",
|
|
||||||
"name": "Annotations & Alerts",
|
|
||||||
"target": {
|
|
||||||
"limit": 100,
|
|
||||||
"matchAny": false,
|
|
||||||
"tags": [],
|
|
||||||
"type": "dashboard"
|
|
||||||
},
|
|
||||||
"type": "dashboard"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"editable": true,
|
|
||||||
"fiscalYearStartMonth": 0,
|
|
||||||
"graphTooltip": 0,
|
|
||||||
"iteration": 1640181195825,
|
|
||||||
"links": [],
|
|
||||||
"liveNow": false,
|
|
||||||
"panels": [
|
|
||||||
{
|
|
||||||
"collapsed": false,
|
|
||||||
"gridPos": {
|
|
||||||
"h": 1,
|
|
||||||
"w": 24,
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
},
|
|
||||||
"id": 2,
|
|
||||||
"panels": [],
|
|
||||||
"repeat": "row",
|
|
||||||
"title": "Row title $row",
|
|
||||||
"type": "row"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"datasource": {
|
|
||||||
"type": "testdata"
|
|
||||||
},
|
|
||||||
"description": "",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 0,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"legend": false,
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "auto",
|
|
||||||
"spanNulls": false,
|
|
||||||
"stacking": {
|
|
||||||
"group": "A",
|
|
||||||
"mode": "none"
|
|
||||||
},
|
|
||||||
"thresholdsStyle": {
|
|
||||||
"mode": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 80
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"gridPos": {
|
|
||||||
"h": 8,
|
|
||||||
"w": 8,
|
|
||||||
"x": 0,
|
|
||||||
"y": 1
|
|
||||||
},
|
|
||||||
"id": 4,
|
|
||||||
"options": {
|
|
||||||
"legend": {
|
|
||||||
"calcs": [],
|
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom"
|
|
||||||
},
|
|
||||||
"tooltip": {
|
|
||||||
"mode": "single",
|
|
||||||
"sort": "none"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Panel Title",
|
|
||||||
"type": "timeseries"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"schemaVersion": 34,
|
|
||||||
"tags": [],
|
|
||||||
"templating": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "vertical",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "horizontal",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "row",
|
|
||||||
"options": [
|
|
||||||
{
|
|
||||||
"selected": true,
|
|
||||||
"text": "All",
|
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"time": {
|
|
||||||
"from": "now-6h",
|
|
||||||
"to": "now"
|
|
||||||
},
|
|
||||||
"timepicker": {},
|
|
||||||
"timezone": "utc",
|
|
||||||
"title": "Repeating a row with a non-repeating panel",
|
|
||||||
"uid": "ZzyTojpnz",
|
|
||||||
"version": 3,
|
|
||||||
"weekStart": ""
|
|
||||||
}
|
|
@ -4,19 +4,13 @@
|
|||||||
{
|
{
|
||||||
"builtIn": 1,
|
"builtIn": 1,
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "datasource",
|
"type": "grafana",
|
||||||
"uid": "grafana"
|
"uid": "-- Grafana --"
|
||||||
},
|
},
|
||||||
"enable": true,
|
"enable": true,
|
||||||
"hide": true,
|
"hide": true,
|
||||||
"iconColor": "rgba(0, 211, 255, 1)",
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
"name": "Annotations & Alerts",
|
"name": "Annotations & Alerts",
|
||||||
"target": {
|
|
||||||
"limit": 100,
|
|
||||||
"matchAny": false,
|
|
||||||
"tags": [],
|
|
||||||
"type": "dashboard"
|
|
||||||
},
|
|
||||||
"type": "dashboard"
|
"type": "dashboard"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -24,41 +18,72 @@
|
|||||||
"editable": true,
|
"editable": true,
|
||||||
"fiscalYearStartMonth": 0,
|
"fiscalYearStartMonth": 0,
|
||||||
"graphTooltip": 0,
|
"graphTooltip": 0,
|
||||||
"id": 85,
|
|
||||||
"links": [],
|
"links": [],
|
||||||
"liveNow": false,
|
"liveNow": false,
|
||||||
"panels": [
|
"panels": [
|
||||||
{
|
{
|
||||||
"collapsed": false,
|
"collapsed": false,
|
||||||
"datasource": {
|
|
||||||
"type": "testdata",
|
|
||||||
"uid": "PD8C576611E62080A"
|
|
||||||
},
|
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 1,
|
"h": 1,
|
||||||
"w": 24,
|
"w": 24,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 0
|
"y": 0
|
||||||
},
|
},
|
||||||
"id": 2,
|
"id": 20,
|
||||||
"panels": [],
|
"panels": [],
|
||||||
"repeat": "row",
|
"title": "Row at the top - not repeated - saved expanded",
|
||||||
"title": "Row $row",
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gridPos": {
|
||||||
|
"h": 2,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"id": 15,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false
|
||||||
|
},
|
||||||
|
"content": "<div class=\"center-vh\">\n Repeated row below. The row has \n a panel that is also repeated horizontally based\n on values in the $pod variable. \n</div>",
|
||||||
|
"mode": "markdown"
|
||||||
|
},
|
||||||
|
"pluginVersion": "10.2.0-pre",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 3
|
||||||
|
},
|
||||||
|
"id": 16,
|
||||||
|
"panels": [],
|
||||||
|
"repeat": "server",
|
||||||
|
"repeatDirection": "h",
|
||||||
|
"title": "Row for server $server",
|
||||||
"type": "row"
|
"type": "row"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"datasource": {
|
"datasource": {
|
||||||
"type": "testdata"
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
},
|
},
|
||||||
"description": "",
|
|
||||||
"fieldConfig": {
|
"fieldConfig": {
|
||||||
"defaults": {
|
"defaults": {
|
||||||
"color": {
|
"color": {
|
||||||
"mode": "palette-classic"
|
"mode": "palette-classic"
|
||||||
},
|
},
|
||||||
"custom": {
|
"custom": {
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
"axisLabel": "",
|
"axisLabel": "",
|
||||||
"axisPlacement": "auto",
|
"axisPlacement": "auto",
|
||||||
|
"axisShow": false,
|
||||||
"barAlignment": 0,
|
"barAlignment": 0,
|
||||||
"drawStyle": "line",
|
"drawStyle": "line",
|
||||||
"fillOpacity": 0,
|
"fillOpacity": 0,
|
||||||
@ -68,6 +93,7 @@
|
|||||||
"tooltip": false,
|
"tooltip": false,
|
||||||
"viz": false
|
"viz": false
|
||||||
},
|
},
|
||||||
|
"insertNulls": false,
|
||||||
"lineInterpolation": "linear",
|
"lineInterpolation": "linear",
|
||||||
"lineWidth": 1,
|
"lineWidth": 1,
|
||||||
"pointSize": 5,
|
"pointSize": 5,
|
||||||
@ -89,7 +115,8 @@
|
|||||||
"mode": "absolute",
|
"mode": "absolute",
|
||||||
"steps": [
|
"steps": [
|
||||||
{
|
{
|
||||||
"color": "green"
|
"color": "green",
|
||||||
|
"value": null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"color": "red",
|
"color": "red",
|
||||||
@ -101,145 +128,174 @@
|
|||||||
"overrides": []
|
"overrides": []
|
||||||
},
|
},
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 8,
|
"h": 6,
|
||||||
"w": 8,
|
"w": 12,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 1
|
"y": 4
|
||||||
},
|
},
|
||||||
"id": 4,
|
"id": 2,
|
||||||
|
"maxPerRow": 3,
|
||||||
"options": {
|
"options": {
|
||||||
"legend": {
|
"legend": {
|
||||||
"calcs": [],
|
"calcs": [],
|
||||||
"displayMode": "list",
|
"displayMode": "list",
|
||||||
"placement": "bottom"
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
},
|
},
|
||||||
"tooltip": {
|
"tooltip": {
|
||||||
"mode": "single",
|
"mode": "single",
|
||||||
"sort": "none"
|
"sort": "none"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"title": "Row $row non-repeating panel",
|
"repeat": "pod",
|
||||||
|
"repeatDirection": "h",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "server = $server, pod id = $pod ",
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"seriesCount": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "server = $server, pod = $pod",
|
||||||
"type": "timeseries"
|
"type": "timeseries"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"datasource": {
|
"collapsed": true,
|
||||||
"type": "testdata"
|
|
||||||
},
|
|
||||||
"description": "",
|
|
||||||
"fieldConfig": {
|
|
||||||
"defaults": {
|
|
||||||
"color": {
|
|
||||||
"mode": "palette-classic"
|
|
||||||
},
|
|
||||||
"custom": {
|
|
||||||
"axisLabel": "",
|
|
||||||
"axisPlacement": "auto",
|
|
||||||
"barAlignment": 0,
|
|
||||||
"drawStyle": "line",
|
|
||||||
"fillOpacity": 0,
|
|
||||||
"gradientMode": "none",
|
|
||||||
"hideFrom": {
|
|
||||||
"legend": false,
|
|
||||||
"tooltip": false,
|
|
||||||
"viz": false
|
|
||||||
},
|
|
||||||
"lineInterpolation": "linear",
|
|
||||||
"lineWidth": 1,
|
|
||||||
"pointSize": 5,
|
|
||||||
"scaleDistribution": {
|
|
||||||
"type": "linear"
|
|
||||||
},
|
|
||||||
"showPoints": "auto",
|
|
||||||
"spanNulls": false,
|
|
||||||
"stacking": {
|
|
||||||
"group": "A",
|
|
||||||
"mode": "none"
|
|
||||||
},
|
|
||||||
"thresholdsStyle": {
|
|
||||||
"mode": "off"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mappings": [],
|
|
||||||
"thresholds": {
|
|
||||||
"mode": "absolute",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"color": "green"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"color": "red",
|
|
||||||
"value": 80
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overrides": []
|
|
||||||
},
|
|
||||||
"gridPos": {
|
"gridPos": {
|
||||||
"h": 8,
|
"h": 1,
|
||||||
"w": 8,
|
"w": 24,
|
||||||
"x": 0,
|
"x": 0,
|
||||||
"y": 9
|
"y": 21
|
||||||
},
|
},
|
||||||
"id": 9,
|
"id": 25,
|
||||||
"options": {
|
"panels": [
|
||||||
"legend": {
|
{
|
||||||
"calcs": [],
|
"gridPos": {
|
||||||
"displayMode": "list",
|
"h": 2,
|
||||||
"placement": "bottom"
|
"w": 24,
|
||||||
},
|
"x": 0,
|
||||||
"tooltip": {
|
"y": 22
|
||||||
"mode": "single",
|
},
|
||||||
"sort": "none"
|
"id": 30,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false
|
||||||
|
},
|
||||||
|
"content": "<div class=\"center-vh\">\n Just a panel\n</div>",
|
||||||
|
"mode": "markdown"
|
||||||
|
},
|
||||||
|
"pluginVersion": "10.2.0-pre",
|
||||||
|
"type": "text"
|
||||||
}
|
}
|
||||||
},
|
],
|
||||||
"repeat": "horizontal",
|
"title": "Row at the bottom - not repeated - saved collapsed ",
|
||||||
"repeatDirection": "h",
|
"type": "row"
|
||||||
"title": "Row $row repeating panel $horizontal",
|
|
||||||
"type": "timeseries"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"schemaVersion": 36,
|
"refresh": "",
|
||||||
"tags": [],
|
"schemaVersion": 38,
|
||||||
|
"tags": [
|
||||||
|
"templating",
|
||||||
|
"gdev"
|
||||||
|
],
|
||||||
"templating": {
|
"templating": {
|
||||||
"list": [
|
"list": [
|
||||||
{
|
{
|
||||||
"current": {
|
"current": {
|
||||||
"selected": true,
|
"selected": true,
|
||||||
"text": [
|
"text": [
|
||||||
"All"
|
"A",
|
||||||
|
"B"
|
||||||
],
|
],
|
||||||
"value": [
|
"value": [
|
||||||
"$__all"
|
"A",
|
||||||
|
"B"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hide": 0,
|
"hide": 0,
|
||||||
"includeAll": true,
|
"includeAll": true,
|
||||||
"multi": true,
|
"multi": true,
|
||||||
"name": "vertical",
|
"name": "server",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"selected": true,
|
"selected": false,
|
||||||
"text": "All",
|
"text": "All",
|
||||||
"value": "$__all"
|
"value": "$__all"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"selected": false,
|
"selected": true,
|
||||||
"text": "1",
|
"text": "A",
|
||||||
"value": "1"
|
"value": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": true,
|
||||||
|
"text": "B",
|
||||||
|
"value": "B"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"selected": false,
|
"selected": false,
|
||||||
"text": "2",
|
"text": "C",
|
||||||
"value": "2"
|
"value": "C"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"selected": false,
|
"selected": false,
|
||||||
"text": "3",
|
"text": "D",
|
||||||
"value": "3"
|
"value": "D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "E",
|
||||||
|
"value": "E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "F",
|
||||||
|
"value": "F"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "E",
|
||||||
|
"value": "E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "G",
|
||||||
|
"value": "G"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "H",
|
||||||
|
"value": "H"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "I",
|
||||||
|
"value": "I"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "J",
|
||||||
|
"value": "J"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "K",
|
||||||
|
"value": "K"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "L",
|
||||||
|
"value": "L"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"query": "1,2,3",
|
"query": "A,B,C,D,E,F,E,G,H,I,J,K,L",
|
||||||
"queryValue": "",
|
"queryValue": "",
|
||||||
"skipUrlSync": false,
|
"skipUrlSync": false,
|
||||||
"type": "custom"
|
"type": "custom"
|
||||||
@ -248,80 +304,51 @@
|
|||||||
"current": {
|
"current": {
|
||||||
"selected": true,
|
"selected": true,
|
||||||
"text": [
|
"text": [
|
||||||
"All"
|
"Bob",
|
||||||
|
"Rob"
|
||||||
],
|
],
|
||||||
"value": [
|
"value": [
|
||||||
"$__all"
|
"1",
|
||||||
|
"2"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hide": 0,
|
"hide": 0,
|
||||||
"includeAll": true,
|
"includeAll": true,
|
||||||
"multi": true,
|
"multi": true,
|
||||||
"name": "horizontal",
|
"name": "pod",
|
||||||
"options": [
|
"options": [
|
||||||
{
|
{
|
||||||
"selected": true,
|
"selected": false,
|
||||||
"text": "All",
|
"text": "All",
|
||||||
"value": "$__all"
|
"value": "$__all"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"selected": false,
|
"selected": true,
|
||||||
"text": "1",
|
"text": "Bob",
|
||||||
"value": "1"
|
"value": "1"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "3",
|
|
||||||
"value": "3"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"query": "1,2,3",
|
|
||||||
"queryValue": "",
|
|
||||||
"skipUrlSync": false,
|
|
||||||
"type": "custom"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"current": {
|
|
||||||
"selected": true,
|
|
||||||
"text": [
|
|
||||||
"All"
|
|
||||||
],
|
|
||||||
"value": [
|
|
||||||
"$__all"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"hide": 0,
|
|
||||||
"includeAll": true,
|
|
||||||
"multi": true,
|
|
||||||
"name": "row",
|
|
||||||
"options": [
|
|
||||||
{
|
{
|
||||||
"selected": true,
|
"selected": true,
|
||||||
"text": "All",
|
"text": "Rob",
|
||||||
"value": "$__all"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "1",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"selected": false,
|
|
||||||
"text": "2",
|
|
||||||
"value": "2"
|
"value": "2"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"selected": false,
|
"selected": false,
|
||||||
"text": "3",
|
"text": "Sod",
|
||||||
"value": "3"
|
"value": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "Hod",
|
||||||
|
"value": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "Cod",
|
||||||
|
"value": "5"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"query": "1,2,3",
|
"query": "Bob : 1, Rob : 2,Sod : 3, Hod : 4, Cod : 5",
|
||||||
"queryValue": "",
|
"queryValue": "",
|
||||||
"skipUrlSync": false,
|
"skipUrlSync": false,
|
||||||
"type": "custom"
|
"type": "custom"
|
||||||
@ -333,9 +360,9 @@
|
|||||||
"to": "now"
|
"to": "now"
|
||||||
},
|
},
|
||||||
"timepicker": {},
|
"timepicker": {},
|
||||||
"timezone": "utc",
|
"timezone": "",
|
||||||
"title": "Repeating a row with a non-repeating panel and horizontal repeating panel",
|
"title": "Repeating rows",
|
||||||
"uid": "k3PEoCpnk",
|
"uid": "Repeating-rows-uid",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"weekStart": ""
|
"weekStart": ""
|
||||||
}
|
}
|
@ -30,27 +30,6 @@ local dashboard = grafana.dashboard;
|
|||||||
id: 0,
|
id: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dashboard.new('Repeating-a-row-with-a-non-repeating-pan', import '../dev-dashboards/e2e-repeats/Repeating-a-row-with-a-non-repeating-panel-and-horizontal-repeating-panel.json') +
|
|
||||||
resource.addMetadata('folder', 'dev-dashboards') +
|
|
||||||
{
|
|
||||||
spec+: {
|
|
||||||
id: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dashboard.new('Repeating-a-row-with-a-non-repeating-pan', import '../dev-dashboards/e2e-repeats/Repeating-a-row-with-a-non-repeating-panel-and-vertical-repeating-panel.json') +
|
|
||||||
resource.addMetadata('folder', 'dev-dashboards') +
|
|
||||||
{
|
|
||||||
spec+: {
|
|
||||||
id: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dashboard.new('Repeating-a-row-with-a-non-repeating-pan', import '../dev-dashboards/e2e-repeats/Repeating-a-row-with-a-non-repeating-panel.json') +
|
|
||||||
resource.addMetadata('folder', 'dev-dashboards') +
|
|
||||||
{
|
|
||||||
spec+: {
|
|
||||||
id: 0,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dashboard.new('Repeating-a-row-with-a-repeating-horizon', import '../dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-horizontal-panel.json') +
|
dashboard.new('Repeating-a-row-with-a-repeating-horizon', import '../dev-dashboards/e2e-repeats/Repeating-a-row-with-a-repeating-horizontal-panel.json') +
|
||||||
resource.addMetadata('folder', 'dev-dashboards') +
|
resource.addMetadata('folder', 'dev-dashboards') +
|
||||||
{
|
{
|
||||||
@ -625,6 +604,13 @@ local dashboard = grafana.dashboard;
|
|||||||
id: 0,
|
id: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
dashboard.new('templating-repeating-rows', import '../dev-dashboards/feature-templating/templating-repeating-rows.json') +
|
||||||
|
resource.addMetadata('folder', 'dev-dashboards') +
|
||||||
|
{
|
||||||
|
spec+: {
|
||||||
|
id: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
dashboard.new('templating-textbox-e2e-scenarios', import '../dev-dashboards/feature-templating/templating-textbox-e2e-scenarios.json') +
|
dashboard.new('templating-textbox-e2e-scenarios', import '../dev-dashboards/feature-templating/templating-textbox-e2e-scenarios.json') +
|
||||||
resource.addMetadata('folder', 'dev-dashboards') +
|
resource.addMetadata('folder', 'dev-dashboards') +
|
||||||
{
|
{
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
import { e2e } from '../utils';
|
|
||||||
const PAGE_UNDER_TEST = 'k3PEoCpnk/repeating-a-row-with-a-non-repeating-panel-and-horizontal-repeating-panel';
|
|
||||||
const DASHBOARD_NAME = 'Repeating a row with a non-repeating panel and horizontal repeating panel';
|
|
||||||
|
|
||||||
describe('Repeating a row with repeated panels and a non-repeating panel', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
e2e.flows.login('admin', 'admin');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to collapse and expand a repeated row without losing panels', () => {
|
|
||||||
e2e.flows.openDashboard({ uid: PAGE_UNDER_TEST });
|
|
||||||
e2e().contains(DASHBOARD_NAME).should('be.visible');
|
|
||||||
|
|
||||||
const panelsToCheck = [
|
|
||||||
'Row 2 non-repeating panel',
|
|
||||||
'Row 2 repeating panel 1',
|
|
||||||
'Row 2 repeating panel 2',
|
|
||||||
'Row 2 repeating panel 3',
|
|
||||||
];
|
|
||||||
|
|
||||||
// Collapse Row 1 first so the Row 2 panels all fit on the screen
|
|
||||||
e2e.components.DashboardRow.title('Row 1').click();
|
|
||||||
|
|
||||||
// Rows are expanded by default, so check that all panels are visible
|
|
||||||
panelsToCheck.forEach((title) => {
|
|
||||||
e2e.components.Panels.Panel.title(title).should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Collapse the row and check panels are no longer visible
|
|
||||||
e2e.components.DashboardRow.title('Row 2').click();
|
|
||||||
panelsToCheck.forEach((title) => {
|
|
||||||
e2e.components.Panels.Panel.title(title).should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Expand the row and check all panels are visible again
|
|
||||||
e2e.components.DashboardRow.title('Row 2').click();
|
|
||||||
panelsToCheck.forEach((title) => {
|
|
||||||
e2e.components.Panels.Panel.title(title).should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -253,7 +253,7 @@
|
|||||||
"@grafana/lezer-traceql": "0.0.5",
|
"@grafana/lezer-traceql": "0.0.5",
|
||||||
"@grafana/monaco-logql": "^0.0.7",
|
"@grafana/monaco-logql": "^0.0.7",
|
||||||
"@grafana/runtime": "workspace:*",
|
"@grafana/runtime": "workspace:*",
|
||||||
"@grafana/scenes": "^0.29.0",
|
"@grafana/scenes": "^1.1.1",
|
||||||
"@grafana/schema": "workspace:*",
|
"@grafana/schema": "workspace:*",
|
||||||
"@grafana/ui": "workspace:*",
|
"@grafana/ui": "workspace:*",
|
||||||
"@kusto/monaco-kusto": "^7.4.0",
|
"@kusto/monaco-kusto": "^7.4.0",
|
||||||
|
@ -14,11 +14,12 @@ import {
|
|||||||
SceneGridItemLike,
|
SceneGridItemLike,
|
||||||
sceneGraph,
|
sceneGraph,
|
||||||
MultiValueVariable,
|
MultiValueVariable,
|
||||||
VariableValueSingle,
|
|
||||||
LocalValueVariable,
|
LocalValueVariable,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
|
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
|
||||||
|
|
||||||
|
import { getMultiVariableValues } from '../utils/utils';
|
||||||
|
|
||||||
interface PanelRepeaterGridItemState extends SceneGridItemStateLike {
|
interface PanelRepeaterGridItemState extends SceneGridItemStateLike {
|
||||||
source: VizPanel;
|
source: VizPanel;
|
||||||
repeatedPanels?: VizPanel[];
|
repeatedPanels?: VizPanel[];
|
||||||
@ -106,7 +107,7 @@ export class PanelRepeaterGridItem extends SceneObjectBase<PanelRepeaterGridItem
|
|||||||
}
|
}
|
||||||
|
|
||||||
const panelToRepeat = this.state.source;
|
const panelToRepeat = this.state.source;
|
||||||
const { values, texts } = this.getVariableValues(variable);
|
const { values, texts } = getMultiVariableValues(variable);
|
||||||
const repeatedPanels: VizPanel[] = [];
|
const repeatedPanels: VizPanel[] = [];
|
||||||
|
|
||||||
// Loop through variable values and create repeates
|
// Loop through variable values and create repeates
|
||||||
@ -143,25 +144,6 @@ export class PanelRepeaterGridItem extends SceneObjectBase<PanelRepeaterGridItem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getVariableValues(variable: MultiValueVariable): {
|
|
||||||
values: VariableValueSingle[];
|
|
||||||
texts: VariableValueSingle[];
|
|
||||||
} {
|
|
||||||
const { value, text, options } = variable.state;
|
|
||||||
|
|
||||||
if (variable.hasAllValue()) {
|
|
||||||
return {
|
|
||||||
values: options.map((o) => o.value),
|
|
||||||
texts: options.map((o) => o.label),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
values: Array.isArray(value) ? value : [value],
|
|
||||||
texts: Array.isArray(text) ? text : [text],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMaxPerRow(): number {
|
private getMaxPerRow(): number {
|
||||||
return this.state.maxPerRow ?? 4;
|
return this.state.maxPerRow ?? 4;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,144 @@
|
|||||||
|
import {
|
||||||
|
EmbeddedScene,
|
||||||
|
SceneCanvasText,
|
||||||
|
SceneGridItem,
|
||||||
|
SceneGridLayout,
|
||||||
|
SceneGridRow,
|
||||||
|
SceneTimeRange,
|
||||||
|
SceneVariableSet,
|
||||||
|
TestVariable,
|
||||||
|
} from '@grafana/scenes';
|
||||||
|
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants';
|
||||||
|
|
||||||
|
import { activateFullSceneTree } from '../utils/test-utils';
|
||||||
|
|
||||||
|
import { RepeatDirection } from './PanelRepeaterGridItem';
|
||||||
|
import { RowRepeaterBehavior } from './RowRepeaterBehavior';
|
||||||
|
|
||||||
|
describe('RowRepeaterBehavior', () => {
|
||||||
|
describe('Given scene with variable with 5 values', () => {
|
||||||
|
let scene: EmbeddedScene, grid: SceneGridLayout;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
({ scene, grid } = buildScene({ variableQueryTime: 0 }));
|
||||||
|
activateFullSceneTree(scene);
|
||||||
|
await new Promise((r) => setTimeout(r, 1));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should repeat row', () => {
|
||||||
|
// Verify that panel above row remains
|
||||||
|
expect(grid.state.children[0]).toBeInstanceOf(SceneGridItem);
|
||||||
|
// Verify that first row still has repeat behavior
|
||||||
|
const row1 = grid.state.children[1] as SceneGridRow;
|
||||||
|
expect(row1.state.$behaviors?.[0]).toBeInstanceOf(RowRepeaterBehavior);
|
||||||
|
expect(row1.state.$variables!.state.variables[0].getValue()).toBe('1');
|
||||||
|
|
||||||
|
const row2 = grid.state.children[2] as SceneGridRow;
|
||||||
|
expect(row2.state.$variables!.state.variables[0].getValueText?.()).toBe('B');
|
||||||
|
|
||||||
|
// Should give repeated panels unique keys
|
||||||
|
const gridItem = row2.state.children[0] as SceneGridItem;
|
||||||
|
expect(gridItem.state.body?.state.key).toBe('canvas-1-row-1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should push row at the bottom down', () => {
|
||||||
|
// Should push row at the bottom down
|
||||||
|
const rowAtTheBottom = grid.state.children[6] as SceneGridRow;
|
||||||
|
expect(rowAtTheBottom.state.title).toBe('Row at the bottom');
|
||||||
|
|
||||||
|
// Panel at the top is 10, each row is (1+5)*5 = 30, so the grid item below it should be 40
|
||||||
|
expect(rowAtTheBottom.state.y).toBe(40);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should handle second repeat cycle and update remove old repeats', async () => {
|
||||||
|
// trigger another repeat cycle by changing the variable
|
||||||
|
const variable = scene.state.$variables!.state.variables[0] as TestVariable;
|
||||||
|
variable.changeValueTo(['2', '3']);
|
||||||
|
|
||||||
|
await new Promise((r) => setTimeout(r, 1));
|
||||||
|
|
||||||
|
// should now only have 2 repeated rows (and the panel above + the row at the bottom)
|
||||||
|
expect(grid.state.children.length).toBe(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
interface SceneOptions {
|
||||||
|
variableQueryTime: number;
|
||||||
|
maxPerRow?: number;
|
||||||
|
itemHeight?: number;
|
||||||
|
repeatDirection?: RepeatDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildScene(options: SceneOptions) {
|
||||||
|
const grid = new SceneGridLayout({
|
||||||
|
children: [
|
||||||
|
new SceneGridItem({
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 24,
|
||||||
|
height: 10,
|
||||||
|
body: new SceneCanvasText({
|
||||||
|
text: 'Panel above row',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
new SceneGridRow({
|
||||||
|
x: 0,
|
||||||
|
y: 10,
|
||||||
|
width: 24,
|
||||||
|
height: 1,
|
||||||
|
$behaviors: [
|
||||||
|
new RowRepeaterBehavior({
|
||||||
|
variableName: 'server',
|
||||||
|
sources: [
|
||||||
|
new SceneGridItem({
|
||||||
|
x: 0,
|
||||||
|
y: 11,
|
||||||
|
width: 24,
|
||||||
|
height: 5,
|
||||||
|
body: new SceneCanvasText({
|
||||||
|
key: 'canvas-1',
|
||||||
|
text: 'Panel inside repeated row, server = $server',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new SceneGridRow({
|
||||||
|
x: 0,
|
||||||
|
y: 16,
|
||||||
|
width: 24,
|
||||||
|
height: 5,
|
||||||
|
title: 'Row at the bottom',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const scene = new EmbeddedScene({
|
||||||
|
$timeRange: new SceneTimeRange({ from: 'now-6h', to: 'now' }),
|
||||||
|
$variables: new SceneVariableSet({
|
||||||
|
variables: [
|
||||||
|
new TestVariable({
|
||||||
|
name: 'server',
|
||||||
|
query: 'A.*',
|
||||||
|
value: ALL_VARIABLE_VALUE,
|
||||||
|
text: ALL_VARIABLE_TEXT,
|
||||||
|
isMulti: true,
|
||||||
|
includeAll: true,
|
||||||
|
delayMs: options.variableQueryTime,
|
||||||
|
optionsToReturn: [
|
||||||
|
{ label: 'A', value: '1' },
|
||||||
|
{ label: 'B', value: '2' },
|
||||||
|
{ label: 'C', value: '3' },
|
||||||
|
{ label: 'D', value: '4' },
|
||||||
|
{ label: 'E', value: '5' },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
body: grid,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { scene, grid };
|
||||||
|
}
|
215
public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts
Normal file
215
public/app/features/dashboard-scene/scene/RowRepeaterBehavior.ts
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import {
|
||||||
|
LocalValueVariable,
|
||||||
|
MultiValueVariable,
|
||||||
|
sceneGraph,
|
||||||
|
SceneGridItemLike,
|
||||||
|
SceneGridLayout,
|
||||||
|
SceneGridRow,
|
||||||
|
SceneObjectBase,
|
||||||
|
SceneObjectState,
|
||||||
|
SceneVariable,
|
||||||
|
SceneVariableSet,
|
||||||
|
VariableDependencyConfig,
|
||||||
|
VariableValueSingle,
|
||||||
|
} from '@grafana/scenes';
|
||||||
|
|
||||||
|
import { getMultiVariableValues } from '../utils/utils';
|
||||||
|
|
||||||
|
interface RowRepeaterBehaviorState extends SceneObjectState {
|
||||||
|
variableName: string;
|
||||||
|
sources: SceneGridItemLike[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This behavior will run an effect function when specified variables change
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class RowRepeaterBehavior extends SceneObjectBase<RowRepeaterBehaviorState> {
|
||||||
|
protected _variableDependency = new VariableDependencyConfig(this, {
|
||||||
|
variableNames: [this.state.variableName],
|
||||||
|
onVariableUpdatesCompleted: this._onVariableChanged.bind(this),
|
||||||
|
});
|
||||||
|
|
||||||
|
private _isWaitingForVariables = false;
|
||||||
|
|
||||||
|
public constructor(state: RowRepeaterBehaviorState) {
|
||||||
|
super(state);
|
||||||
|
|
||||||
|
this.addActivationHandler(() => this._activationHandler());
|
||||||
|
}
|
||||||
|
|
||||||
|
private _activationHandler() {
|
||||||
|
// If we our variable is ready we can process repeats on activation
|
||||||
|
if (sceneGraph.hasVariableDependencyInLoadingState(this)) {
|
||||||
|
this._isWaitingForVariables = true;
|
||||||
|
} else {
|
||||||
|
this._performRepeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onVariableChanged(changedVariables: Set<SceneVariable>, dependencyChanged: boolean): void {
|
||||||
|
if (dependencyChanged) {
|
||||||
|
this._performRepeat();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are waiting for variables and the variable is no longer loading then we are ready to repeat as well
|
||||||
|
if (this._isWaitingForVariables && !sceneGraph.hasVariableDependencyInLoadingState(this)) {
|
||||||
|
this._isWaitingForVariables = false;
|
||||||
|
this._performRepeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _performRepeat() {
|
||||||
|
const variable = sceneGraph.lookupVariable(this.state.variableName, this.parent?.parent!);
|
||||||
|
|
||||||
|
if (!variable) {
|
||||||
|
console.error('RepeatedRowBehavior: Variable not found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(variable instanceof MultiValueVariable)) {
|
||||||
|
console.error('RepeatedRowBehavior: Variable is not a MultiValueVariable');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(this.parent instanceof SceneGridRow)) {
|
||||||
|
console.error('RepeatedRowBehavior: Parent is not a SceneGridRow');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const layout = sceneGraph.getLayout(this);
|
||||||
|
|
||||||
|
if (!(layout instanceof SceneGridLayout)) {
|
||||||
|
console.error('RepeatedRowBehavior: Layout is not a SceneGridLayout');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowToRepeat = this.parent as SceneGridRow;
|
||||||
|
const { values, texts } = getMultiVariableValues(variable);
|
||||||
|
const rows: SceneGridRow[] = [];
|
||||||
|
const rowContentHeight = getRowContentHeight(this.state.sources);
|
||||||
|
let maxYOfRows = 0;
|
||||||
|
|
||||||
|
// Loop through variable values and create repeates
|
||||||
|
for (let index = 0; index < values.length; index++) {
|
||||||
|
const children: SceneGridItemLike[] = [];
|
||||||
|
|
||||||
|
// Loop through panels inside row
|
||||||
|
for (const source of this.state.sources) {
|
||||||
|
const sourceItemY = source.state.y ?? 0;
|
||||||
|
const itemY = sourceItemY + (rowContentHeight + 1) * index;
|
||||||
|
|
||||||
|
const itemClone = source.clone({
|
||||||
|
key: `${source.state.key}-clone-${index}`,
|
||||||
|
y: itemY,
|
||||||
|
});
|
||||||
|
|
||||||
|
//Make sure all the child scene objects have unique keys
|
||||||
|
ensureUniqueKeys(itemClone, index);
|
||||||
|
|
||||||
|
children.push(itemClone);
|
||||||
|
|
||||||
|
if (maxYOfRows < itemY + itemClone.state.height!) {
|
||||||
|
maxYOfRows = itemY + itemClone.state.height!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rowClone = this.getRowClone(rowToRepeat, index, values[index], texts[index], rowContentHeight, children);
|
||||||
|
rows.push(rowClone);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLayout(layout, rows, maxYOfRows, rowToRepeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowClone(
|
||||||
|
rowToRepeat: SceneGridRow,
|
||||||
|
index: number,
|
||||||
|
value: VariableValueSingle,
|
||||||
|
text: VariableValueSingle,
|
||||||
|
rowContentHeight: number,
|
||||||
|
children: SceneGridItemLike[]
|
||||||
|
): SceneGridRow {
|
||||||
|
if (index === 0) {
|
||||||
|
rowToRepeat.setState({
|
||||||
|
// not activated
|
||||||
|
$variables: new SceneVariableSet({
|
||||||
|
variables: [new LocalValueVariable({ name: this.state.variableName, value, text: String(text) })],
|
||||||
|
}),
|
||||||
|
children,
|
||||||
|
});
|
||||||
|
return rowToRepeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceRowY = rowToRepeat.state.y ?? 0;
|
||||||
|
|
||||||
|
return rowToRepeat.clone({
|
||||||
|
key: `${rowToRepeat.state.key}-clone-${index}`,
|
||||||
|
$variables: new SceneVariableSet({
|
||||||
|
variables: [new LocalValueVariable({ name: this.state.variableName, value, text: String(text) })],
|
||||||
|
}),
|
||||||
|
$behaviors: [],
|
||||||
|
children,
|
||||||
|
y: sourceRowY + rowContentHeight * index + index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRowContentHeight(panels: SceneGridItemLike[]): number {
|
||||||
|
let maxY = 0;
|
||||||
|
let minY = Number.MAX_VALUE;
|
||||||
|
|
||||||
|
for (const panel of panels) {
|
||||||
|
if (panel.state.y! + panel.state.height! > maxY) {
|
||||||
|
maxY = panel.state.y! + panel.state.height!;
|
||||||
|
}
|
||||||
|
if (panel.state.y! < minY) {
|
||||||
|
minY = panel.state.y!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxY - minY;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLayout(layout: SceneGridLayout, rows: SceneGridRow[], maxYOfRows: number, rowToRepeat: SceneGridRow) {
|
||||||
|
const allChildren = getLayoutChildrenFilterOutRepeatClones(layout, rowToRepeat);
|
||||||
|
const index = allChildren.indexOf(rowToRepeat);
|
||||||
|
|
||||||
|
if (index === -1) {
|
||||||
|
throw new Error('RowRepeaterBehavior: Parent row not found in layout children');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newChildren = [...allChildren.slice(0, index), ...rows, ...allChildren.slice(index + 1)];
|
||||||
|
|
||||||
|
// Is there grid items after rows?
|
||||||
|
if (allChildren.length > index + 1) {
|
||||||
|
const childrenAfter = allChildren.slice(index + 1);
|
||||||
|
const firstChildAfterY = childrenAfter[0].state.y!;
|
||||||
|
const diff = maxYOfRows - firstChildAfterY;
|
||||||
|
|
||||||
|
for (const child of childrenAfter) {
|
||||||
|
if (child.state.y! < maxYOfRows) {
|
||||||
|
child.setState({ y: child.state.y! + diff });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layout.setState({ children: newChildren });
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLayoutChildrenFilterOutRepeatClones(layout: SceneGridLayout, rowToRepeat: SceneGridRow) {
|
||||||
|
return layout.state.children.filter((child) => {
|
||||||
|
if (child.state.key?.startsWith(`${rowToRepeat.state.key}-clone-`)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureUniqueKeys(item: SceneGridItemLike, rowIndex: number) {
|
||||||
|
item.forEachChild((child) => {
|
||||||
|
child.setState({ key: `${child.state.key}-row-${rowIndex}` });
|
||||||
|
ensureUniqueKeys(child, rowIndex);
|
||||||
|
});
|
||||||
|
}
|
@ -1,6 +1,124 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`transformSceneToSaveModel Given a scene Should transform back to peristed model 1`] = `
|
exports[`transformSceneToSaveModel Given a scene with rows Should transform back to peristed model 1`] = `
|
||||||
|
{
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0,
|
||||||
|
},
|
||||||
|
"id": 20,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Row at the top - not repeated - saved expanded",
|
||||||
|
"type": "row",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": [],
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 2,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 1,
|
||||||
|
},
|
||||||
|
"id": 15,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false,
|
||||||
|
},
|
||||||
|
"content": "<div class="center-vh">
|
||||||
|
Repeated row below. The row has
|
||||||
|
a panel that is also repeated horizontally based
|
||||||
|
on values in the $pod variable.
|
||||||
|
</div>",
|
||||||
|
"mode": "markdown",
|
||||||
|
},
|
||||||
|
"title": "",
|
||||||
|
"transformations": [],
|
||||||
|
"transparent": false,
|
||||||
|
"type": "text",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 3,
|
||||||
|
},
|
||||||
|
"id": 16,
|
||||||
|
"panels": [],
|
||||||
|
"repeat": "server",
|
||||||
|
"title": "Row for server $server",
|
||||||
|
"type": "row",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": true,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 25,
|
||||||
|
},
|
||||||
|
"id": 25,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": [],
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 2,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 26,
|
||||||
|
},
|
||||||
|
"id": 30,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false,
|
||||||
|
},
|
||||||
|
"content": "<div class="center-vh">
|
||||||
|
Just a panel
|
||||||
|
</div>",
|
||||||
|
"mode": "markdown",
|
||||||
|
},
|
||||||
|
"transformations": [],
|
||||||
|
"transparent": false,
|
||||||
|
"type": "text",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"title": "Row at the bottom - not repeated - saved collapsed ",
|
||||||
|
"type": "row",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"schemaVersion": 36,
|
||||||
|
"tags": [],
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now",
|
||||||
|
},
|
||||||
|
"timezone": "browser",
|
||||||
|
"title": "Repeating rows",
|
||||||
|
"uid": "Repeating-rows-uid",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`transformSceneToSaveModel Given a simple scene Should transform back to peristed model 1`] = `
|
||||||
{
|
{
|
||||||
"editable": true,
|
"editable": true,
|
||||||
"fiscalYearStartMonth": 0,
|
"fiscalYearStartMonth": 0,
|
||||||
@ -45,6 +163,63 @@ exports[`transformSceneToSaveModel Given a scene Should transform back to perist
|
|||||||
"transparent": false,
|
"transparent": false,
|
||||||
"type": "timeseries",
|
"type": "timeseries",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8,
|
||||||
|
},
|
||||||
|
"id": 5,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Row title",
|
||||||
|
"type": "row",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": [],
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 9,
|
||||||
|
},
|
||||||
|
"id": 29,
|
||||||
|
"options": {},
|
||||||
|
"title": "panel inside row",
|
||||||
|
"transformations": [],
|
||||||
|
"transparent": false,
|
||||||
|
"type": "timeseries",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {},
|
||||||
|
"overrides": [],
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 11,
|
||||||
|
"x": 12,
|
||||||
|
"y": 9,
|
||||||
|
},
|
||||||
|
"id": 25,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false,
|
||||||
|
},
|
||||||
|
"content": "content",
|
||||||
|
"mode": "markdown",
|
||||||
|
},
|
||||||
|
"title": "Transparent text panel",
|
||||||
|
"transformations": [],
|
||||||
|
"transparent": true,
|
||||||
|
"type": "text",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
"schemaVersion": 36,
|
"schemaVersion": 36,
|
||||||
"tags": [],
|
"tags": [],
|
||||||
|
@ -0,0 +1,353 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": {
|
||||||
|
"type": "grafana",
|
||||||
|
"uid": "-- Grafana --"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"links": [],
|
||||||
|
"liveNow": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"collapsed": false,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 20,
|
||||||
|
"panels": [],
|
||||||
|
"title": "Row at the top - not repeated - saved expanded",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gridPos": {
|
||||||
|
"h": 2,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 1
|
||||||
|
},
|
||||||
|
"id": 15,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false
|
||||||
|
},
|
||||||
|
"content": "<div class=\"center-vh\">\n Repeated row below. The row has \n a panel that is also repeated horizontally based\n on values in the $pod variable. \n</div>",
|
||||||
|
"mode": "markdown"
|
||||||
|
},
|
||||||
|
"pluginVersion": "10.2.0-pre",
|
||||||
|
"type": "text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 3
|
||||||
|
},
|
||||||
|
"id": 16,
|
||||||
|
"panels": [],
|
||||||
|
"repeat": "server",
|
||||||
|
"repeatDirection": "h",
|
||||||
|
"title": "Row for server $server",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"axisShow": false,
|
||||||
|
"barAlignment": 0,
|
||||||
|
"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",
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 10,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 4
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"maxPerRow": 3,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"repeat": "pod",
|
||||||
|
"repeatDirection": "h",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "server = $server, pod id = $pod ",
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"seriesCount": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "server = $server, pod = $pod",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsed": true,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 1,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 25
|
||||||
|
},
|
||||||
|
"id": 25,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"gridPos": {
|
||||||
|
"h": 2,
|
||||||
|
"w": 24,
|
||||||
|
"x": 0,
|
||||||
|
"y": 26
|
||||||
|
},
|
||||||
|
"id": 30,
|
||||||
|
"options": {
|
||||||
|
"code": {
|
||||||
|
"language": "plaintext",
|
||||||
|
"showLineNumbers": false,
|
||||||
|
"showMiniMap": false
|
||||||
|
},
|
||||||
|
"content": "<div class=\"center-vh\">\n Just a panel\n</div>",
|
||||||
|
"mode": "markdown"
|
||||||
|
},
|
||||||
|
"pluginVersion": "10.2.0-pre",
|
||||||
|
"type": "text"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Row at the bottom - not repeated - saved collapsed ",
|
||||||
|
"type": "row"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": "",
|
||||||
|
"schemaVersion": 38,
|
||||||
|
"tags": ["templating", "gdev"],
|
||||||
|
"templating": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"current": {
|
||||||
|
"selected": true,
|
||||||
|
"text": ["A", "B"],
|
||||||
|
"value": ["A", "B"]
|
||||||
|
},
|
||||||
|
"hide": 0,
|
||||||
|
"includeAll": true,
|
||||||
|
"multi": true,
|
||||||
|
"name": "server",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "All",
|
||||||
|
"value": "$__all"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": true,
|
||||||
|
"text": "A",
|
||||||
|
"value": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": true,
|
||||||
|
"text": "B",
|
||||||
|
"value": "B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "C",
|
||||||
|
"value": "C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "D",
|
||||||
|
"value": "D"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "E",
|
||||||
|
"value": "E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "F",
|
||||||
|
"value": "F"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "E",
|
||||||
|
"value": "E"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "G",
|
||||||
|
"value": "G"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "H",
|
||||||
|
"value": "H"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "I",
|
||||||
|
"value": "I"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "J",
|
||||||
|
"value": "J"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "K",
|
||||||
|
"value": "K"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "L",
|
||||||
|
"value": "L"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "A,B,C,D,E,F,E,G,H,I,J,K,L",
|
||||||
|
"queryValue": "",
|
||||||
|
"skipUrlSync": false,
|
||||||
|
"type": "custom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"current": {
|
||||||
|
"selected": true,
|
||||||
|
"text": ["Bob", "Rob"],
|
||||||
|
"value": ["1", "2"]
|
||||||
|
},
|
||||||
|
"hide": 0,
|
||||||
|
"includeAll": true,
|
||||||
|
"multi": true,
|
||||||
|
"name": "pod",
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "All",
|
||||||
|
"value": "$__all"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": true,
|
||||||
|
"text": "Bob",
|
||||||
|
"value": "1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": true,
|
||||||
|
"text": "Rob",
|
||||||
|
"value": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "Sod",
|
||||||
|
"value": "3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "Hod",
|
||||||
|
"value": "4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"selected": false,
|
||||||
|
"text": "Cod",
|
||||||
|
"value": "5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"query": "Bob : 1, Rob : 2,Sod : 3, Hod : 4, Cod : 5",
|
||||||
|
"queryValue": "",
|
||||||
|
"skipUrlSync": false,
|
||||||
|
"type": "custom"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Repeating rows",
|
||||||
|
"uid": "Repeating-rows-uid",
|
||||||
|
"version": 1,
|
||||||
|
"weekStart": ""
|
||||||
|
}
|
@ -20,15 +20,18 @@ import { DASHBOARD_DATASOURCE_PLUGIN_ID } from 'app/plugins/datasource/dashboard
|
|||||||
|
|
||||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||||
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||||
|
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||||
import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider';
|
import { ShareQueryDataProvider } from '../scene/ShareQueryDataProvider';
|
||||||
|
|
||||||
|
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
|
||||||
import {
|
import {
|
||||||
createDashboardSceneFromDashboardModel,
|
createDashboardSceneFromDashboardModel,
|
||||||
buildGridItemForPanel,
|
buildGridItemForPanel,
|
||||||
createSceneVariableFromVariableModel,
|
createSceneVariableFromVariableModel,
|
||||||
|
transformSaveModelToScene,
|
||||||
} from './transformSaveModelToScene';
|
} from './transformSaveModelToScene';
|
||||||
|
|
||||||
describe('DashboardLoader', () => {
|
describe('transformSaveModelToScene', () => {
|
||||||
describe('when creating dashboard scene', () => {
|
describe('when creating dashboard scene', () => {
|
||||||
it('should initialize the DashboardScene with the model state', () => {
|
it('should initialize the DashboardScene with the model state', () => {
|
||||||
const dash = {
|
const dash = {
|
||||||
@ -130,6 +133,7 @@ describe('DashboardLoader', () => {
|
|||||||
const rowWithPanel = createPanelJSONFixture({
|
const rowWithPanel = createPanelJSONFixture({
|
||||||
title: 'Row with panel',
|
title: 'Row with panel',
|
||||||
type: 'row',
|
type: 'row',
|
||||||
|
id: 10,
|
||||||
collapsed: false,
|
collapsed: false,
|
||||||
gridPos: {
|
gridPos: {
|
||||||
h: 1,
|
h: 1,
|
||||||
@ -182,6 +186,7 @@ describe('DashboardLoader', () => {
|
|||||||
expect(body.state.children[1]).toBeInstanceOf(SceneGridRow);
|
expect(body.state.children[1]).toBeInstanceOf(SceneGridRow);
|
||||||
const rowWithPanelsScene = body.state.children[1] as SceneGridRow;
|
const rowWithPanelsScene = body.state.children[1] as SceneGridRow;
|
||||||
expect(rowWithPanelsScene.state.title).toBe(rowWithPanel.title);
|
expect(rowWithPanelsScene.state.title).toBe(rowWithPanel.title);
|
||||||
|
expect(rowWithPanelsScene.state.key).toBe('panel-10');
|
||||||
expect(rowWithPanelsScene.state.children).toHaveLength(1);
|
expect(rowWithPanelsScene.state.children).toHaveLength(1);
|
||||||
// Panel within row
|
// Panel within row
|
||||||
expect(rowWithPanelsScene.state.children[0]).toBeInstanceOf(SceneGridItem);
|
expect(rowWithPanelsScene.state.children[0]).toBeInstanceOf(SceneGridItem);
|
||||||
@ -410,6 +415,7 @@ describe('DashboardLoader', () => {
|
|||||||
hide: 0,
|
hide: 0,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should migrate query variable', () => {
|
it('should migrate query variable', () => {
|
||||||
const variable = {
|
const variable = {
|
||||||
allValue: null,
|
allValue: null,
|
||||||
@ -615,6 +621,22 @@ describe('DashboardLoader', () => {
|
|||||||
expect(() => createSceneVariableFromVariableModel(variable)).toThrow();
|
expect(() => createSceneVariableFromVariableModel(variable)).toThrow();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Repeating rows', () => {
|
||||||
|
it('Should build correct scene model', () => {
|
||||||
|
const scene = transformSaveModelToScene({ dashboard: repeatingRowsAndPanelsDashboardJson as any, meta: {} });
|
||||||
|
const body = scene.state.body as SceneGridLayout;
|
||||||
|
const row2 = body.state.children[1] as SceneGridRow;
|
||||||
|
|
||||||
|
expect(row2.state.$behaviors?.[0]).toBeInstanceOf(RowRepeaterBehavior);
|
||||||
|
|
||||||
|
const repeatBehavior = row2.state.$behaviors?.[0] as RowRepeaterBehavior;
|
||||||
|
expect(repeatBehavior.state.variableName).toBe('server');
|
||||||
|
|
||||||
|
const lastRow = body.state.children[body.state.children.length - 1] as SceneGridRow;
|
||||||
|
expect(lastRow.state.isCollapsed).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function buildGridItemForTest(saveModel: Partial<Panel>): { gridItem: SceneGridItem; vizPanel: VizPanel } {
|
function buildGridItemForTest(saveModel: Partial<Panel>): { gridItem: SceneGridItem; vizPanel: VizPanel } {
|
||||||
|
@ -35,6 +35,7 @@ import { LibraryVizPanel } from '../scene/LibraryVizPanel';
|
|||||||
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
|
||||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||||
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||||
|
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||||
import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
import { createPanelDataProvider } from '../utils/createPanelDataProvider';
|
||||||
import { getVizPanelKeyForPanelId } from '../utils/utils';
|
import { getVizPanelKeyForPanelId } from '../utils/utils';
|
||||||
|
|
||||||
@ -67,14 +68,7 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI
|
|||||||
if (!currentRow) {
|
if (!currentRow) {
|
||||||
if (Boolean(panel.collapsed)) {
|
if (Boolean(panel.collapsed)) {
|
||||||
// collapsed rows contain their panels within the row model
|
// collapsed rows contain their panels within the row model
|
||||||
panels.push(
|
panels.push(createRowFromPanelModel(panel, []));
|
||||||
new SceneGridRow({
|
|
||||||
title: panel.title,
|
|
||||||
isCollapsed: true,
|
|
||||||
y: panel.gridPos.y,
|
|
||||||
children: panel.panels ? panel.panels.map(buildGridItemForPanel) : [],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
// indicate new row to be processed
|
// indicate new row to be processed
|
||||||
currentRow = panel;
|
currentRow = panel;
|
||||||
@ -83,13 +77,7 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI
|
|||||||
// when a row has been processed, and we hit a next one for processing
|
// when a row has been processed, and we hit a next one for processing
|
||||||
if (currentRow.id !== panel.id) {
|
if (currentRow.id !== panel.id) {
|
||||||
// commit previous row panels
|
// commit previous row panels
|
||||||
panels.push(
|
panels.push(createRowFromPanelModel(currentRow, currentRowPanels));
|
||||||
new SceneGridRow({
|
|
||||||
title: currentRow!.title,
|
|
||||||
y: currentRow.gridPos.y,
|
|
||||||
children: currentRowPanels,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
currentRow = panel;
|
currentRow = panel;
|
||||||
currentRowPanels = [];
|
currentRowPanels = [];
|
||||||
@ -121,18 +109,43 @@ export function createSceneObjectsForPanels(oldPanels: PanelModel[]): SceneGridI
|
|||||||
|
|
||||||
// commit a row if it's the last one
|
// commit a row if it's the last one
|
||||||
if (currentRow) {
|
if (currentRow) {
|
||||||
panels.push(
|
panels.push(createRowFromPanelModel(currentRow, currentRowPanels));
|
||||||
new SceneGridRow({
|
|
||||||
title: currentRow!.title,
|
|
||||||
y: currentRow.gridPos.y,
|
|
||||||
children: currentRowPanels,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return panels;
|
return panels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRowFromPanelModel(row: PanelModel, content: SceneGridItemLike[]): SceneGridItemLike {
|
||||||
|
if (Boolean(row.collapsed)) {
|
||||||
|
if (row.panels) {
|
||||||
|
content = row.panels.map(buildGridItemForPanel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let behaviors: SceneObject[] | undefined;
|
||||||
|
let children = content;
|
||||||
|
|
||||||
|
if (row.repeat) {
|
||||||
|
// For repeated rows the children are stored in the behavior
|
||||||
|
children = [];
|
||||||
|
behaviors = [
|
||||||
|
new RowRepeaterBehavior({
|
||||||
|
variableName: row.repeat,
|
||||||
|
sources: content,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SceneGridRow({
|
||||||
|
key: getVizPanelKeyForPanelId(row.id),
|
||||||
|
title: row.title,
|
||||||
|
y: row.gridPos.y,
|
||||||
|
isCollapsed: row.collapsed,
|
||||||
|
children: children,
|
||||||
|
$behaviors: behaviors,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) {
|
export function createDashboardSceneFromDashboardModel(oldModel: DashboardModel) {
|
||||||
let variables: SceneVariableSet | undefined = undefined;
|
let variables: SceneVariableSet | undefined = undefined;
|
||||||
|
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
import { SceneGridItemLike } from '@grafana/scenes';
|
import { MultiValueVariable, SceneGridItemLike, SceneGridLayout, SceneGridRow, SceneVariable } from '@grafana/scenes';
|
||||||
import { Panel } from '@grafana/schema';
|
import { Panel, RowPanel } from '@grafana/schema';
|
||||||
import { PanelModel } from 'app/features/dashboard/state';
|
import { PanelModel } from 'app/features/dashboard/state';
|
||||||
|
|
||||||
|
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||||
|
|
||||||
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
|
import dashboard_to_load1 from './testfiles/dashboard_to_load1.json';
|
||||||
|
import repeatingRowsAndPanelsDashboardJson from './testfiles/repeating_rows_and_panels.json';
|
||||||
import { buildGridItemForPanel, transformSaveModelToScene } from './transformSaveModelToScene';
|
import { buildGridItemForPanel, transformSaveModelToScene } from './transformSaveModelToScene';
|
||||||
import { gridItemToPanel, transformSceneToSaveModel } from './transformSceneToSaveModel';
|
import { gridItemToPanel, transformSceneToSaveModel } from './transformSceneToSaveModel';
|
||||||
|
|
||||||
describe('transformSceneToSaveModel', () => {
|
describe('transformSceneToSaveModel', () => {
|
||||||
describe('Given a scene', () => {
|
describe('Given a simple scene', () => {
|
||||||
it('Should transform back to peristed model', () => {
|
it('Should transform back to peristed model', () => {
|
||||||
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
|
const scene = transformSaveModelToScene({ dashboard: dashboard_to_load1 as any, meta: {} });
|
||||||
const saveModel = transformSceneToSaveModel(scene);
|
const saveModel = transformSceneToSaveModel(scene);
|
||||||
@ -16,6 +19,40 @@ describe('transformSceneToSaveModel', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Given a scene with rows', () => {
|
||||||
|
it('Should transform back to peristed model', () => {
|
||||||
|
const scene = transformSaveModelToScene({ dashboard: repeatingRowsAndPanelsDashboardJson as any, meta: {} });
|
||||||
|
const saveModel = transformSceneToSaveModel(scene);
|
||||||
|
const row2: RowPanel = saveModel.panels![2] as RowPanel;
|
||||||
|
|
||||||
|
expect(row2.type).toBe('row');
|
||||||
|
expect(row2.repeat).toBe('server');
|
||||||
|
expect(saveModel).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should remove repeated rows in save model', () => {
|
||||||
|
const scene = transformSaveModelToScene({ dashboard: repeatingRowsAndPanelsDashboardJson as any, meta: {} });
|
||||||
|
|
||||||
|
const variable = scene.state.$variables?.state.variables[0] as MultiValueVariable;
|
||||||
|
variable.changeValueTo(['a', 'b', 'c']);
|
||||||
|
|
||||||
|
const grid = scene.state.body as SceneGridLayout;
|
||||||
|
const rowWithRepeat = grid.state.children[1] as SceneGridRow;
|
||||||
|
const rowRepeater = rowWithRepeat.state.$behaviors![0] as RowRepeaterBehavior;
|
||||||
|
|
||||||
|
// trigger row repeater
|
||||||
|
rowRepeater.variableDependency?.variableUpdatesCompleted(new Set<SceneVariable>([variable]));
|
||||||
|
|
||||||
|
// Make sure the repeated rows have been added to runtime scene model
|
||||||
|
expect(grid.state.children.length).toBe(5);
|
||||||
|
|
||||||
|
const saveModel = transformSceneToSaveModel(scene);
|
||||||
|
const rows = saveModel.panels!.filter((p) => p.type === 'row');
|
||||||
|
// Verify the save model does not contain any repeated rows
|
||||||
|
expect(rows.length).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Panel options', () => {
|
describe('Panel options', () => {
|
||||||
it('Given panel with time override', () => {
|
it('Given panel with time override', () => {
|
||||||
const gridItem = buildGridItemFromPanelSchema({
|
const gridItem = buildGridItemFromPanelSchema({
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { SceneGridItem, SceneGridItemLike, SceneGridLayout, VizPanel } from '@grafana/scenes';
|
import { SceneGridItem, SceneGridItemLike, SceneGridLayout, SceneGridRow, VizPanel } from '@grafana/scenes';
|
||||||
import { Dashboard, defaultDashboard, FieldConfigSource, Panel } from '@grafana/schema';
|
import { Dashboard, defaultDashboard, FieldConfigSource, Panel, RowPanel } from '@grafana/schema';
|
||||||
import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object';
|
import { sortedDeepCloneWithoutNulls } from 'app/core/utils/object';
|
||||||
|
|
||||||
import { DashboardScene } from '../scene/DashboardScene';
|
import { DashboardScene } from '../scene/DashboardScene';
|
||||||
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
|
||||||
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
import { PanelTimeRange } from '../scene/PanelTimeRange';
|
||||||
|
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
|
||||||
import { getPanelIdForVizPanel } from '../utils/utils';
|
import { getPanelIdForVizPanel } from '../utils/utils';
|
||||||
|
|
||||||
export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
|
export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
|
||||||
@ -18,6 +19,14 @@ export function transformSceneToSaveModel(scene: DashboardScene): Dashboard {
|
|||||||
if (child instanceof SceneGridItem) {
|
if (child instanceof SceneGridItem) {
|
||||||
panels.push(gridItemToPanel(child));
|
panels.push(gridItemToPanel(child));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (child instanceof SceneGridRow) {
|
||||||
|
// Skip repeat clones
|
||||||
|
if (child.state.key!.indexOf('-clone-') > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gridRowToSaveModel(child, panels);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,3 +107,37 @@ export function gridItemToPanel(gridItem: SceneGridItemLike): Panel {
|
|||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function gridRowToSaveModel(gridRow: SceneGridRow, panelsArray: Array<Panel | RowPanel>) {
|
||||||
|
const rowPanel: RowPanel = {
|
||||||
|
type: 'row',
|
||||||
|
id: getPanelIdForVizPanel(gridRow),
|
||||||
|
title: gridRow.state.title,
|
||||||
|
gridPos: {
|
||||||
|
x: gridRow.state.x ?? 0,
|
||||||
|
y: gridRow.state.y ?? 0,
|
||||||
|
w: gridRow.state.width ?? 24,
|
||||||
|
h: gridRow.state.height ?? 1,
|
||||||
|
},
|
||||||
|
collapsed: Boolean(gridRow.state.isCollapsed),
|
||||||
|
panels: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gridRow.state.$behaviors?.length) {
|
||||||
|
const behavior = gridRow.state.$behaviors[0];
|
||||||
|
|
||||||
|
if (behavior instanceof RowRepeaterBehavior) {
|
||||||
|
rowPanel.repeat = behavior.state.variableName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panelsArray.push(rowPanel);
|
||||||
|
|
||||||
|
const panelsInsideRow = gridRow.state.children.map(gridItemToPanel);
|
||||||
|
|
||||||
|
if (gridRow.state.isCollapsed) {
|
||||||
|
rowPanel.panels = panelsInsideRow;
|
||||||
|
} else {
|
||||||
|
panelsArray.push(...panelsInsideRow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import { sceneGraph, SceneObject, VizPanel } from '@grafana/scenes';
|
import { MultiValueVariable, sceneGraph, SceneObject, VizPanel } from '@grafana/scenes';
|
||||||
|
|
||||||
export function getVizPanelKeyForPanelId(panelId: number) {
|
export function getVizPanelKeyForPanelId(panelId: number) {
|
||||||
return `panel-${panelId}`;
|
return `panel-${panelId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPanelIdForVizPanel(panel: VizPanel): number {
|
export function getPanelIdForVizPanel(panel: SceneObject): number {
|
||||||
return parseInt(panel.state.key!.replace('panel-', ''), 10);
|
return parseInt(panel.state.key!.replace('panel-', ''), 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,3 +66,19 @@ export function forceRenderChildren(model: SceneObject, recursive?: boolean) {
|
|||||||
forceRenderChildren(child, recursive);
|
forceRenderChildren(child, recursive);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMultiVariableValues(variable: MultiValueVariable) {
|
||||||
|
const { value, text, options } = variable.state;
|
||||||
|
|
||||||
|
if (variable.hasAllValue()) {
|
||||||
|
return {
|
||||||
|
values: options.map((o) => o.value),
|
||||||
|
texts: options.map((o) => o.label),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
values: Array.isArray(value) ? value : [value],
|
||||||
|
texts: Array.isArray(text) ? text : [text],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -4,7 +4,7 @@ import { getGridWithMultipleTimeRanges } from './gridMultiTimeRange';
|
|||||||
import { getMultipleGridLayoutTest } from './gridMultiple';
|
import { getMultipleGridLayoutTest } from './gridMultiple';
|
||||||
import { getGridWithMultipleData } from './gridWithMultipleData';
|
import { getGridWithMultipleData } from './gridWithMultipleData';
|
||||||
import { getQueryVariableDemo } from './queryVariableDemo';
|
import { getQueryVariableDemo } from './queryVariableDemo';
|
||||||
import { getRepeatingPanelsDemo } from './repeatingPanels';
|
import { getRepeatingPanelsDemo, getRepeatingRowsDemo } from './repeatingPanels';
|
||||||
import { getSceneWithRows } from './sceneWithRows';
|
import { getSceneWithRows } from './sceneWithRows';
|
||||||
import { getTransformationsDemo } from './transformations';
|
import { getTransformationsDemo } from './transformations';
|
||||||
import { getVariablesDemo, getVariablesDemoWithAll } from './variablesDemo';
|
import { getVariablesDemo, getVariablesDemoWithAll } from './variablesDemo';
|
||||||
@ -22,6 +22,7 @@ export function getScenes(): SceneDef[] {
|
|||||||
{ title: 'Variables', getScene: getVariablesDemo },
|
{ title: 'Variables', getScene: getVariablesDemo },
|
||||||
{ title: 'Variables with All values', getScene: getVariablesDemoWithAll },
|
{ title: 'Variables with All values', getScene: getVariablesDemoWithAll },
|
||||||
{ title: 'Variables - Repeating panels', getScene: getRepeatingPanelsDemo },
|
{ title: 'Variables - Repeating panels', getScene: getRepeatingPanelsDemo },
|
||||||
|
{ title: 'Variables - Repeating rows', getScene: getRepeatingRowsDemo },
|
||||||
{ title: 'Query variable', getScene: getQueryVariableDemo },
|
{ title: 'Query variable', getScene: getQueryVariableDemo },
|
||||||
{ title: 'Transformations demo', getScene: getTransformationsDemo },
|
{ title: 'Transformations demo', getScene: getTransformationsDemo },
|
||||||
];
|
];
|
||||||
|
@ -8,9 +8,11 @@ import {
|
|||||||
PanelBuilders,
|
PanelBuilders,
|
||||||
SceneGridLayout,
|
SceneGridLayout,
|
||||||
SceneControlsSpacer,
|
SceneControlsSpacer,
|
||||||
|
SceneGridRow,
|
||||||
} from '@grafana/scenes';
|
} from '@grafana/scenes';
|
||||||
import { VariableRefresh } from '@grafana/schema';
|
import { VariableRefresh } from '@grafana/schema';
|
||||||
import { PanelRepeaterGridItem } from 'app/features/dashboard-scene/scene/PanelRepeaterGridItem';
|
import { PanelRepeaterGridItem } from 'app/features/dashboard-scene/scene/PanelRepeaterGridItem';
|
||||||
|
import { RowRepeaterBehavior } from 'app/features/dashboard-scene/scene/RowRepeaterBehavior';
|
||||||
|
|
||||||
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene';
|
import { DashboardScene } from '../../dashboard-scene/scene/DashboardScene';
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ export function getRepeatingPanelsDemo(): DashboardScene {
|
|||||||
refresh: VariableRefresh.onTimeRangeChanged,
|
refresh: VariableRefresh.onTimeRangeChanged,
|
||||||
optionsToReturn: [
|
optionsToReturn: [
|
||||||
{ label: 'A', value: 'A' },
|
{ label: 'A', value: 'A' },
|
||||||
{ label: 'B', value: 'C' },
|
{ label: 'B', value: 'B' },
|
||||||
],
|
],
|
||||||
options: [],
|
options: [],
|
||||||
$behaviors: [changeVariable],
|
$behaviors: [changeVariable],
|
||||||
@ -78,27 +80,24 @@ export function getRepeatingPanelsDemo(): DashboardScene {
|
|||||||
function changeVariable(variable: TestVariable) {
|
function changeVariable(variable: TestVariable) {
|
||||||
const sub = variable.subscribeToState((state, old) => {
|
const sub = variable.subscribeToState((state, old) => {
|
||||||
if (!state.loading && old.loading) {
|
if (!state.loading && old.loading) {
|
||||||
setTimeout(() => {
|
if (variable.state.optionsToReturn.length === 2) {
|
||||||
if (variable.state.query === 'AB') {
|
variable.setState({
|
||||||
variable.setState({
|
query: 'ABC',
|
||||||
query: 'ABC',
|
optionsToReturn: [
|
||||||
optionsToReturn: [
|
{ label: 'A', value: 'A' },
|
||||||
{ label: 'A', value: 'A' },
|
{ label: 'B', value: 'B' },
|
||||||
{ label: 'B', value: 'B' },
|
{ label: 'C', value: 'C' },
|
||||||
{ label: 'C', value: 'C' },
|
],
|
||||||
],
|
});
|
||||||
});
|
} else {
|
||||||
} else {
|
variable.setState({
|
||||||
variable.setState({
|
query: 'AB',
|
||||||
query: 'AB',
|
optionsToReturn: [
|
||||||
optionsToReturn: [
|
{ label: 'A', value: 'A' },
|
||||||
{ label: 'A', value: 'A' },
|
{ label: 'B', value: 'B' },
|
||||||
{ label: 'B', value: 'B' },
|
],
|
||||||
],
|
});
|
||||||
});
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,3 +105,87 @@ function changeVariable(variable: TestVariable) {
|
|||||||
sub.unsubscribe();
|
sub.unsubscribe();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRepeatingRowsDemo(): DashboardScene {
|
||||||
|
return new DashboardScene({
|
||||||
|
title: 'Variables - Repeating rows',
|
||||||
|
$variables: new SceneVariableSet({
|
||||||
|
variables: [
|
||||||
|
new TestVariable({
|
||||||
|
name: 'server',
|
||||||
|
query: 'AB',
|
||||||
|
value: ['A', 'B', 'C'],
|
||||||
|
text: ['A', 'B', 'C'],
|
||||||
|
delayMs: 2000,
|
||||||
|
isMulti: true,
|
||||||
|
includeAll: true,
|
||||||
|
refresh: VariableRefresh.onTimeRangeChanged,
|
||||||
|
optionsToReturn: [
|
||||||
|
{ label: 'A', value: 'A' },
|
||||||
|
{ label: 'B', value: 'B' },
|
||||||
|
{ label: 'C', value: 'C' },
|
||||||
|
],
|
||||||
|
options: [],
|
||||||
|
//$behaviors: [changeVariable],
|
||||||
|
}),
|
||||||
|
new TestVariable({
|
||||||
|
name: 'pod',
|
||||||
|
query: 'AB',
|
||||||
|
value: ['Mu', 'Ma', 'Mi'],
|
||||||
|
text: ['Mu', 'Ma', 'Mi'],
|
||||||
|
delayMs: 2000,
|
||||||
|
isMulti: true,
|
||||||
|
includeAll: true,
|
||||||
|
refresh: VariableRefresh.onTimeRangeChanged,
|
||||||
|
optionsToReturn: [
|
||||||
|
{ label: 'Mu', value: 'Mu' },
|
||||||
|
{ label: 'Ma', value: 'Ma' },
|
||||||
|
{ label: 'Mi', value: 'Mi' },
|
||||||
|
],
|
||||||
|
options: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
body: new SceneGridLayout({
|
||||||
|
isDraggable: true,
|
||||||
|
isResizable: true,
|
||||||
|
children: [
|
||||||
|
new SceneGridRow({
|
||||||
|
title: 'Row $server',
|
||||||
|
key: 'Row A',
|
||||||
|
isCollapsed: false,
|
||||||
|
y: 0,
|
||||||
|
x: 0,
|
||||||
|
$behaviors: [
|
||||||
|
new RowRepeaterBehavior({
|
||||||
|
variableName: 'server',
|
||||||
|
sources: [
|
||||||
|
new PanelRepeaterGridItem({
|
||||||
|
variableName: 'pod',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 24,
|
||||||
|
height: 5,
|
||||||
|
itemHeight: 5,
|
||||||
|
//@ts-expect-error
|
||||||
|
source: PanelBuilders.timeseries()
|
||||||
|
.setTitle('server = $server, pod = $pod')
|
||||||
|
.setData(getQueryRunnerWithRandomWalkQuery({ alias: 'server = $server, pod = $pod' }))
|
||||||
|
.build(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
$timeRange: new SceneTimeRange(),
|
||||||
|
actions: [],
|
||||||
|
controls: [
|
||||||
|
new VariableValueSelectors({}),
|
||||||
|
new SceneControlsSpacer(),
|
||||||
|
new SceneTimePicker({}),
|
||||||
|
new SceneRefreshPicker({}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
10
yarn.lock
10
yarn.lock
@ -3939,9 +3939,9 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@grafana/scenes@npm:^0.29.0":
|
"@grafana/scenes@npm:^1.1.1":
|
||||||
version: 0.29.0
|
version: 1.1.1
|
||||||
resolution: "@grafana/scenes@npm:0.29.0"
|
resolution: "@grafana/scenes@npm:1.1.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@grafana/e2e-selectors": 10.0.2
|
"@grafana/e2e-selectors": 10.0.2
|
||||||
react-grid-layout: 1.3.4
|
react-grid-layout: 1.3.4
|
||||||
@ -3953,7 +3953,7 @@ __metadata:
|
|||||||
"@grafana/runtime": 10.0.3
|
"@grafana/runtime": 10.0.3
|
||||||
"@grafana/schema": 10.0.3
|
"@grafana/schema": 10.0.3
|
||||||
"@grafana/ui": 10.0.3
|
"@grafana/ui": 10.0.3
|
||||||
checksum: 8a91ea0290d54c5c081595e85f853b14af90468da3d85b5cd83e26d24d4fc84cceea9be930aa9239439ff3af7388ae3f5bebe1973214c686f4cf143a64752548
|
checksum: 6405998a40e38f088443f5d4b1f5ea1f73e5bc0d08216e4aaccf8ff0b68ec4c3d691430857f357d3eed335dee0dc2e24c41c5c2f286fc7fdd32375382ad3eafe
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -19288,7 +19288,7 @@ __metadata:
|
|||||||
"@grafana/lezer-traceql": 0.0.5
|
"@grafana/lezer-traceql": 0.0.5
|
||||||
"@grafana/monaco-logql": ^0.0.7
|
"@grafana/monaco-logql": ^0.0.7
|
||||||
"@grafana/runtime": "workspace:*"
|
"@grafana/runtime": "workspace:*"
|
||||||
"@grafana/scenes": ^0.29.0
|
"@grafana/scenes": ^1.1.1
|
||||||
"@grafana/schema": "workspace:*"
|
"@grafana/schema": "workspace:*"
|
||||||
"@grafana/tsconfig": ^1.3.0-rc1
|
"@grafana/tsconfig": ^1.3.0-rc1
|
||||||
"@grafana/ui": "workspace:*"
|
"@grafana/ui": "workspace:*"
|
||||||
|
Loading…
Reference in New Issue
Block a user