mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Annotations: Support filtering the target panels (#66325)
Co-authored-by: Adela Almasan <adela.almasan@grafana.com> Co-authored-by: nmarrs <nathanielmarrs@gmail.com>
This commit is contained in:
parent
a384194e15
commit
9452c0d718
@ -858,7 +858,9 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "3"],
|
[0, 0, 0, "Do not use any type assertions.", "3"],
|
||||||
[0, 0, 0, "Do not use any type assertions.", "4"]
|
[0, 0, 0, "Do not use any type assertions.", "4"],
|
||||||
|
[0, 0, 0, "Do not use any type assertions.", "5"],
|
||||||
|
[0, 0, 0, "Do not use any type assertions.", "6"]
|
||||||
],
|
],
|
||||||
"packages/grafana-toolkit/src/cli/tasks/component.create.ts:5381": [
|
"packages/grafana-toolkit/src/cli/tasks/component.create.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
@ -2231,7 +2233,10 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"]
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "4"]
|
||||||
],
|
],
|
||||||
"public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsEdit.tsx:5381": [
|
"public/app/features/dashboard/components/AnnotationSettings/AnnotationSettingsEdit.tsx:5381": [
|
||||||
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"],
|
||||||
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "1"],
|
||||||
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "2"],
|
||||||
|
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "3"]
|
||||||
],
|
],
|
||||||
"public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts:5381": [
|
"public/app/features/dashboard/components/DashExportModal/DashboardExporter.test.ts:5381": [
|
||||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||||
|
433
devenv/dev-dashboards/annotations/annotation-filtering.json
Normal file
433
devenv/dev-dashboards/annotations/annotation-filtering.json
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": {
|
||||||
|
"type": "grafana",
|
||||||
|
"uid": "-- Grafana --"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "grafana",
|
||||||
|
"uid": "-- Grafana --"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"target": {
|
||||||
|
"limit": 100,
|
||||||
|
"matchAny": false,
|
||||||
|
"tags": [],
|
||||||
|
"type": "dashboard"
|
||||||
|
},
|
||||||
|
"type": "dashboard"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"filter": {
|
||||||
|
"exclude": false,
|
||||||
|
"ids": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"iconColor": "red",
|
||||||
|
"name": "Red, only panel 1",
|
||||||
|
"target": {
|
||||||
|
"lines": 4,
|
||||||
|
"refId": "Anno",
|
||||||
|
"scenarioId": "annotations"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"filter": {
|
||||||
|
"exclude": true,
|
||||||
|
"ids": [
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"iconColor": "yellow",
|
||||||
|
"name": "Yellow - all except 1",
|
||||||
|
"target": {
|
||||||
|
"lines": 5,
|
||||||
|
"refId": "Anno",
|
||||||
|
"scenarioId": "annotations"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"enable": true,
|
||||||
|
"filter": {
|
||||||
|
"exclude": false,
|
||||||
|
"ids": [
|
||||||
|
3,
|
||||||
|
4
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"iconColor": "dark-purple",
|
||||||
|
"name": "Purple only panel 3+4",
|
||||||
|
"target": {
|
||||||
|
"lines": 6,
|
||||||
|
"refId": "Anno",
|
||||||
|
"scenarioId": "annotations"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"id": 119,
|
||||||
|
"links": [],
|
||||||
|
"liveNow": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"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": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 1,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Panel one",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"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": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Panel two",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"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": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 3,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Panel three",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": {
|
||||||
|
"type": "testdata",
|
||||||
|
"uid": "PD8C576611E62080A"
|
||||||
|
},
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"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": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": "Panel four",
|
||||||
|
"type": "timeseries"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": "",
|
||||||
|
"schemaVersion": 38,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": ["gdev", "annotations"],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-30m",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Annotation filtering",
|
||||||
|
"uid": "ed155665",
|
||||||
|
"version": 17,
|
||||||
|
"weekStart": ""
|
||||||
|
}
|
@ -868,4 +868,4 @@
|
|||||||
"uid": "e7c29343-6d1e-4167-9c13-803fe5be8c46",
|
"uid": "e7c29343-6d1e-4167-9c13-803fe5be8c46",
|
||||||
"version": 48,
|
"version": 48,
|
||||||
"weekStart": ""
|
"weekStart": ""
|
||||||
}
|
}
|
||||||
|
@ -119,8 +119,7 @@
|
|||||||
"mode": "absolute",
|
"mode": "absolute",
|
||||||
"steps": [
|
"steps": [
|
||||||
{
|
{
|
||||||
"color": "green",
|
"color": "green"
|
||||||
"value": null
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"color": "red",
|
"color": "red",
|
||||||
@ -582,4 +581,4 @@
|
|||||||
"uid": "cdd412c4",
|
"uid": "cdd412c4",
|
||||||
"version": 6,
|
"version": 6,
|
||||||
"weekStart": ""
|
"weekStart": ""
|
||||||
}
|
}
|
||||||
|
@ -453,4 +453,4 @@
|
|||||||
"uid": "ZqZnVvFZz",
|
"uid": "ZqZnVvFZz",
|
||||||
"version": 8,
|
"version": 8,
|
||||||
"weekStart": ""
|
"weekStart": ""
|
||||||
}
|
}
|
||||||
|
@ -1040,4 +1040,4 @@
|
|||||||
"uid": "U_bZIMRMk",
|
"uid": "U_bZIMRMk",
|
||||||
"version": 7,
|
"version": 7,
|
||||||
"weekStart": ""
|
"weekStart": ""
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,13 @@ local dashboard = grafana.dashboard;
|
|||||||
id: 0,
|
id: 0,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
dashboard.new('annotation-filtering', import '../dev-dashboards/annotations/annotation-filtering.json') +
|
||||||
|
resource.addMetadata('folder', 'dev-dashboards') +
|
||||||
|
{
|
||||||
|
spec+: {
|
||||||
|
id: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
dashboard.new('auto_decimals', import '../dev-dashboards/panel-common/auto_decimals.json') +
|
dashboard.new('auto_decimals', import '../dev-dashboards/panel-common/auto_decimals.json') +
|
||||||
resource.addMetadata('folder', 'dev-dashboards') +
|
resource.addMetadata('folder', 'dev-dashboards') +
|
||||||
{
|
{
|
||||||
|
@ -13,32 +13,84 @@ title: Dashboard kind
|
|||||||
|
|
||||||
A Grafana dashboard.
|
A Grafana dashboard.
|
||||||
|
|
||||||
| Property | Type | Required | Default | Description |
|
| Property | Type | Required | Default | Description |
|
||||||
|------------------------|-----------------------------------|----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|------------------------|---------------------------------------------|----------|-----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `editable` | boolean | **Yes** | `true` | Whether a dashboard is editable or not. |
|
| `editable` | boolean | **Yes** | `true` | Whether a dashboard is editable or not. |
|
||||||
| `graphTooltip` | integer | **Yes** | `0` | 0 for no shared crosshair or tooltip (default).<br/>1 for shared crosshair.<br/>2 for shared crosshair AND shared tooltip.<br/>Possible values are: `0`, `1`, `2`. |
|
| `graphTooltip` | integer | **Yes** | `0` | 0 for no shared crosshair or tooltip (default).<br/>1 for shared crosshair.<br/>2 for shared crosshair AND shared tooltip.<br/>Possible values are: `0`, `1`, `2`. |
|
||||||
| `schemaVersion` | uint16 | **Yes** | `36` | Version of the JSON schema, incremented each time a Grafana update brings<br/>changes to said schema.<br/>TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion |
|
| `schemaVersion` | uint16 | **Yes** | `36` | Version of the JSON schema, incremented each time a Grafana update brings<br/>changes to said schema.<br/>TODO this is the existing schema numbering system. It will be replaced by Thema's themaVersion |
|
||||||
| `style` | string | **Yes** | `dark` | Theme of dashboard.<br/>Possible values are: `dark`, `light`. |
|
| `style` | string | **Yes** | `dark` | Theme of dashboard.<br/>Possible values are: `dark`, `light`. |
|
||||||
| `annotations` | [object](#annotations) | No | | TODO docs |
|
| `annotations` | [AnnotationContainer](#annotationcontainer) | No | | TODO -- should not be a public interface on its own, but required for Veneer |
|
||||||
| `description` | string | No | | Description of dashboard. |
|
| `description` | string | No | | Description of dashboard. |
|
||||||
| `fiscalYearStartMonth` | integer | No | `0` | The month that the fiscal year starts on. 0 = January, 11 = December<br/>Constraint: `>=0 & <12`. |
|
| `fiscalYearStartMonth` | integer | No | `0` | The month that the fiscal year starts on. 0 = January, 11 = December<br/>Constraint: `>=0 & <12`. |
|
||||||
| `gnetId` | string | No | | For dashboards imported from the https://grafana.com/grafana/dashboards/ portal |
|
| `gnetId` | string | No | | For dashboards imported from the https://grafana.com/grafana/dashboards/ portal |
|
||||||
| `id` | integer | No | | Unique numeric identifier for the dashboard.<br/>TODO must isolate or remove identifiers local to a Grafana instance...? |
|
| `id` | integer | No | | Unique numeric identifier for the dashboard.<br/>TODO must isolate or remove identifiers local to a Grafana instance...? |
|
||||||
| `links` | [DashboardLink](#dashboardlink)[] | No | | TODO docs |
|
| `links` | [DashboardLink](#dashboardlink)[] | No | | TODO docs |
|
||||||
| `liveNow` | boolean | No | | When set to true, the dashboard will redraw panels at an interval matching the pixel width.<br/>This will keep data "moving left" regardless of the query refresh rate. This setting helps<br/>avoid dashboards presenting stale live data |
|
| `liveNow` | boolean | No | | When set to true, the dashboard will redraw panels at an interval matching the pixel width.<br/>This will keep data "moving left" regardless of the query refresh rate. This setting helps<br/>avoid dashboards presenting stale live data |
|
||||||
| `panels` | [object](#panels)[] | No | | |
|
| `panels` | [object](#panels)[] | No | | |
|
||||||
| `refresh` | | No | | Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". |
|
| `refresh` | | No | | Refresh rate of dashboard. Represented via interval string, e.g. "5s", "1m", "1h", "1d". |
|
||||||
| `revision` | integer | No | | This property should only be used in dashboards defined by plugins. It is a quick check<br/>to see if the version has changed since the last time. Unclear why using the version property<br/>is insufficient. |
|
| `revision` | integer | No | | This property should only be used in dashboards defined by plugins. It is a quick check<br/>to see if the version has changed since the last time. Unclear why using the version property<br/>is insufficient. |
|
||||||
| `snapshot` | [Snapshot](#snapshot) | No | | TODO docs |
|
| `snapshot` | [Snapshot](#snapshot) | No | | TODO docs |
|
||||||
| `tags` | string[] | No | | Tags associated with dashboard. |
|
| `tags` | string[] | No | | Tags associated with dashboard. |
|
||||||
| `templating` | [object](#templating) | No | | TODO docs |
|
| `templating` | [object](#templating) | No | | TODO docs |
|
||||||
| `time` | [object](#time) | No | | Time range for dashboard, e.g. last 6 hours, last 7 days, etc |
|
| `time` | [object](#time) | No | | Time range for dashboard, e.g. last 6 hours, last 7 days, etc |
|
||||||
| `timepicker` | [object](#timepicker) | No | | TODO docs<br/>TODO this appears to be spread all over in the frontend. Concepts will likely need tidying in tandem with schema changes |
|
| `timepicker` | [object](#timepicker) | No | | TODO docs<br/>TODO this appears to be spread all over in the frontend. Concepts will likely need tidying in tandem with schema changes |
|
||||||
| `timezone` | string | No | `browser` | Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc". |
|
| `timezone` | string | No | `browser` | Timezone of dashboard. Accepts IANA TZDB zone ID or "browser" or "utc". |
|
||||||
| `title` | string | No | | Title of dashboard. |
|
| `title` | string | No | | Title of dashboard. |
|
||||||
| `uid` | string | No | | Unique dashboard identifier that can be generated by anyone. string (8-40) |
|
| `uid` | string | No | | Unique dashboard identifier that can be generated by anyone. string (8-40) |
|
||||||
| `version` | uint32 | No | | Version of the dashboard, incremented each time the dashboard is updated. |
|
| `version` | uint32 | No | | Version of the dashboard, incremented each time the dashboard is updated. |
|
||||||
| `weekStart` | string | No | | TODO docs |
|
| `weekStart` | string | No | | TODO docs |
|
||||||
|
|
||||||
|
### AnnotationContainer
|
||||||
|
|
||||||
|
TODO -- should not be a public interface on its own, but required for Veneer
|
||||||
|
|
||||||
|
| Property | Type | Required | Default | Description |
|
||||||
|
|----------|---------------------------------------|----------|---------|-------------|
|
||||||
|
| `list` | [AnnotationQuery](#annotationquery)[] | No | | |
|
||||||
|
|
||||||
|
### AnnotationQuery
|
||||||
|
|
||||||
|
TODO docs
|
||||||
|
FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
||||||
|
|
||||||
|
| Property | Type | Required | Default | Description |
|
||||||
|
|--------------|-------------------------------------------------|----------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `datasource` | [object](#datasource) | **Yes** | | TODO: Should be DataSourceRef |
|
||||||
|
| `enable` | boolean | **Yes** | `true` | When enabled the annotation query is issued with every dashboard refresh |
|
||||||
|
| `iconColor` | string | **Yes** | | Color to use for the annotation event markers |
|
||||||
|
| `name` | string | **Yes** | | Name of annotation. |
|
||||||
|
| `filter` | [AnnotationPanelFilter](#annotationpanelfilter) | No | | |
|
||||||
|
| `hide` | boolean | No | `false` | Annotation queries can be toggled on or off at the top of the dashboard.<br/>When hide is true, the toggle is not shown in the dashboard. |
|
||||||
|
| `target` | [AnnotationTarget](#annotationtarget) | No | | TODO: this should be a regular DataQuery that depends on the selected dashboard<br/>these match the properties of the "grafana" datasouce that is default in most dashboards |
|
||||||
|
| `type` | string | No | | TODO -- this should not exist here, it is based on the --grafana-- datasource |
|
||||||
|
|
||||||
|
### AnnotationPanelFilter
|
||||||
|
|
||||||
|
| Property | Type | Required | Default | Description |
|
||||||
|
|-----------|-----------|----------|---------|-----------------------------------------------------|
|
||||||
|
| `ids` | integer[] | **Yes** | | Panel IDs that should be included or excluded |
|
||||||
|
| `exclude` | boolean | No | `false` | Should the specified panels be included or excluded |
|
||||||
|
|
||||||
|
### AnnotationTarget
|
||||||
|
|
||||||
|
TODO: this should be a regular DataQuery that depends on the selected dashboard
|
||||||
|
these match the properties of the "grafana" datasouce that is default in most dashboards
|
||||||
|
|
||||||
|
| Property | Type | Required | Default | Description |
|
||||||
|
|------------|----------|----------|---------|-------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| `limit` | integer | **Yes** | | Only required/valid for the grafana datasource...<br/>but code+tests is already depending on it so hard to change |
|
||||||
|
| `matchAny` | boolean | **Yes** | | Only required/valid for the grafana datasource...<br/>but code+tests is already depending on it so hard to change |
|
||||||
|
| `tags` | string[] | **Yes** | | Only required/valid for the grafana datasource...<br/>but code+tests is already depending on it so hard to change |
|
||||||
|
| `type` | string | **Yes** | | Only required/valid for the grafana datasource...<br/>but code+tests is already depending on it so hard to change |
|
||||||
|
|
||||||
|
### Datasource
|
||||||
|
|
||||||
|
TODO: Should be DataSourceRef
|
||||||
|
|
||||||
|
| Property | Type | Required | Default | Description |
|
||||||
|
|----------|--------|----------|---------|-------------|
|
||||||
|
| `type` | string | No | | |
|
||||||
|
| `uid` | string | No | | |
|
||||||
|
|
||||||
### DashboardLink
|
### DashboardLink
|
||||||
|
|
||||||
@ -76,52 +128,6 @@ TODO docs
|
|||||||
| `userId` | uint32 | **Yes** | | TODO docs |
|
| `userId` | uint32 | **Yes** | | TODO docs |
|
||||||
| `url` | string | No | | TODO docs |
|
| `url` | string | No | | TODO docs |
|
||||||
|
|
||||||
### Annotations
|
|
||||||
|
|
||||||
TODO docs
|
|
||||||
|
|
||||||
| Property | Type | Required | Default | Description |
|
|
||||||
|----------|---------------------------------------|----------|---------|-------------|
|
|
||||||
| `list` | [AnnotationQuery](#annotationquery)[] | No | | |
|
|
||||||
|
|
||||||
### AnnotationQuery
|
|
||||||
|
|
||||||
TODO docs
|
|
||||||
FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
|
||||||
|
|
||||||
| Property | Type | Required | Default | Description |
|
|
||||||
|--------------|---------------------------------------|----------|-------------|-----------------------------------|
|
|
||||||
| `builtIn` | uint8 | **Yes** | `0` | |
|
|
||||||
| `datasource` | [object](#datasource) | **Yes** | | Datasource to use for annotation. |
|
|
||||||
| `enable` | boolean | **Yes** | `true` | Whether annotation is enabled. |
|
|
||||||
| `showIn` | uint8 | **Yes** | `0` | |
|
|
||||||
| `type` | string | **Yes** | `dashboard` | |
|
|
||||||
| `hide` | boolean | No | `false` | Whether to hide annotation. |
|
|
||||||
| `iconColor` | string | No | | Annotation icon color. |
|
|
||||||
| `name` | string | No | | Name of annotation. |
|
|
||||||
| `rawQuery` | string | No | | Query for annotation data. |
|
|
||||||
| `target` | [AnnotationTarget](#annotationtarget) | No | | TODO docs |
|
|
||||||
|
|
||||||
### AnnotationTarget
|
|
||||||
|
|
||||||
TODO docs
|
|
||||||
|
|
||||||
| Property | Type | Required | Default | Description |
|
|
||||||
|------------|----------|----------|---------|-------------|
|
|
||||||
| `limit` | integer | **Yes** | | |
|
|
||||||
| `matchAny` | boolean | **Yes** | | |
|
|
||||||
| `tags` | string[] | **Yes** | | |
|
|
||||||
| `type` | string | **Yes** | | |
|
|
||||||
|
|
||||||
### Datasource
|
|
||||||
|
|
||||||
Datasource to use for annotation.
|
|
||||||
|
|
||||||
| Property | Type | Required | Default | Description |
|
|
||||||
|----------|--------|----------|---------|-------------|
|
|
||||||
| `type` | string | No | | |
|
|
||||||
| `uid` | string | No | | |
|
|
||||||
|
|
||||||
### Panels
|
### Panels
|
||||||
|
|
||||||
| Property | Type | Required | Default | Description |
|
| Property | Type | Required | Default | Description |
|
||||||
|
61
e2e/various-suite/filter-annotations.spec.ts
Normal file
61
e2e/various-suite/filter-annotations.spec.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { e2e } from '@grafana/e2e';
|
||||||
|
const DASHBOARD_ID = 'ed155665';
|
||||||
|
|
||||||
|
e2e.scenario({
|
||||||
|
describeName: 'Annotations filtering',
|
||||||
|
itName: 'Tests switching filter type updates the UI accordingly',
|
||||||
|
addScenarioDataSource: false,
|
||||||
|
addScenarioDashBoard: false,
|
||||||
|
skipScenario: false,
|
||||||
|
scenario: () => {
|
||||||
|
e2e.flows.openDashboard({ uid: DASHBOARD_ID });
|
||||||
|
|
||||||
|
e2e.components.PageToolbar.item('Dashboard settings').click();
|
||||||
|
e2e.components.Tab.title('Annotations').click();
|
||||||
|
cy.contains('New query').click();
|
||||||
|
e2e.pages.Dashboard.Settings.Annotations.Settings.name().clear().type('Red - Panel two');
|
||||||
|
|
||||||
|
e2e.pages.Dashboard.Settings.Annotations.NewAnnotation.showInLabel()
|
||||||
|
.should('be.visible')
|
||||||
|
.within(() => {
|
||||||
|
// All panels
|
||||||
|
e2e.components.Annotations.annotationsTypeInput().click({ force: true }).type('All panels{enter}');
|
||||||
|
e2e.components.Annotations.annotationsChoosePanelInput().should('not.exist');
|
||||||
|
|
||||||
|
// All panels except
|
||||||
|
e2e.components.Annotations.annotationsTypeInput().click({ force: true }).type('All panels except{enter}');
|
||||||
|
e2e.components.Annotations.annotationsChoosePanelInput().should('be.visible');
|
||||||
|
|
||||||
|
// Selected panels
|
||||||
|
e2e.components.Annotations.annotationsTypeInput().click({ force: true }).type('Selected panels{enter}');
|
||||||
|
e2e.components.Annotations.annotationsChoosePanelInput()
|
||||||
|
.should('be.visible')
|
||||||
|
.click({ force: true })
|
||||||
|
.type('Panel two{enter}');
|
||||||
|
});
|
||||||
|
|
||||||
|
e2e.pages.Dashboard.Settings.Annotations.NewAnnotation.previewInDashboard().click({ force: true });
|
||||||
|
|
||||||
|
e2e.pages.Dashboard.SubMenu.Annotations.annotationsWrapper()
|
||||||
|
.should('be.visible')
|
||||||
|
.within(() => {
|
||||||
|
e2e.pages.Dashboard.SubMenu.Annotations.annotationLabel('Red - Panel two').should('be.visible');
|
||||||
|
e2e.pages.Dashboard.SubMenu.Annotations.annotationToggle('Red - Panel two')
|
||||||
|
.should('be.checked')
|
||||||
|
.uncheck({ force: true })
|
||||||
|
.should('not.be.checked')
|
||||||
|
.check({ force: true });
|
||||||
|
|
||||||
|
e2e.pages.Dashboard.SubMenu.Annotations.annotationLabel('Red, only panel 1').should('be.visible');
|
||||||
|
e2e.pages.Dashboard.SubMenu.Annotations.annotationToggle('Red, only panel 1').should('be.checked');
|
||||||
|
});
|
||||||
|
|
||||||
|
e2e().wait(3000);
|
||||||
|
|
||||||
|
e2e.components.Panels.Panel.title('Panel one')
|
||||||
|
.should('exist')
|
||||||
|
.within(() => {
|
||||||
|
e2e.pages.SoloPanel.Annotations.marker().should('exist').should('have.length', 4);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
@ -85,10 +85,18 @@ lineage: seqs: [
|
|||||||
templating?: {
|
templating?: {
|
||||||
list?: [...#VariableModel] @grafanamaturity(NeedsExpertReview)
|
list?: [...#VariableModel] @grafanamaturity(NeedsExpertReview)
|
||||||
}
|
}
|
||||||
// TODO docs
|
|
||||||
annotations?: {
|
// TODO -- should not be a public interface on its own, but required for Veneer
|
||||||
|
#AnnotationContainer: {
|
||||||
|
// annoying... but required so that the list is defined using the nested Veneer
|
||||||
|
@grafana(TSVeneer="type")
|
||||||
|
|
||||||
list?: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview)
|
list?: [...#AnnotationQuery] @grafanamaturity(NeedsExpertReview)
|
||||||
}
|
} @cuetsy(kind="interface")
|
||||||
|
|
||||||
|
// TODO docs
|
||||||
|
annotations?: #AnnotationContainer
|
||||||
|
|
||||||
// TODO docs
|
// TODO docs
|
||||||
links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview)
|
links?: [...#DashboardLink] @grafanamaturity(NeedsExpertReview)
|
||||||
|
|
||||||
@ -97,39 +105,72 @@ lineage: seqs: [
|
|||||||
///////////////////////////////////////
|
///////////////////////////////////////
|
||||||
// Definitions (referenced above) are declared below
|
// Definitions (referenced above) are declared below
|
||||||
|
|
||||||
// TODO docs
|
// TODO: this should be a regular DataQuery that depends on the selected dashboard
|
||||||
|
// these match the properties of the "grafana" datasouce that is default in most dashboards
|
||||||
#AnnotationTarget: {
|
#AnnotationTarget: {
|
||||||
limit: int64
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
|
limit: int64
|
||||||
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
matchAny: bool
|
matchAny: bool
|
||||||
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
tags: [...string]
|
tags: [...string]
|
||||||
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
type: string
|
type: string
|
||||||
|
... // datasource will stick their raw DataQuery here
|
||||||
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
} @cuetsy(kind="interface") @grafanamaturity(NeedsExpertReview)
|
||||||
|
|
||||||
|
#AnnotationPanelFilter: {
|
||||||
|
// Should the specified panels be included or excluded
|
||||||
|
exclude?: bool | *false
|
||||||
|
|
||||||
|
// Panel IDs that should be included or excluded
|
||||||
|
ids: [...uint8]
|
||||||
|
} @cuetsy(kind="interface")
|
||||||
|
|
||||||
// TODO docs
|
// TODO docs
|
||||||
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
||||||
#AnnotationQuery: {
|
#AnnotationQuery: {
|
||||||
// Datasource to use for annotation.
|
@grafana(TSVeneer="type")
|
||||||
|
|
||||||
|
// Name of annotation.
|
||||||
|
name: string
|
||||||
|
|
||||||
|
// TODO: Should be DataSourceRef
|
||||||
datasource: {
|
datasource: {
|
||||||
type?: string
|
type?: string
|
||||||
uid?: string
|
uid?: string
|
||||||
} @grafanamaturity(NeedsExpertReview)
|
} @grafanamaturity(NeedsExpertReview)
|
||||||
|
|
||||||
// Whether annotation is enabled.
|
// When enabled the annotation query is issued with every dashboard refresh
|
||||||
enable: bool | *true @grafanamaturity(NeedsExpertReview)
|
enable: bool | *true
|
||||||
// Name of annotation.
|
|
||||||
name?: string @grafanamaturity(NeedsExpertReview)
|
// Annotation queries can be toggled on or off at the top of the dashboard.
|
||||||
builtIn: uint8 | *0 @grafanamaturity(NeedsExpertReview) // TODO should this be persisted at all?
|
// When hide is true, the toggle is not shown in the dashboard.
|
||||||
// Whether to hide annotation.
|
hide?: bool | *false
|
||||||
hide?: bool | *false @grafanamaturity(NeedsExpertReview)
|
|
||||||
// Annotation icon color.
|
// Color to use for the annotation event markers
|
||||||
iconColor?: string @grafanamaturity(NeedsExpertReview)
|
iconColor: string
|
||||||
type: string | *"dashboard" @grafanamaturity(NeedsExpertReview)
|
|
||||||
// Query for annotation data.
|
// Optionally
|
||||||
rawQuery?: string @grafanamaturity(NeedsExpertReview)
|
filter?: #AnnotationPanelFilter
|
||||||
showIn: uint8 | *0 @grafanamaturity(NeedsExpertReview)
|
|
||||||
target?: #AnnotationTarget @grafanamaturity(NeedsExpertReview)
|
// TODO.. this should just be a normal query target
|
||||||
|
target?: #AnnotationTarget
|
||||||
|
|
||||||
|
// TODO -- this should not exist here, it is based on the --grafana-- datasource
|
||||||
|
type?: string @grafanamaturity(NeedsExpertReview)
|
||||||
|
|
||||||
|
// unless datasources have migrated to the target+mapping,
|
||||||
|
// they just spread their query into the base object :(
|
||||||
|
...
|
||||||
} @cuetsy(kind="interface")
|
} @cuetsy(kind="interface")
|
||||||
|
|
||||||
|
#LoadingState: "NotStarted" | "Loading" | "Streaming" | "Done" | "Error" @cuetsy(kind="enum") @grafanamaturity(NeedsExpertReview)
|
||||||
|
|
||||||
// FROM: packages/grafana-data/src/types/templateVars.ts
|
// FROM: packages/grafana-data/src/types/templateVars.ts
|
||||||
// TODO docs
|
// TODO docs
|
||||||
// TODO what about what's in public/app/features/types.ts?
|
// TODO what about what's in public/app/features/types.ts?
|
||||||
|
@ -1,30 +1,23 @@
|
|||||||
import { ComponentType } from 'react';
|
import { ComponentType } from 'react';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
|
import { DataQuery, AnnotationQuery as SchemaAnnotationQuery } from '@grafana/schema';
|
||||||
|
|
||||||
import { DataFrame } from './dataFrame';
|
import { DataFrame } from './dataFrame';
|
||||||
import { QueryEditorProps } from './datasource';
|
import { QueryEditorProps } from './datasource';
|
||||||
import { DataQuery, DataSourceRef } from './query';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This JSON object is stored in the dashboard json model.
|
* This JSON object is stored in the dashboard json model.
|
||||||
*/
|
*/
|
||||||
export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> {
|
export interface AnnotationQuery<TQuery extends DataQuery = DataQuery> extends SchemaAnnotationQuery<TQuery> {
|
||||||
datasource?: DataSourceRef | null;
|
|
||||||
|
|
||||||
enable: boolean;
|
|
||||||
name: string;
|
|
||||||
iconColor: string;
|
|
||||||
hide?: boolean;
|
|
||||||
builtIn?: number;
|
|
||||||
type?: string;
|
|
||||||
snapshotData?: any;
|
snapshotData?: any;
|
||||||
|
|
||||||
// Standard datasource query
|
|
||||||
target?: TQuery;
|
|
||||||
|
|
||||||
// Convert a dataframe to an AnnotationEvent
|
// Convert a dataframe to an AnnotationEvent
|
||||||
mappings?: AnnotationEventMappings;
|
mappings?: AnnotationEventMappings;
|
||||||
|
|
||||||
|
// When using the 'grafana' datasource, this may be dashboard
|
||||||
|
type?: string;
|
||||||
|
|
||||||
// Sadly plugins can set any property directly on the main object
|
// Sadly plugins can set any property directly on the main object
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
}
|
}
|
||||||
@ -51,7 +44,7 @@ export interface AnnotationEvent {
|
|||||||
newState?: string;
|
newState?: string;
|
||||||
|
|
||||||
// Currently used to merge annotations from alerts and dashboard
|
// Currently used to merge annotations from alerts and dashboard
|
||||||
source?: any; // source.type === 'dashboard'
|
source?: any; // source.type === 'dashboard' -- should be AnnotationQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AnnotationEventUIModel {
|
export interface AnnotationEventUIModel {
|
||||||
|
@ -57,6 +57,7 @@ export class DataSourcePlugin<
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated -- register the annotation support in the instance constructor */
|
||||||
setAnnotationQueryCtrl(AnnotationsQueryCtrl: any) {
|
setAnnotationQueryCtrl(AnnotationsQueryCtrl: any) {
|
||||||
this.components.AnnotationsQueryCtrl = AnnotationsQueryCtrl;
|
this.components.AnnotationsQueryCtrl = AnnotationsQueryCtrl;
|
||||||
return this;
|
return this;
|
||||||
|
@ -416,4 +416,8 @@ export const Components = {
|
|||||||
Variables: {
|
Variables: {
|
||||||
variableOption: 'data-testid variable-option',
|
variableOption: 'data-testid variable-option',
|
||||||
},
|
},
|
||||||
|
Annotations: {
|
||||||
|
annotationsTypeInput: 'annotations-type-input',
|
||||||
|
annotationsChoosePanelInput: 'choose-panels-input',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@ -66,6 +66,11 @@ export const Pages = {
|
|||||||
submenuItemValueDropDownDropDown: 'Variable options',
|
submenuItemValueDropDownDropDown: 'Variable options',
|
||||||
submenuItemValueDropDownOptionTexts: (item: string) =>
|
submenuItemValueDropDownOptionTexts: (item: string) =>
|
||||||
`data-testid Dashboard template variables Variable Value DropDown option text ${item}`,
|
`data-testid Dashboard template variables Variable Value DropDown option text ${item}`,
|
||||||
|
Annotations: {
|
||||||
|
annotationsWrapper: 'data-testid annotation-wrapper',
|
||||||
|
annotationLabel: (label: string) => `data-testid Dashboard annotations submenu Label ${label}`,
|
||||||
|
annotationToggle: (label: string) => `data-testid Dashboard annotations submenu Toggle ${label}`,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Settings: {
|
Settings: {
|
||||||
Actions: {
|
Actions: {
|
||||||
@ -93,6 +98,11 @@ export const Pages = {
|
|||||||
Settings: {
|
Settings: {
|
||||||
name: 'Annotations settings name input',
|
name: 'Annotations settings name input',
|
||||||
},
|
},
|
||||||
|
NewAnnotation: {
|
||||||
|
panelFilterSelect: 'data-testid annotations-panel-filter',
|
||||||
|
showInLabel: 'show-in-label',
|
||||||
|
previewInDashboard: 'data-testid annotations-preview',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Variables: {
|
Variables: {
|
||||||
List: {
|
List: {
|
||||||
@ -239,6 +249,9 @@ export const Pages = {
|
|||||||
},
|
},
|
||||||
SoloPanel: {
|
SoloPanel: {
|
||||||
url: (page: string) => `/d-solo/${page}`,
|
url: (page: string) => `/d-solo/${page}`,
|
||||||
|
Annotations: {
|
||||||
|
marker: 'data-testid annotation-marker',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
PluginsList: {
|
PluginsList: {
|
||||||
page: 'Plugins list page',
|
page: 'Plugins list page',
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
// Raw generated types from Dashboard kind.
|
// Raw generated types from Dashboard kind.
|
||||||
export type {
|
export type {
|
||||||
AnnotationTarget,
|
AnnotationTarget,
|
||||||
AnnotationQuery,
|
AnnotationPanelFilter,
|
||||||
DashboardLink,
|
DashboardLink,
|
||||||
DashboardLinkType,
|
DashboardLinkType,
|
||||||
VariableType,
|
VariableType,
|
||||||
@ -34,7 +34,7 @@ export type {
|
|||||||
// Raw generated enums and default consts from dashboard kind.
|
// Raw generated enums and default consts from dashboard kind.
|
||||||
export {
|
export {
|
||||||
defaultAnnotationTarget,
|
defaultAnnotationTarget,
|
||||||
defaultAnnotationQuery,
|
defaultAnnotationPanelFilter,
|
||||||
LoadingState,
|
LoadingState,
|
||||||
defaultDashboardLink,
|
defaultDashboardLink,
|
||||||
FieldColorModeId,
|
FieldColorModeId,
|
||||||
@ -59,6 +59,8 @@ export {
|
|||||||
// TODO generate code such that tsc enforces type compatibility between raw and veneer decls
|
// TODO generate code such that tsc enforces type compatibility between raw and veneer decls
|
||||||
export type {
|
export type {
|
||||||
Dashboard,
|
Dashboard,
|
||||||
|
AnnotationContainer,
|
||||||
|
AnnotationQuery,
|
||||||
VariableModel,
|
VariableModel,
|
||||||
DataSourceRef,
|
DataSourceRef,
|
||||||
DataTransformerConfig,
|
DataTransformerConfig,
|
||||||
@ -79,6 +81,8 @@ export type {
|
|||||||
// TODO generate code such that tsc enforces type compatibility between raw and veneer decls
|
// TODO generate code such that tsc enforces type compatibility between raw and veneer decls
|
||||||
export {
|
export {
|
||||||
defaultDashboard,
|
defaultDashboard,
|
||||||
|
defaultAnnotationContainer,
|
||||||
|
defaultAnnotationQuery,
|
||||||
defaultVariableModel,
|
defaultVariableModel,
|
||||||
VariableHide,
|
VariableHide,
|
||||||
defaultPanel,
|
defaultPanel,
|
||||||
|
@ -9,12 +9,40 @@
|
|||||||
// Run 'make gen-cue' from repository root to regenerate.
|
// Run 'make gen-cue' from repository root to regenerate.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO docs
|
* TODO -- should not be a public interface on its own, but required for Veneer
|
||||||
|
*/
|
||||||
|
export interface AnnotationContainer {
|
||||||
|
list?: Array<AnnotationQuery>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultAnnotationContainer: Partial<AnnotationContainer> = {
|
||||||
|
list: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: this should be a regular DataQuery that depends on the selected dashboard
|
||||||
|
* these match the properties of the "grafana" datasouce that is default in most dashboards
|
||||||
*/
|
*/
|
||||||
export interface AnnotationTarget {
|
export interface AnnotationTarget {
|
||||||
|
/**
|
||||||
|
* Only required/valid for the grafana datasource...
|
||||||
|
* but code+tests is already depending on it so hard to change
|
||||||
|
*/
|
||||||
limit: number;
|
limit: number;
|
||||||
|
/**
|
||||||
|
* Only required/valid for the grafana datasource...
|
||||||
|
* but code+tests is already depending on it so hard to change
|
||||||
|
*/
|
||||||
matchAny: boolean;
|
matchAny: boolean;
|
||||||
|
/**
|
||||||
|
* Only required/valid for the grafana datasource...
|
||||||
|
* but code+tests is already depending on it so hard to change
|
||||||
|
*/
|
||||||
tags: Array<string>;
|
tags: Array<string>;
|
||||||
|
/**
|
||||||
|
* Only required/valid for the grafana datasource...
|
||||||
|
* but code+tests is already depending on it so hard to change
|
||||||
|
*/
|
||||||
type: string;
|
type: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,52 +50,78 @@ export const defaultAnnotationTarget: Partial<AnnotationTarget> = {
|
|||||||
tags: [],
|
tags: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface AnnotationPanelFilter {
|
||||||
|
/**
|
||||||
|
* Should the specified panels be included or excluded
|
||||||
|
*/
|
||||||
|
exclude?: boolean;
|
||||||
|
/**
|
||||||
|
* Panel IDs that should be included or excluded
|
||||||
|
*/
|
||||||
|
ids: Array<number>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const defaultAnnotationPanelFilter: Partial<AnnotationPanelFilter> = {
|
||||||
|
exclude: false,
|
||||||
|
ids: [],
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO docs
|
* TODO docs
|
||||||
* FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
* FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
||||||
*/
|
*/
|
||||||
export interface AnnotationQuery {
|
export interface AnnotationQuery {
|
||||||
builtIn: number; // TODO should this be persisted at all?
|
|
||||||
/**
|
/**
|
||||||
* Datasource to use for annotation.
|
* TODO: Should be DataSourceRef
|
||||||
*/
|
*/
|
||||||
datasource: {
|
datasource: {
|
||||||
type?: string;
|
type?: string;
|
||||||
uid?: string;
|
uid?: string;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Whether annotation is enabled.
|
* When enabled the annotation query is issued with every dashboard refresh
|
||||||
*/
|
*/
|
||||||
enable: boolean;
|
enable: boolean;
|
||||||
/**
|
/**
|
||||||
* Whether to hide annotation.
|
* Optionally
|
||||||
|
*/
|
||||||
|
filter?: AnnotationPanelFilter;
|
||||||
|
/**
|
||||||
|
* Annotation queries can be toggled on or off at the top of the dashboard.
|
||||||
|
* When hide is true, the toggle is not shown in the dashboard.
|
||||||
*/
|
*/
|
||||||
hide?: boolean;
|
hide?: boolean;
|
||||||
/**
|
/**
|
||||||
* Annotation icon color.
|
* Color to use for the annotation event markers
|
||||||
*/
|
*/
|
||||||
iconColor?: string;
|
iconColor: string;
|
||||||
/**
|
/**
|
||||||
* Name of annotation.
|
* Name of annotation.
|
||||||
*/
|
*/
|
||||||
name?: string;
|
name: string;
|
||||||
/**
|
/**
|
||||||
* Query for annotation data.
|
* TODO.. this should just be a normal query target
|
||||||
*/
|
*/
|
||||||
rawQuery?: string;
|
|
||||||
showIn: number;
|
|
||||||
target?: AnnotationTarget;
|
target?: AnnotationTarget;
|
||||||
type: string;
|
/**
|
||||||
|
* TODO -- this should not exist here, it is based on the --grafana-- datasource
|
||||||
|
*/
|
||||||
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const defaultAnnotationQuery: Partial<AnnotationQuery> = {
|
export const defaultAnnotationQuery: Partial<AnnotationQuery> = {
|
||||||
builtIn: 0,
|
|
||||||
enable: true,
|
enable: true,
|
||||||
hide: false,
|
hide: false,
|
||||||
showIn: 0,
|
|
||||||
type: 'dashboard',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum LoadingState {
|
||||||
|
Done = 'Done',
|
||||||
|
Error = 'Error',
|
||||||
|
Loading = 'Loading',
|
||||||
|
NotStarted = 'NotStarted',
|
||||||
|
Streaming = 'Streaming',
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FROM: packages/grafana-data/src/types/templateVars.ts
|
* FROM: packages/grafana-data/src/types/templateVars.ts
|
||||||
* TODO docs
|
* TODO docs
|
||||||
@ -107,14 +161,6 @@ export enum VariableHide {
|
|||||||
hideVariable = 2,
|
hideVariable = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum LoadingState {
|
|
||||||
Done = 'Done',
|
|
||||||
Error = 'Error',
|
|
||||||
Loading = 'Loading',
|
|
||||||
NotStarted = 'NotStarted',
|
|
||||||
Streaming = 'Streaming',
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ref to a DataSource instance
|
* Ref to a DataSource instance
|
||||||
*/
|
*/
|
||||||
@ -662,9 +708,7 @@ export interface Dashboard {
|
|||||||
/**
|
/**
|
||||||
* TODO docs
|
* TODO docs
|
||||||
*/
|
*/
|
||||||
annotations?: {
|
annotations?: AnnotationContainer;
|
||||||
list?: Array<AnnotationQuery>;
|
|
||||||
};
|
|
||||||
/**
|
/**
|
||||||
* Description of dashboard.
|
* Description of dashboard.
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { DataSourceRef as CommonDataSourceRef } from '../common/common.gen';
|
import { DataSourceRef as CommonDataSourceRef, DataSourceRef } from '../common/common.gen';
|
||||||
import * as raw from '../raw/dashboard/x/dashboard_types.gen';
|
import * as raw from '../raw/dashboard/x/dashboard_types.gen';
|
||||||
|
|
||||||
|
import { DataQuery } from './common.types';
|
||||||
|
|
||||||
export type { CommonDataSourceRef as DataSourceRef };
|
export type { CommonDataSourceRef as DataSourceRef };
|
||||||
|
|
||||||
export interface Panel<TOptions = Record<string, unknown>, TCustomFieldConfig = Record<string, unknown>>
|
export interface Panel<TOptions = Record<string, unknown>, TCustomFieldConfig = Record<string, unknown>>
|
||||||
@ -28,13 +30,24 @@ export interface VariableModel
|
|||||||
datasource: CommonDataSourceRef | null;
|
datasource: CommonDataSourceRef | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Dashboard extends Omit<raw.Dashboard, 'templating'> {
|
export interface Dashboard extends Omit<raw.Dashboard, 'templating' | 'annotations'> {
|
||||||
panels?: Array<Panel | raw.RowPanel | raw.GraphPanel | raw.HeatmapPanel>;
|
panels?: Array<Panel | raw.RowPanel | raw.GraphPanel | raw.HeatmapPanel>;
|
||||||
|
annotations?: AnnotationContainer;
|
||||||
templating?: {
|
templating?: {
|
||||||
list?: VariableModel[];
|
list?: VariableModel[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AnnotationQuery<TQuery extends DataQuery = DataQuery>
|
||||||
|
extends Omit<raw.AnnotationQuery, 'target' | 'datasource'> {
|
||||||
|
datasource?: DataSourceRef | null;
|
||||||
|
target?: TQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AnnotationContainer extends Omit<raw.AnnotationContainer, 'list'> {
|
||||||
|
list?: AnnotationQuery[]; // use the version from this file
|
||||||
|
}
|
||||||
|
|
||||||
export interface FieldConfig<TOptions = Record<string, unknown>> extends raw.FieldConfig {
|
export interface FieldConfig<TOptions = Record<string, unknown>> extends raw.FieldConfig {
|
||||||
custom?: TOptions & Record<string, unknown>;
|
custom?: TOptions & Record<string, unknown>;
|
||||||
}
|
}
|
||||||
@ -69,3 +82,6 @@ export const defaultPanel: Partial<Panel> = raw.defaultPanel;
|
|||||||
export const defaultFieldConfig: Partial<FieldConfig> = raw.defaultFieldConfig;
|
export const defaultFieldConfig: Partial<FieldConfig> = raw.defaultFieldConfig;
|
||||||
export const defaultFieldConfigSource: Partial<FieldConfigSource> = raw.defaultFieldConfigSource;
|
export const defaultFieldConfigSource: Partial<FieldConfigSource> = raw.defaultFieldConfigSource;
|
||||||
export const defaultMatcherConfig: Partial<MatcherConfig> = raw.defaultMatcherConfig;
|
export const defaultMatcherConfig: Partial<MatcherConfig> = raw.defaultMatcherConfig;
|
||||||
|
export const defaultAnnotationQuery: Partial<AnnotationQuery> = raw.defaultAnnotationQuery as AnnotationQuery;
|
||||||
|
export const defaultAnnotationContainer: Partial<AnnotationContainer> =
|
||||||
|
raw.defaultAnnotationContainer as AnnotationContainer;
|
||||||
|
@ -160,52 +160,75 @@ const (
|
|||||||
VariableTypeTextbox VariableType = "textbox"
|
VariableTypeTextbox VariableType = "textbox"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO -- should not be a public interface on its own, but required for Veneer
|
||||||
|
type AnnotationContainer struct {
|
||||||
|
List []AnnotationQuery `json:"list,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnnotationPanelFilter defines model for AnnotationPanelFilter.
|
||||||
|
type AnnotationPanelFilter struct {
|
||||||
|
// Should the specified panels be included or excluded
|
||||||
|
Exclude *bool `json:"exclude,omitempty"`
|
||||||
|
|
||||||
|
// Panel IDs that should be included or excluded
|
||||||
|
Ids []int `json:"ids"`
|
||||||
|
}
|
||||||
|
|
||||||
// TODO docs
|
// TODO docs
|
||||||
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
// FROM: AnnotationQuery in grafana-data/src/types/annotations.ts
|
||||||
type AnnotationQuery struct {
|
type AnnotationQuery struct {
|
||||||
BuiltIn int `json:"builtIn"`
|
// TODO: Should be DataSourceRef
|
||||||
|
|
||||||
// Datasource to use for annotation.
|
|
||||||
Datasource struct {
|
Datasource struct {
|
||||||
Type *string `json:"type,omitempty"`
|
Type *string `json:"type,omitempty"`
|
||||||
Uid *string `json:"uid,omitempty"`
|
Uid *string `json:"uid,omitempty"`
|
||||||
} `json:"datasource"`
|
} `json:"datasource"`
|
||||||
|
|
||||||
// Whether annotation is enabled.
|
// When enabled the annotation query is issued with every dashboard refresh
|
||||||
Enable bool `json:"enable"`
|
Enable bool `json:"enable"`
|
||||||
|
Filter *AnnotationPanelFilter `json:"filter,omitempty"`
|
||||||
|
|
||||||
// Whether to hide annotation.
|
// Annotation queries can be toggled on or off at the top of the dashboard.
|
||||||
|
// When hide is true, the toggle is not shown in the dashboard.
|
||||||
Hide *bool `json:"hide,omitempty"`
|
Hide *bool `json:"hide,omitempty"`
|
||||||
|
|
||||||
// Annotation icon color.
|
// Color to use for the annotation event markers
|
||||||
IconColor *string `json:"iconColor,omitempty"`
|
IconColor string `json:"iconColor"`
|
||||||
|
|
||||||
// Name of annotation.
|
// Name of annotation.
|
||||||
Name *string `json:"name,omitempty"`
|
Name string `json:"name"`
|
||||||
|
|
||||||
// Query for annotation data.
|
// TODO: this should be a regular DataQuery that depends on the selected dashboard
|
||||||
RawQuery *string `json:"rawQuery,omitempty"`
|
// these match the properties of the "grafana" datasouce that is default in most dashboards
|
||||||
ShowIn int `json:"showIn"`
|
|
||||||
|
|
||||||
// TODO docs
|
|
||||||
Target *AnnotationTarget `json:"target,omitempty"`
|
Target *AnnotationTarget `json:"target,omitempty"`
|
||||||
Type string `json:"type"`
|
|
||||||
|
// TODO -- this should not exist here, it is based on the --grafana-- datasource
|
||||||
|
Type *string `json:"type,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO docs
|
// TODO: this should be a regular DataQuery that depends on the selected dashboard
|
||||||
|
// these match the properties of the "grafana" datasouce that is default in most dashboards
|
||||||
type AnnotationTarget struct {
|
type AnnotationTarget struct {
|
||||||
Limit int64 `json:"limit"`
|
// Only required/valid for the grafana datasource...
|
||||||
MatchAny bool `json:"matchAny"`
|
// but code+tests is already depending on it so hard to change
|
||||||
Tags []string `json:"tags"`
|
Limit int64 `json:"limit"`
|
||||||
Type string `json:"type"`
|
|
||||||
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
|
MatchAny bool `json:"matchAny"`
|
||||||
|
|
||||||
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
|
||||||
|
// Only required/valid for the grafana datasource...
|
||||||
|
// but code+tests is already depending on it so hard to change
|
||||||
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dashboard defines model for Dashboard.
|
// Dashboard defines model for Dashboard.
|
||||||
type Dashboard struct {
|
type Dashboard struct {
|
||||||
// TODO docs
|
// TODO -- should not be a public interface on its own, but required for Veneer
|
||||||
Annotations *struct {
|
Annotations *AnnotationContainer `json:"annotations,omitempty"`
|
||||||
List []AnnotationQuery `json:"list,omitempty"`
|
|
||||||
} `json:"annotations,omitempty"`
|
|
||||||
|
|
||||||
// Description of dashboard.
|
// Description of dashboard.
|
||||||
Description *string `json:"description,omitempty"`
|
Description *string `json:"description,omitempty"`
|
||||||
|
@ -70,7 +70,7 @@ func (pd *PublicDashboardServiceImpl) FindAnnotations(ctx context.Context, reqDT
|
|||||||
Tags: item.Tags,
|
Tags: item.Tags,
|
||||||
IsRegion: item.TimeEnd > 0 && item.Time != item.TimeEnd,
|
IsRegion: item.TimeEnd > 0 && item.Time != item.TimeEnd,
|
||||||
Text: item.Text,
|
Text: item.Text,
|
||||||
Color: *anno.IconColor,
|
Color: anno.IconColor,
|
||||||
Time: item.Time,
|
Time: item.Time,
|
||||||
TimeEnd: item.TimeEnd,
|
TimeEnd: item.TimeEnd,
|
||||||
Source: anno,
|
Source: anno,
|
||||||
@ -78,7 +78,7 @@ func (pd *PublicDashboardServiceImpl) FindAnnotations(ctx context.Context, reqDT
|
|||||||
|
|
||||||
// We want dashboard annotations to reference the panel they're for. If no panelId is provided, they'll show up on all panels
|
// We want dashboard annotations to reference the panel they're for. If no panelId is provided, they'll show up on all panels
|
||||||
// which is only intended for tag and org annotations.
|
// which is only intended for tag and org annotations.
|
||||||
if anno.Type == "dashboard" {
|
if anno.Type != nil && *anno.Type == "dashboard" {
|
||||||
event.PanelId = item.PanelID
|
event.PanelId = item.PanelID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
"github.com/grafana/grafana/pkg/services/tag/tagimpl"
|
||||||
"github.com/grafana/grafana/pkg/setting"
|
"github.com/grafana/grafana/pkg/setting"
|
||||||
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
"github.com/grafana/grafana/pkg/tsdb/intervalv2"
|
||||||
|
"github.com/grafana/grafana/pkg/util"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/mock"
|
"github.com/stretchr/testify/mock"
|
||||||
@ -743,21 +744,21 @@ func TestFindAnnotations(t *testing.T) {
|
|||||||
grafanaAnnotation := DashAnnotation{
|
grafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Target: &dashboard2.AnnotationTarget{
|
Target: &dashboard2.AnnotationTarget{
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
MatchAny: false,
|
MatchAny: false,
|
||||||
Tags: nil,
|
Tags: nil,
|
||||||
Type: "dashboard",
|
Type: "dashboard",
|
||||||
},
|
},
|
||||||
Type: "dashboard",
|
Type: util.Pointer("dashboard"),
|
||||||
}
|
}
|
||||||
grafanaTagAnnotation := DashAnnotation{
|
grafanaTagAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Target: &dashboard2.AnnotationTarget{
|
Target: &dashboard2.AnnotationTarget{
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
MatchAny: false,
|
MatchAny: false,
|
||||||
@ -816,8 +817,8 @@ func TestFindAnnotations(t *testing.T) {
|
|||||||
grafanaAnnotation := DashAnnotation{
|
grafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Target: &dashboard2.AnnotationTarget{
|
Target: &dashboard2.AnnotationTarget{
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
MatchAny: false,
|
MatchAny: false,
|
||||||
@ -876,26 +877,26 @@ func TestFindAnnotations(t *testing.T) {
|
|||||||
disabledGrafanaAnnotation := DashAnnotation{
|
disabledGrafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: false,
|
Enable: false,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
}
|
}
|
||||||
grafanaAnnotation := DashAnnotation{
|
grafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Target: &dashboard2.AnnotationTarget{
|
Target: &dashboard2.AnnotationTarget{
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
MatchAny: true,
|
MatchAny: true,
|
||||||
Tags: nil,
|
Tags: nil,
|
||||||
Type: "dashboard",
|
Type: "dashboard",
|
||||||
},
|
},
|
||||||
Type: "dashboard",
|
Type: util.Pointer("dashboard"),
|
||||||
}
|
}
|
||||||
queryAnnotation := DashAnnotation{
|
queryAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("prometheus", "abc123"),
|
Datasource: CreateDatasource("prometheus", "abc123"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
}
|
}
|
||||||
annos := []DashAnnotation{grafanaAnnotation, queryAnnotation, disabledGrafanaAnnotation}
|
annos := []DashAnnotation{grafanaAnnotation, queryAnnotation, disabledGrafanaAnnotation}
|
||||||
dashboard := AddAnnotationsToDashboard(t, dash, annos)
|
dashboard := AddAnnotationsToDashboard(t, dash, annos)
|
||||||
@ -975,15 +976,15 @@ func TestFindAnnotations(t *testing.T) {
|
|||||||
grafanaAnnotation := DashAnnotation{
|
grafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Target: &dashboard2.AnnotationTarget{
|
Target: &dashboard2.AnnotationTarget{
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
MatchAny: false,
|
MatchAny: false,
|
||||||
Tags: nil,
|
Tags: nil,
|
||||||
Type: "dashboard",
|
Type: "dashboard",
|
||||||
},
|
},
|
||||||
Type: "dashboard",
|
Type: util.Pointer("dashboard"),
|
||||||
}
|
}
|
||||||
annos := []DashAnnotation{grafanaAnnotation}
|
annos := []DashAnnotation{grafanaAnnotation}
|
||||||
dashboard := AddAnnotationsToDashboard(t, dash, annos)
|
dashboard := AddAnnotationsToDashboard(t, dash, annos)
|
||||||
@ -1010,8 +1011,8 @@ func TestFindAnnotations(t *testing.T) {
|
|||||||
grafanaAnnotation := DashAnnotation{
|
grafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Target: &dashboard2.AnnotationTarget{
|
Target: &dashboard2.AnnotationTarget{
|
||||||
Limit: 100,
|
Limit: 100,
|
||||||
MatchAny: false,
|
MatchAny: false,
|
||||||
@ -1039,9 +1040,9 @@ func TestFindAnnotations(t *testing.T) {
|
|||||||
grafanaAnnotation := DashAnnotation{
|
grafanaAnnotation := DashAnnotation{
|
||||||
Datasource: CreateDatasource("grafana", "grafana"),
|
Datasource: CreateDatasource("grafana", "grafana"),
|
||||||
Enable: true,
|
Enable: true,
|
||||||
Name: &name,
|
Name: name,
|
||||||
IconColor: &color,
|
IconColor: color,
|
||||||
Type: "dashboard",
|
Type: util.Pointer("dashboard"),
|
||||||
Target: nil,
|
Target: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,31 @@
|
|||||||
import React, { useState } from 'react';
|
import { css } from '@emotion/css';
|
||||||
|
import React, { useMemo, useState } from 'react';
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { AnnotationQuery, DataSourceInstanceSettings, getDataSourceRef } from '@grafana/data';
|
import {
|
||||||
|
AnnotationQuery,
|
||||||
|
DataSourceInstanceSettings,
|
||||||
|
getDataSourceRef,
|
||||||
|
GrafanaTheme2,
|
||||||
|
SelectableValue,
|
||||||
|
} from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { Stack } from '@grafana/experimental';
|
import { Stack } from '@grafana/experimental';
|
||||||
import { DataSourcePicker, getDataSourceSrv, locationService } from '@grafana/runtime';
|
import { DataSourcePicker, getDataSourceSrv, locationService } from '@grafana/runtime';
|
||||||
import { Button, Checkbox, Field, FieldSet, HorizontalGroup, Input } from '@grafana/ui';
|
import { AnnotationPanelFilter } from '@grafana/schema/src/raw/dashboard/x/dashboard_types.gen';
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Checkbox,
|
||||||
|
Field,
|
||||||
|
FieldSet,
|
||||||
|
HorizontalGroup,
|
||||||
|
Input,
|
||||||
|
MultiSelect,
|
||||||
|
Select,
|
||||||
|
useStyles2,
|
||||||
|
} from '@grafana/ui';
|
||||||
import { ColorValueEditor } from 'app/core/components/OptionsUI/color';
|
import { ColorValueEditor } from 'app/core/components/OptionsUI/color';
|
||||||
|
import config from 'app/core/config';
|
||||||
import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor';
|
import StandardAnnotationQueryEditor from 'app/features/annotations/components/StandardAnnotationQueryEditor';
|
||||||
|
|
||||||
import { DashboardModel } from '../../state/DashboardModel';
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
@ -21,8 +40,16 @@ type Props = {
|
|||||||
export const newAnnotationName = 'New annotation';
|
export const newAnnotationName = 'New annotation';
|
||||||
|
|
||||||
export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
||||||
|
const styles = useStyles2(getStyles);
|
||||||
const [annotation, setAnnotation] = useState(dashboard.annotations.list[editIdx]);
|
const [annotation, setAnnotation] = useState(dashboard.annotations.list[editIdx]);
|
||||||
|
|
||||||
|
const panelFilter = useMemo(() => {
|
||||||
|
if (!annotation.filter) {
|
||||||
|
return PanelFilterType.AllPanels;
|
||||||
|
}
|
||||||
|
return annotation.filter.exclude ? PanelFilterType.ExcludePanels : PanelFilterType.IncludePanels;
|
||||||
|
}, [annotation.filter]);
|
||||||
|
|
||||||
const { value: ds } = useAsync(() => {
|
const { value: ds } = useAsync(() => {
|
||||||
return getDataSourceSrv().get(annotation.datasource);
|
return getDataSourceSrv().get(annotation.datasource);
|
||||||
}, [annotation.datasource]);
|
}, [annotation.datasource]);
|
||||||
@ -65,6 +92,31 @@ export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onFilterTypeChange = (v: SelectableValue<PanelFilterType>) => {
|
||||||
|
let filter =
|
||||||
|
v.value === PanelFilterType.AllPanels
|
||||||
|
? undefined
|
||||||
|
: {
|
||||||
|
exclude: v.value === PanelFilterType.ExcludePanels,
|
||||||
|
ids: annotation.filter?.ids ?? [],
|
||||||
|
};
|
||||||
|
onUpdate({ ...annotation, filter });
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddFilterPanelID = (selections: Array<SelectableValue<number>>) => {
|
||||||
|
if (!Array.isArray(selections)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filter: AnnotationPanelFilter = {
|
||||||
|
exclude: panelFilter === PanelFilterType.ExcludePanels,
|
||||||
|
ids: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
selections.forEach((selection) => selection.value && filter.ids.push(selection.value));
|
||||||
|
onUpdate({ ...annotation, filter });
|
||||||
|
};
|
||||||
|
|
||||||
const onApply = goBackToList;
|
const onApply = goBackToList;
|
||||||
|
|
||||||
const onPreview = () => {
|
const onPreview = () => {
|
||||||
@ -79,9 +131,30 @@ export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
|||||||
|
|
||||||
const isNewAnnotation = annotation.name === newAnnotationName;
|
const isNewAnnotation = annotation.name === newAnnotationName;
|
||||||
|
|
||||||
|
const sortFn = (a: SelectableValue<number>, b: SelectableValue<number>) => {
|
||||||
|
if (a.label && b.label) {
|
||||||
|
return a.label.toLowerCase().localeCompare(b.label.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const panels: Array<SelectableValue<number>> = useMemo(
|
||||||
|
() =>
|
||||||
|
dashboard?.panels
|
||||||
|
.map((panel) => ({
|
||||||
|
value: panel.id,
|
||||||
|
label: panel.title ?? `Panel ${panel.id}`,
|
||||||
|
description: panel.description,
|
||||||
|
imgUrl: config.panels[panel.type].info.logos.small,
|
||||||
|
}))
|
||||||
|
.sort(sortFn) ?? [],
|
||||||
|
[dashboard]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<FieldSet>
|
<FieldSet className={styles.settingsForm}>
|
||||||
<Field label="Name">
|
<Field label="Name">
|
||||||
<Input
|
<Input
|
||||||
aria-label={selectors.pages.Dashboard.Settings.Annotations.Settings.name}
|
aria-label={selectors.pages.Dashboard.Settings.Annotations.Settings.name}
|
||||||
@ -90,17 +163,10 @@ export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
|||||||
autoFocus={isNewAnnotation}
|
autoFocus={isNewAnnotation}
|
||||||
value={annotation.name}
|
value={annotation.name}
|
||||||
onChange={onNameChange}
|
onChange={onNameChange}
|
||||||
width={50}
|
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Data source" htmlFor="data-source-picker">
|
<Field label="Data source" htmlFor="data-source-picker">
|
||||||
<DataSourcePicker
|
<DataSourcePicker annotations variables current={annotation.datasource} onChange={onDataSourceChange} />
|
||||||
width={50}
|
|
||||||
annotations
|
|
||||||
variables
|
|
||||||
current={annotation.datasource}
|
|
||||||
onChange={onDataSourceChange}
|
|
||||||
/>
|
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Enabled" description="When enabled the annotation query is issued every dashboard refresh">
|
<Field label="Enabled" description="When enabled the annotation query is issued every dashboard refresh">
|
||||||
<Checkbox name="enable" id="enable" value={annotation.enable} onChange={onChange} />
|
<Checkbox name="enable" id="enable" value={annotation.enable} onChange={onChange} />
|
||||||
@ -116,6 +182,31 @@ export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
|||||||
<ColorValueEditor value={annotation?.iconColor} onChange={onColorChange} />
|
<ColorValueEditor value={annotation?.iconColor} onChange={onColorChange} />
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
</Field>
|
</Field>
|
||||||
|
<Field label="Show in" aria-label={selectors.pages.Dashboard.Settings.Annotations.NewAnnotation.showInLabel}>
|
||||||
|
<>
|
||||||
|
<Select
|
||||||
|
options={panelFilters}
|
||||||
|
value={panelFilter}
|
||||||
|
onChange={onFilterTypeChange}
|
||||||
|
aria-label={selectors.components.Annotations.annotationsTypeInput}
|
||||||
|
/>
|
||||||
|
{panelFilter !== PanelFilterType.AllPanels && (
|
||||||
|
<MultiSelect
|
||||||
|
options={panels}
|
||||||
|
value={panels.filter((panel) => annotation.filter?.ids.includes(panel.value!))}
|
||||||
|
onChange={onAddFilterPanelID}
|
||||||
|
isClearable={true}
|
||||||
|
placeholder="Choose panels"
|
||||||
|
width={100}
|
||||||
|
closeMenuOnSelect={false}
|
||||||
|
className={styles.select}
|
||||||
|
aria-label={selectors.components.Annotations.annotationsChoosePanelInput}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
</Field>
|
||||||
|
</FieldSet>
|
||||||
|
<FieldSet>
|
||||||
<h3 className="page-heading">Query</h3>
|
<h3 className="page-heading">Query</h3>
|
||||||
{ds?.annotations && dsi && (
|
{ds?.annotations && dsi && (
|
||||||
<StandardAnnotationQueryEditor
|
<StandardAnnotationQueryEditor
|
||||||
@ -133,7 +224,11 @@ export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
|||||||
Delete
|
Delete
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button variant="secondary" onClick={onPreview}>
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
onClick={onPreview}
|
||||||
|
data-testid={selectors.pages.Dashboard.Settings.Annotations.NewAnnotation.previewInDashboard}
|
||||||
|
>
|
||||||
Preview in dashboard
|
Preview in dashboard
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="primary" onClick={onApply}>
|
<Button variant="primary" onClick={onApply}>
|
||||||
@ -144,8 +239,43 @@ export const AnnotationSettingsEdit = ({ editIdx, dashboard }: Props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
AnnotationSettingsEdit.displayName = 'AnnotationSettingsEdit';
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
|
return {
|
||||||
|
settingsForm: css({
|
||||||
|
maxWidth: theme.spacing(60),
|
||||||
|
marginBottom: theme.spacing(2),
|
||||||
|
}),
|
||||||
|
select: css`
|
||||||
|
margin-top: 8px;
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
function goBackToList() {
|
function goBackToList() {
|
||||||
locationService.partial({ editIndex: null });
|
locationService.partial({ editIndex: null });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Synthetic type
|
||||||
|
enum PanelFilterType {
|
||||||
|
AllPanels,
|
||||||
|
IncludePanels,
|
||||||
|
ExcludePanels,
|
||||||
|
}
|
||||||
|
|
||||||
|
const panelFilters = [
|
||||||
|
{
|
||||||
|
label: 'All panels',
|
||||||
|
value: PanelFilterType.AllPanels,
|
||||||
|
description: 'Send the annotation data to all panels that support annotations',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Selected panels',
|
||||||
|
value: PanelFilterType.IncludePanels,
|
||||||
|
description: 'Send the annotations to the explicitly listed panels',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'All panels except',
|
||||||
|
value: PanelFilterType.ExcludePanels,
|
||||||
|
description: 'Do not send annotation data to the following panels',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@ -74,23 +74,11 @@ describe('AnnotationsSettings', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
// we have a default build-in annotation
|
||||||
dashboard = createDashboardModelFixture({
|
dashboard = createDashboardModelFixture({
|
||||||
id: 74,
|
id: 74,
|
||||||
version: 7,
|
version: 7,
|
||||||
annotations: {
|
annotations: {},
|
||||||
list: [
|
|
||||||
{
|
|
||||||
builtIn: 1,
|
|
||||||
datasource: { uid: 'uid1', type: 'grafana' },
|
|
||||||
enable: true,
|
|
||||||
hide: true,
|
|
||||||
iconColor: 'rgba(0, 211, 255, 1)',
|
|
||||||
name: 'Annotations & Alerts',
|
|
||||||
type: 'dashboard',
|
|
||||||
showIn: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
links: [],
|
links: [],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -99,7 +87,8 @@ describe('AnnotationsSettings', () => {
|
|||||||
setup(dashboard);
|
setup(dashboard);
|
||||||
|
|
||||||
expect(screen.queryByRole('grid')).toBeInTheDocument();
|
expect(screen.queryByRole('grid')).toBeInTheDocument();
|
||||||
expect(screen.getByRole('row', { name: /annotations & alerts \(built\-in\) grafana/i })).toBeInTheDocument();
|
expect(screen.getByRole('row', { name: /annotations & alerts \(built-in\) -- grafana --/i })).toBeInTheDocument();
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
|
screen.getByTestId(selectors.components.CallToActionCard.buttonV2('Add annotation query'))
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
@ -115,7 +104,7 @@ describe('AnnotationsSettings', () => {
|
|||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it renders the annotation names or uid if annotation doesnt exist', async () => {
|
test('it renders the annotation names or uid if annotation does not exist', async () => {
|
||||||
dashboard.annotations.list = [
|
dashboard.annotations.list = [
|
||||||
...dashboard.annotations.list,
|
...dashboard.annotations.list,
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@ import { css } from '@emotion/css';
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { AnnotationQuery, EventBus, GrafanaTheme2 } from '@grafana/data';
|
import { AnnotationQuery, EventBus, GrafanaTheme2 } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { InlineField, InlineFieldRow, InlineSwitch, useStyles2 } from '@grafana/ui';
|
import { InlineField, InlineFieldRow, InlineSwitch, useStyles2 } from '@grafana/ui';
|
||||||
import { LoadingIndicator } from '@grafana/ui/src/components/PanelChrome/LoadingIndicator';
|
import { LoadingIndicator } from '@grafana/ui/src/components/PanelChrome/LoadingIndicator';
|
||||||
|
|
||||||
@ -44,8 +45,17 @@ export const AnnotationPicker = ({ annotation, events, onEnabledChanged }: Annot
|
|||||||
return (
|
return (
|
||||||
<div key={annotation.name} className={styles.annotation}>
|
<div key={annotation.name} className={styles.annotation}>
|
||||||
<InlineFieldRow>
|
<InlineFieldRow>
|
||||||
<InlineField label={annotation.name} disabled={loading}>
|
<InlineField
|
||||||
<InlineSwitch value={annotation.enable} onChange={() => onEnabledChanged(annotation)} disabled={loading} />
|
label={annotation.name}
|
||||||
|
disabled={loading}
|
||||||
|
data-testid={selectors.pages.Dashboard.SubMenu.Annotations.annotationLabel(annotation.name)}
|
||||||
|
>
|
||||||
|
<InlineSwitch
|
||||||
|
value={annotation.enable}
|
||||||
|
onChange={() => onEnabledChanged(annotation)}
|
||||||
|
disabled={loading}
|
||||||
|
data-testid={selectors.pages.Dashboard.SubMenu.Annotations.annotationToggle(annotation.name)}
|
||||||
|
/>
|
||||||
</InlineField>
|
</InlineField>
|
||||||
<div className={styles.indicator}>
|
<div className={styles.indicator}>
|
||||||
<LoadingIndicator loading={loading} onCancel={onCancel} />
|
<LoadingIndicator loading={loading} onCancel={onCancel} />
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { AnnotationQuery, DataQuery, EventBus } from '@grafana/data';
|
import { AnnotationQuery, DataQuery, EventBus } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
|
|
||||||
import { AnnotationPicker } from './AnnotationPicker';
|
import { AnnotationPicker } from './AnnotationPicker';
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ export const Annotations = ({ annotations, onAnnotationChanged, events }: Props)
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div data-testId={selectors.pages.Dashboard.SubMenu.Annotations.annotationsWrapper}>
|
||||||
{visibleAnnotations.map((annotation) => (
|
{visibleAnnotations.map((annotation) => (
|
||||||
<AnnotationPicker
|
<AnnotationPicker
|
||||||
events={events}
|
events={events}
|
||||||
@ -30,6 +31,6 @@ export const Annotations = ({ annotations, onAnnotationChanged, events }: Props)
|
|||||||
key={annotation.name}
|
key={annotation.name}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -2016,8 +2016,8 @@ describe('DashboardModel', () => {
|
|||||||
},
|
},
|
||||||
annotations: {
|
annotations: {
|
||||||
list: [
|
list: [
|
||||||
|
// @ts-expect-error
|
||||||
{
|
{
|
||||||
// @ts-expect-error
|
|
||||||
datasource: null,
|
datasource: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -318,13 +318,11 @@ describe('DashboardModel', () => {
|
|||||||
list: [
|
list: [
|
||||||
{
|
{
|
||||||
datasource: { uid: 'fake-uid', type: 'prometheus' },
|
datasource: { uid: 'fake-uid', type: 'prometheus' },
|
||||||
showIn: 0,
|
|
||||||
name: 'Fake annotation',
|
name: 'Fake annotation',
|
||||||
type: 'dashboard',
|
type: 'dashboard',
|
||||||
iconColor: 'rgba(0, 211, 255, 1)',
|
iconColor: 'rgba(0, 211, 255, 1)',
|
||||||
enable: true,
|
enable: true,
|
||||||
hide: false,
|
hide: false,
|
||||||
builtIn: 0,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -46,13 +46,12 @@ export function createPanelJSONFixture(panelInput: Partial<Panel | GraphPanel |
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createAnnotationJSONFixture(annotationInput: Partial<AnnotationQuery>): AnnotationQuery {
|
export function createAnnotationJSONFixture(annotationInput: Partial<AnnotationQuery>): AnnotationQuery {
|
||||||
|
// @ts-expect-error
|
||||||
return {
|
return {
|
||||||
builtIn: 0, // ??
|
|
||||||
datasource: {
|
datasource: {
|
||||||
type: 'foo',
|
type: 'foo',
|
||||||
uid: 'bar',
|
uid: 'bar',
|
||||||
},
|
},
|
||||||
showIn: 2,
|
|
||||||
enable: true,
|
enable: true,
|
||||||
type: 'anno',
|
type: 'anno',
|
||||||
...annotationInput,
|
...annotationInput,
|
||||||
|
@ -44,8 +44,31 @@ function notifyWithError(title: string, err: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getAnnotationsByPanelId(annotations: AnnotationEvent[], panelId?: number) {
|
export function getAnnotationsByPanelId(annotations: AnnotationEvent[], panelId?: number) {
|
||||||
|
if (panelId == null) {
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
return annotations.filter((item) => {
|
return annotations.filter((item) => {
|
||||||
if (panelId !== undefined && item.panelId && item.source?.type === 'dashboard') {
|
let source: AnnotationQuery;
|
||||||
|
source = item.source;
|
||||||
|
if (!source) {
|
||||||
|
return true; // should not happen
|
||||||
|
}
|
||||||
|
|
||||||
|
// generic panel filtering
|
||||||
|
if (source.filter) {
|
||||||
|
const includes = (source.filter.ids ?? []).includes(panelId);
|
||||||
|
if (source.filter.exclude) {
|
||||||
|
if (includes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!includes) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is valid for the main 'grafana' datasource
|
||||||
|
if (item.panelId && item.source.type === 'dashboard') {
|
||||||
return item.panelId === panelId;
|
return item.panelId === panelId;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
DataQueryRequest,
|
DataQueryRequest,
|
||||||
DataQueryResponse,
|
DataQueryResponse,
|
||||||
DataSourceInstanceSettings,
|
DataSourceInstanceSettings,
|
||||||
DataSourceRef,
|
|
||||||
isValidLiveChannelAddress,
|
isValidLiveChannelAddress,
|
||||||
MutableDataFrame,
|
MutableDataFrame,
|
||||||
parseLiveChannelAddress,
|
parseLiveChannelAddress,
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
getTemplateSrv,
|
getTemplateSrv,
|
||||||
StreamingFrameOptions,
|
StreamingFrameOptions,
|
||||||
} from '@grafana/runtime';
|
} from '@grafana/runtime';
|
||||||
|
import { DataSourceRef } from '@grafana/schema';
|
||||||
import { migrateDatasourceNameToRef } from 'app/features/dashboard/state/DashboardMigrator';
|
import { migrateDatasourceNameToRef } from 'app/features/dashboard/state/DashboardMigrator';
|
||||||
|
|
||||||
import { getDashboardSrv } from '../../../features/dashboard/services/DashboardSrv';
|
import { getDashboardSrv } from '../../../features/dashboard/services/DashboardSrv';
|
||||||
@ -61,7 +61,10 @@ export class GrafanaDatasource extends DataSourceWithBackend<GrafanaQuery> {
|
|||||||
datasource = anno.datasource as DataSourceRef;
|
datasource = anno.datasource as DataSourceRef;
|
||||||
}
|
}
|
||||||
|
|
||||||
return { ...anno, refId: anno.name, queryType: GrafanaQueryType.Annotations, datasource };
|
// Filter from streaming query conflicts with filter from annotations
|
||||||
|
const { filter, ...rest } = anno;
|
||||||
|
|
||||||
|
return { ...rest, refId: anno.name, queryType: GrafanaQueryType.Annotations, datasource };
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { DataQuery, DataFrameJSON } from '@grafana/data';
|
import { DataFrameJSON } from '@grafana/data';
|
||||||
import { LiveDataFilter } from '@grafana/runtime';
|
import { LiveDataFilter } from '@grafana/runtime';
|
||||||
|
import { DataQuery } from '@grafana/schema';
|
||||||
import { SearchQuery } from 'app/features/search/service';
|
import { SearchQuery } from 'app/features/search/service';
|
||||||
|
|
||||||
//----------------------------------------------
|
//----------------------------------------------
|
||||||
|
@ -35,7 +35,7 @@ export class EventEditorCtrl {
|
|||||||
|
|
||||||
canDelete(): boolean {
|
canDelete(): boolean {
|
||||||
if (contextSrv.accessControlEnabled()) {
|
if (contextSrv.accessControlEnabled()) {
|
||||||
if (this.event.source.type === 'dashboard') {
|
if (this.event.source?.type === 'dashboard') {
|
||||||
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.dashboard.canDelete;
|
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.dashboard.canDelete;
|
||||||
}
|
}
|
||||||
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.organization.canDelete;
|
return !!this.panelCtrl.dashboard.meta.annotationsPermissions?.organization.canDelete;
|
||||||
|
@ -3,6 +3,7 @@ import React, { HTMLAttributes, useCallback, useRef, useState } from 'react';
|
|||||||
import { usePopper } from 'react-popper';
|
import { usePopper } from 'react-popper';
|
||||||
|
|
||||||
import { GrafanaTheme2, dateTimeFormat, systemDateFormats, TimeZone } from '@grafana/data';
|
import { GrafanaTheme2, dateTimeFormat, systemDateFormats, TimeZone } from '@grafana/data';
|
||||||
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { Portal, useStyles2, usePanelContext } from '@grafana/ui';
|
import { Portal, useStyles2, usePanelContext } from '@grafana/ui';
|
||||||
import { getTooltipContainerStyles } from '@grafana/ui/src/themes/mixins';
|
import { getTooltipContainerStyles } from '@grafana/ui/src/themes/mixins';
|
||||||
|
|
||||||
@ -127,6 +128,7 @@ export function AnnotationMarker({ annotation, timeZone, width }: Props) {
|
|||||||
onMouseEnter={onMouseEnter}
|
onMouseEnter={onMouseEnter}
|
||||||
onMouseLeave={onMouseLeave}
|
onMouseLeave={onMouseLeave}
|
||||||
className={!isRegionAnnotation ? styles.markerWrapper : undefined}
|
className={!isRegionAnnotation ? styles.markerWrapper : undefined}
|
||||||
|
data-testId={selectors.pages.SoloPanel.Annotations.marker}
|
||||||
>
|
>
|
||||||
{marker}
|
{marker}
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user