Canvas: Fix image loading when icon element SVG defined by field mappings (#115748)
* chore(gdev-dashboard): minimal repro of escalation #19939 bug report * fix(canvas): add branching logic to handle field mapping to icons case * test(canvas): validate integration of canvas icon mappings * refactor(resource-dimension): defensive against JS `undefined` in paths
This commit is contained in:
+598
@@ -0,0 +1,598 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations \u0026 Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Mixed --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{
|
||||
"options": {
|
||||
"1": {
|
||||
"color": "green",
|
||||
"icon": "img/icons/unicons/check-circle.svg",
|
||||
"index": 0,
|
||||
"text": "Success"
|
||||
},
|
||||
"2": {
|
||||
"color": "orange",
|
||||
"icon": "img/icons/unicons/exclamation-triangle.svg",
|
||||
"index": 1,
|
||||
"text": "Warning"
|
||||
},
|
||||
"3": {
|
||||
"color": "red",
|
||||
"icon": "img/icons/unicons/times-circle.svg",
|
||||
"index": 2,
|
||||
"text": "Error"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "text"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "success"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"1": {
|
||||
"color": "green",
|
||||
"icon": "img/icons/unicons/check-circle.svg",
|
||||
"index": 0,
|
||||
"text": "Success"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "warning"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"2": {
|
||||
"color": "orange",
|
||||
"icon": "img/icons/unicons/exclamation-triangle.svg",
|
||||
"index": 1,
|
||||
"text": "Warning"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "error"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"3": {
|
||||
"color": "red",
|
||||
"icon": "img/icons/unicons/times-circle.svg",
|
||||
"index": 2,
|
||||
"text": "Error"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "unmapped"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"1": {
|
||||
"color": "green",
|
||||
"icon": "img/icons/unicons/check-circle.svg",
|
||||
"index": 0,
|
||||
"text": "Success"
|
||||
},
|
||||
"2": {
|
||||
"color": "orange",
|
||||
"icon": "img/icons/unicons/exclamation-triangle.svg",
|
||||
"index": 1,
|
||||
"text": "Warning"
|
||||
},
|
||||
"3": {
|
||||
"color": "red",
|
||||
"icon": "img/icons/unicons/times-circle.svg",
|
||||
"index": 2,
|
||||
"text": "Error"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"inlineEditing": true,
|
||||
"root": {
|
||||
"background": {
|
||||
"color": {
|
||||
"fixed": "transparent"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 16,
|
||||
"text": {
|
||||
"fixed": "Field-based Icons (from value mappings):",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Header",
|
||||
"placement": {
|
||||
"height": 40,
|
||||
"left": 20,
|
||||
"top": 10,
|
||||
"width": 400
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "success",
|
||||
"fixed": "green"
|
||||
},
|
||||
"path": {
|
||||
"field": "success",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Success Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 50,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "success",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"field": "success",
|
||||
"mode": "field"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Success Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 30,
|
||||
"top": 115,
|
||||
"width": 90
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "warning",
|
||||
"fixed": "orange"
|
||||
},
|
||||
"path": {
|
||||
"field": "warning",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Warning Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 180,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "warning",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"field": "warning",
|
||||
"mode": "field"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Warning Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 160,
|
||||
"top": 115,
|
||||
"width": 90
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "error",
|
||||
"fixed": "red"
|
||||
},
|
||||
"path": {
|
||||
"field": "error",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Error Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 310,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "error",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"field": "error",
|
||||
"mode": "field"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Error Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 290,
|
||||
"top": 115,
|
||||
"width": 90
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "unmapped",
|
||||
"fixed": "#808080"
|
||||
},
|
||||
"path": {
|
||||
"field": "unmapped",
|
||||
"fixed": "img/icons/unicons/question-circle.svg",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Unmapped Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 440,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "unmapped",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"fixed": "No mapping (14)",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Unmapped Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 410,
|
||||
"top": 115,
|
||||
"width": 110
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 14,
|
||||
"text": {
|
||||
"fixed": "Fixed Relative Path:",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Relative Label",
|
||||
"placement": {
|
||||
"height": 30,
|
||||
"left": 50,
|
||||
"top": 170,
|
||||
"width": 200
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"fixed": "blue"
|
||||
},
|
||||
"path": {
|
||||
"fixed": "img/icons/unicons/cloud.svg",
|
||||
"mode": "fixed"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Relative Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 260,
|
||||
"top": 165,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 14,
|
||||
"text": {
|
||||
"fixed": "Fixed Absolute URL:",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Absolute Label",
|
||||
"placement": {
|
||||
"height": 30,
|
||||
"left": 50,
|
||||
"top": 240,
|
||||
"width": 200
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"fixed": "purple"
|
||||
},
|
||||
"path": {
|
||||
"fixed": "https://grafana.com/static/assets/img/grafana_icon.svg",
|
||||
"mode": "fixed"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Absolute Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 260,
|
||||
"top": 235,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
}
|
||||
],
|
||||
"name": "Canvas Root",
|
||||
"placement": {
|
||||
"height": 100,
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": 100
|
||||
},
|
||||
"type": "frame"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "success\n1",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
},
|
||||
{
|
||||
"csvContent": "warning\n2",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_content"
|
||||
},
|
||||
{
|
||||
"csvContent": "error\n3",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "C",
|
||||
"scenarioId": "csv_content"
|
||||
},
|
||||
{
|
||||
"csvContent": "unmapped\n14",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "D",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Various SVG icons",
|
||||
"type": "canvas"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 42,
|
||||
"tags": [
|
||||
"canvas",
|
||||
"icons",
|
||||
"test",
|
||||
"v2"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Panel tests - Canvas - Kitchen sink",
|
||||
"uid": "canvas-icon-fix-test-v2",
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -0,0 +1,582 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "datasource",
|
||||
"uid": "-- Mixed --"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"mappings": [
|
||||
{
|
||||
"options": {
|
||||
"1": {
|
||||
"color": "green",
|
||||
"icon": "img/icons/unicons/check-circle.svg",
|
||||
"index": 0,
|
||||
"text": "Success"
|
||||
},
|
||||
"2": {
|
||||
"color": "orange",
|
||||
"icon": "img/icons/unicons/exclamation-triangle.svg",
|
||||
"index": 1,
|
||||
"text": "Warning"
|
||||
},
|
||||
"3": {
|
||||
"color": "red",
|
||||
"icon": "img/icons/unicons/times-circle.svg",
|
||||
"index": 2,
|
||||
"text": "Error"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "text",
|
||||
"value": null
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "success"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"1": {
|
||||
"color": "green",
|
||||
"icon": "img/icons/unicons/check-circle.svg",
|
||||
"index": 0,
|
||||
"text": "Success"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "warning"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"2": {
|
||||
"color": "orange",
|
||||
"icon": "img/icons/unicons/exclamation-triangle.svg",
|
||||
"index": 1,
|
||||
"text": "Warning"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "error"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"3": {
|
||||
"color": "red",
|
||||
"icon": "img/icons/unicons/times-circle.svg",
|
||||
"index": 2,
|
||||
"text": "Error"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": {
|
||||
"id": "byName",
|
||||
"options": "unmapped"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"id": "mappings",
|
||||
"value": [
|
||||
{
|
||||
"options": {
|
||||
"1": {
|
||||
"color": "green",
|
||||
"icon": "img/icons/unicons/check-circle.svg",
|
||||
"index": 0,
|
||||
"text": "Success"
|
||||
},
|
||||
"2": {
|
||||
"color": "orange",
|
||||
"icon": "img/icons/unicons/exclamation-triangle.svg",
|
||||
"index": 1,
|
||||
"text": "Warning"
|
||||
},
|
||||
"3": {
|
||||
"color": "red",
|
||||
"icon": "img/icons/unicons/times-circle.svg",
|
||||
"index": 2,
|
||||
"text": "Error"
|
||||
}
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"inlineEditing": true,
|
||||
"root": {
|
||||
"background": {
|
||||
"color": {
|
||||
"fixed": "transparent"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 16,
|
||||
"text": {
|
||||
"fixed": "Field-based Icons (from value mappings):",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Header",
|
||||
"placement": {
|
||||
"height": 40,
|
||||
"left": 20,
|
||||
"top": 10,
|
||||
"width": 400
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "success",
|
||||
"fixed": "green"
|
||||
},
|
||||
"path": {
|
||||
"field": "success",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Success Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 50,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "success",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"field": "success",
|
||||
"mode": "field"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Success Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 30,
|
||||
"top": 115,
|
||||
"width": 90
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "warning",
|
||||
"fixed": "orange"
|
||||
},
|
||||
"path": {
|
||||
"field": "warning",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Warning Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 180,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "warning",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"field": "warning",
|
||||
"mode": "field"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Warning Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 160,
|
||||
"top": 115,
|
||||
"width": 90
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "error",
|
||||
"fixed": "red"
|
||||
},
|
||||
"path": {
|
||||
"field": "error",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Error Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 310,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "error",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"field": "error",
|
||||
"mode": "field"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Error Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 290,
|
||||
"top": 115,
|
||||
"width": 90
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"field": "unmapped",
|
||||
"fixed": "#808080"
|
||||
},
|
||||
"path": {
|
||||
"field": "unmapped",
|
||||
"fixed": "img/icons/unicons/question-circle.svg",
|
||||
"mode": "field"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Unmapped Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 440,
|
||||
"top": 60,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"field": "unmapped",
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 12,
|
||||
"text": {
|
||||
"fixed": "No mapping (14)",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Unmapped Text",
|
||||
"placement": {
|
||||
"height": 25,
|
||||
"left": 410,
|
||||
"top": 115,
|
||||
"width": 110
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 14,
|
||||
"text": {
|
||||
"fixed": "Fixed Relative Path:",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Relative Label",
|
||||
"placement": {
|
||||
"height": 30,
|
||||
"left": 50,
|
||||
"top": 170,
|
||||
"width": 200
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"fixed": "blue"
|
||||
},
|
||||
"path": {
|
||||
"fixed": "img/icons/unicons/cloud.svg",
|
||||
"mode": "fixed"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Relative Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 260,
|
||||
"top": 165,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"align": "center",
|
||||
"color": {
|
||||
"fixed": "text"
|
||||
},
|
||||
"size": 14,
|
||||
"text": {
|
||||
"fixed": "Fixed Absolute URL:",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"valign": "middle"
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Absolute Label",
|
||||
"placement": {
|
||||
"height": 30,
|
||||
"left": 50,
|
||||
"top": 240,
|
||||
"width": 200
|
||||
},
|
||||
"type": "text"
|
||||
},
|
||||
{
|
||||
"config": {
|
||||
"fill": {
|
||||
"fixed": "purple"
|
||||
},
|
||||
"path": {
|
||||
"fixed": "https://grafana.com/static/assets/img/grafana_icon.svg",
|
||||
"mode": "fixed"
|
||||
}
|
||||
},
|
||||
"constraint": {
|
||||
"horizontal": "left",
|
||||
"vertical": "top"
|
||||
},
|
||||
"name": "Absolute Icon",
|
||||
"placement": {
|
||||
"height": 50,
|
||||
"left": 260,
|
||||
"top": 235,
|
||||
"width": 50
|
||||
},
|
||||
"type": "icon"
|
||||
}
|
||||
],
|
||||
"name": "Canvas Root",
|
||||
"placement": {
|
||||
"height": 100,
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
"width": 100
|
||||
},
|
||||
"type": "frame"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "12.1.0",
|
||||
"targets": [
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content",
|
||||
"csvContent": "success\n1"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "B",
|
||||
"scenarioId": "csv_content",
|
||||
"csvContent": "warning\n2"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "C",
|
||||
"scenarioId": "csv_content",
|
||||
"csvContent": "error\n3"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "D",
|
||||
"scenarioId": "csv_content",
|
||||
"csvContent": "unmapped\n14"
|
||||
}
|
||||
],
|
||||
"title": "Various SVG icons",
|
||||
"type": "canvas"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 39,
|
||||
"tags": ["canvas", "icons", "test", "v2"],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "browser",
|
||||
"title": "Panel tests - Canvas - Kitchen sink",
|
||||
"uid": "canvas-icon-fix-test-v2",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
"canvas-connection-examples": (import '../dev-dashboards/panel-canvas/canvas-connection-examples.json'),
|
||||
"canvas-datalinks": (import '../dev-dashboards/panel-canvas/canvas-datalinks.json'),
|
||||
"canvas-examples": (import '../dev-dashboards/panel-canvas/canvas-examples.json'),
|
||||
"canvas_kitchen_sink": (import '../dev-dashboards/panel-canvas/canvas_kitchen_sink.json'),
|
||||
"color_modes": (import '../dev-dashboards/panel-common/color_modes.json'),
|
||||
"config-from-query": (import '../dev-dashboards/transforms/config-from-query.json'),
|
||||
"dashlist": (import '../dev-dashboards/panel-dashlist/dashlist.json'),
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
import { test, expect } from '@grafana/plugin-e2e';
|
||||
|
||||
const DASHBOARD_UID = 'canvas-icon-fix-test-v2';
|
||||
const PANEL_TITLE = 'Various SVG icons';
|
||||
|
||||
test.describe('Canvas Panel - Icon Mappings', () => {
|
||||
test('should render field-based icons from value mappings correctly', async ({ gotoDashboardPage, page }) => {
|
||||
await test.step('Navigate to dashboard and wait for panel to load', async () => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
await page.waitForSelector('svg', { timeout: 10000 });
|
||||
});
|
||||
|
||||
await test.step('Verify value mapping text values are displayed', async () => {
|
||||
await expect(page.getByText('Success')).toBeVisible();
|
||||
await expect(page.getByText('Warning')).toBeVisible();
|
||||
await expect(page.getByText('Error')).toBeVisible();
|
||||
});
|
||||
|
||||
await test.step('Verify SVG icons rendered for mapped values', async () => {
|
||||
const svgCount = await page.locator('svg').count();
|
||||
expect(svgCount).toBeGreaterThanOrEqual(3);
|
||||
});
|
||||
});
|
||||
|
||||
test('should render fixed path icons correctly', async ({ gotoDashboardPage, page }) => {
|
||||
await test.step('Set up network interception for absolute URL icon', async () => {
|
||||
await page.route('https://grafana.com/static/assets/img/grafana_icon.svg', async (route) => {
|
||||
const dummySvg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<circle cx="12" cy="12" r="10" fill="purple"/>
|
||||
<text x="12" y="16" text-anchor="middle" fill="white" font-size="10">TEST</text>
|
||||
</svg>`;
|
||||
await route.fulfill({
|
||||
status: 200,
|
||||
contentType: 'image/svg+xml',
|
||||
body: dummySvg,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to dashboard and wait for SVGs to load', async () => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
await page.waitForSelector('svg:not([aria-hidden="true"])', { timeout: 10000 });
|
||||
await page.waitForLoadState('networkidle', { timeout: 10000 });
|
||||
});
|
||||
|
||||
await test.step('Verify at least 5 visible SVG icons are rendered (3 mapped + 2 fixed)', async () => {
|
||||
const visibleSvgs = page.locator('svg:not([aria-hidden="true"])');
|
||||
const svgCount = await visibleSvgs.count();
|
||||
expect(svgCount).toBeGreaterThanOrEqual(5);
|
||||
});
|
||||
|
||||
await test.step('Verify visible SVG icons have content', async () => {
|
||||
const visibleSvgs = page.locator('svg:not([aria-hidden="true"])');
|
||||
const count = await visibleSvgs.count();
|
||||
|
||||
for (let i = 0; i < Math.min(count, 5); i++) {
|
||||
const svg = visibleSvgs.nth(i);
|
||||
await expect(svg).toBeAttached();
|
||||
const svgContent = await svg.innerHTML();
|
||||
expect(svgContent.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('should not make invalid requests for unmapped numeric values', async ({ gotoDashboardPage, page }) => {
|
||||
const failedRequests: string[] = [];
|
||||
|
||||
await test.step('Set up network request monitoring', async () => {
|
||||
page.on('requestfailed', (request) => {
|
||||
const url = request.url();
|
||||
if (url.match(/\/build\/\d+$/)) {
|
||||
failedRequests.push(url);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
await test.step('Navigate to dashboard and wait for loading', async () => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
await page.waitForTimeout(2000);
|
||||
});
|
||||
|
||||
await test.step('Verify no invalid numeric path requests were made', async () => {
|
||||
expect(failedRequests).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
test('should display text values from value mappings correctly', async ({ gotoDashboardPage, page }) => {
|
||||
await test.step('Navigate to dashboard', async () => {
|
||||
await gotoDashboardPage({ uid: DASHBOARD_UID });
|
||||
});
|
||||
|
||||
await test.step('Verify mapped text values are displayed', async () => {
|
||||
await expect(page.getByText('Success')).toBeVisible();
|
||||
await expect(page.getByText('Warning')).toBeVisible();
|
||||
await expect(page.getByText('Error')).toBeVisible();
|
||||
await expect(page.getByText('No mapping (14)')).toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -103,6 +103,84 @@ describe('getResourceDimension', () => {
|
||||
expect(getResourceDimension(frame, config).value()).toEqual('');
|
||||
});
|
||||
|
||||
it('should handle numeric field values with icon from value mapping', () => {
|
||||
const publicPath = 'https://grafana.fake/public/';
|
||||
const frame = createDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'status_field',
|
||||
values: [1, 2, 3],
|
||||
display: (v) => ({
|
||||
text: v === 1 ? 'Success' : v === 2 ? 'Warning' : 'Error',
|
||||
numeric: Number(v),
|
||||
icon:
|
||||
v === 1
|
||||
? 'img/icons/unicons/check-circle.svg'
|
||||
: v === 2
|
||||
? 'img/icons/unicons/exclamation-triangle.svg'
|
||||
: 'img/icons/unicons/times-circle.svg',
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
const config = { mode: ResourceDimensionMode.Field, field: 'status_field', fixed: '' };
|
||||
|
||||
expect(getResourceDimension(frame, config).get(0)).toEqual(`${publicPath}build/img/icons/unicons/check-circle.svg`);
|
||||
expect(getResourceDimension(frame, config).get(1)).toEqual(
|
||||
`${publicPath}build/img/icons/unicons/exclamation-triangle.svg`
|
||||
);
|
||||
expect(getResourceDimension(frame, config).get(2)).toEqual(`${publicPath}build/img/icons/unicons/times-circle.svg`);
|
||||
});
|
||||
|
||||
it('should return empty string for unmapped numeric values without icon', () => {
|
||||
const frame = createDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'status_field',
|
||||
values: [14],
|
||||
display: (v) => ({
|
||||
text: String(v),
|
||||
numeric: Number(v),
|
||||
icon: undefined,
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
const config = { mode: ResourceDimensionMode.Field, field: 'status_field', fixed: '' };
|
||||
|
||||
expect(getResourceDimension(frame, config).get(0)).toEqual('');
|
||||
expect(getResourceDimension(frame, config).value()).toEqual('');
|
||||
});
|
||||
|
||||
it('should handle mixed numeric values with partial mappings', () => {
|
||||
const publicPath = 'https://grafana.fake/public/';
|
||||
const frame = createDataFrame({
|
||||
fields: [
|
||||
{
|
||||
name: 'status_field',
|
||||
values: [1, 99, 2],
|
||||
display: (v) => ({
|
||||
text: v === 1 ? 'Success' : v === 2 ? 'Warning' : String(v),
|
||||
numeric: Number(v),
|
||||
icon:
|
||||
v === 1
|
||||
? 'img/icons/unicons/check-circle.svg'
|
||||
: v === 2
|
||||
? 'img/icons/unicons/exclamation-triangle.svg'
|
||||
: undefined,
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
const config = { mode: ResourceDimensionMode.Field, field: 'status_field', fixed: '' };
|
||||
|
||||
expect(getResourceDimension(frame, config).get(0)).toEqual(`${publicPath}build/img/icons/unicons/check-circle.svg`);
|
||||
expect(getResourceDimension(frame, config).get(1)).toEqual('');
|
||||
expect(getResourceDimension(frame, config).get(2)).toEqual(
|
||||
`${publicPath}build/img/icons/unicons/exclamation-triangle.svg`
|
||||
);
|
||||
});
|
||||
|
||||
// TODO: write tests for mapping modes
|
||||
});
|
||||
|
||||
@@ -125,4 +203,25 @@ describe('getPublicOrAbsoluteUrl', () => {
|
||||
expect(getPublicOrAbsoluteUrl({ path: 'icon.png' })).toEqual('');
|
||||
expect(getPublicOrAbsoluteUrl(['icon.png'])).toEqual('');
|
||||
});
|
||||
|
||||
it('should handle undefined publicPath gracefully', () => {
|
||||
const originalPath = window.__grafana_public_path__;
|
||||
|
||||
// @ts-ignore - Intentionally testing runtime edge case
|
||||
window.__grafana_public_path__ = undefined;
|
||||
|
||||
expect(getPublicOrAbsoluteUrl('icon.png')).toEqual('/build/icon.png');
|
||||
|
||||
window.__grafana_public_path__ = originalPath;
|
||||
});
|
||||
|
||||
it('should handle empty string publicPath gracefully', () => {
|
||||
const originalPath = window.__grafana_public_path__;
|
||||
|
||||
window.__grafana_public_path__ = '';
|
||||
|
||||
expect(getPublicOrAbsoluteUrl('icon.png')).toEqual('/build/icon.png');
|
||||
|
||||
window.__grafana_public_path__ = originalPath;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,8 +15,9 @@ export function getPublicOrAbsoluteUrl(path: unknown): string {
|
||||
// NOTE: The value of `path` could be either an URL string or a relative
|
||||
// path to a Grafana CDN asset served from the CDN.
|
||||
const isUrl = path.indexOf(':/') > 0;
|
||||
const publicPath = window.__grafana_public_path__ || '/';
|
||||
|
||||
return isUrl ? path : `${window.__grafana_public_path__}build/${path}`;
|
||||
return isUrl ? path : `${publicPath}build/${path}`;
|
||||
}
|
||||
|
||||
export function getResourceDimension(
|
||||
@@ -56,11 +57,11 @@ export function getResourceDimension(
|
||||
|
||||
// mode === ResourceDimensionMode.Field case
|
||||
const getImageOrIcon = (value: unknown): string => {
|
||||
if (typeof value !== 'string') {
|
||||
if (typeof value !== 'string' && typeof value !== 'number') {
|
||||
return '';
|
||||
}
|
||||
|
||||
let url = value;
|
||||
let url = typeof value === 'string' ? value : '';
|
||||
if (field && field.display) {
|
||||
const displayValue = field.display(value);
|
||||
if (displayValue.icon) {
|
||||
@@ -68,6 +69,11 @@ export function getResourceDimension(
|
||||
}
|
||||
}
|
||||
|
||||
const noIconFound = !url;
|
||||
if (noIconFound) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return getPublicOrAbsoluteUrl(url);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user