mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
XYChart: Improved new tooltip (#75818)
Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
parent
a03eca29eb
commit
935ecdd809
@ -6563,16 +6563,11 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/plugins/panel/xychart/TooltipView.tsx:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "2"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "3"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "4"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||
],
|
||||
"public/app/plugins/panel/xychart/XYChartPanel2.tsx:5381": [
|
||||
"public/app/plugins/panel/xychart/XYChartPanel.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"],
|
||||
[0, 0, 0, "Styles should be written using objects.", "2"]
|
||||
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||
],
|
||||
"public/app/plugins/panel/xychart/dims.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
|
@ -0,0 +1,687 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": {
|
||||
"type": "grafana",
|
||||
"uid": "-- Grafana --"
|
||||
},
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 0,
|
||||
"links": [],
|
||||
"liveNow": false,
|
||||
"panels": [
|
||||
{
|
||||
"datasource": {},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"fixedColor": "blue",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 32
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"fieldMinMax": false,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 7,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 1,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "orange"
|
||||
},
|
||||
"x": "Miles_per_Gallon",
|
||||
"y": "Horsepower"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "auto",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "${DS_GDEV-TESTDATA}"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Standard options: Single color",
|
||||
"type": "xychart"
|
||||
},
|
||||
{
|
||||
"datasource": {},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"fixedColor": "green",
|
||||
"mode": "shades"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 32
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"fieldMinMax": false,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 7,
|
||||
"x": 7,
|
||||
"y": 0
|
||||
},
|
||||
"id": 4,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "orange"
|
||||
},
|
||||
"x": "Miles_per_Gallon",
|
||||
"y": "Horsepower"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "auto",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "${DS_GDEV-TESTDATA}"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Standard options: Shades of a color",
|
||||
"type": "xychart"
|
||||
},
|
||||
{
|
||||
"datasource": {},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"fixedColor": "yellow",
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 32
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"fieldMinMax": false,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 11,
|
||||
"w": 7,
|
||||
"x": 14,
|
||||
"y": 0
|
||||
},
|
||||
"id": 3,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "orange"
|
||||
},
|
||||
"x": "Miles_per_Gallon",
|
||||
"y": "Horsepower"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "auto",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "${DS_GDEV-TESTDATA}"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Standard options: Classic palette",
|
||||
"type": "xychart"
|
||||
},
|
||||
{
|
||||
"datasource": {},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"fixedColor": "blue",
|
||||
"mode": "fixed"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 32
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"fieldMinMax": false,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 7,
|
||||
"x": 0,
|
||||
"y": 11
|
||||
},
|
||||
"id": 5,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "orange"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "y"
|
||||
},
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "green"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "z"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "manual",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "${DS_GDEV-TESTDATA}"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Standard options: Single color + Mapping: Fixed color",
|
||||
"type": "xychart"
|
||||
},
|
||||
{
|
||||
"datasource": {},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"fixedColor": "blue",
|
||||
"mode": "shades"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 32
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"fieldMinMax": false,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 7,
|
||||
"x": 7,
|
||||
"y": 11
|
||||
},
|
||||
"id": 6,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "orange"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "y"
|
||||
},
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "green"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "z"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "manual",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "${DS_GDEV-TESTDATA}"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Standard options: Shades of color + Mapping: Fixed color",
|
||||
"type": "xychart"
|
||||
},
|
||||
{
|
||||
"datasource": {},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"fixedColor": "blue",
|
||||
"mode": "palette-classic"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 32
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"fieldMinMax": false,
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 12,
|
||||
"w": 7,
|
||||
"x": 14,
|
||||
"y": 11
|
||||
},
|
||||
"id": 7,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "orange"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "y"
|
||||
},
|
||||
{
|
||||
"pointColor": {
|
||||
"fixed": "green"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "z"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "manual",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
|
||||
"datasource": {
|
||||
"type": "grafana-testdata-datasource",
|
||||
"uid": "${DS_GDEV-TESTDATA}"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Standard options: Classic palette + Mapping: Fixed color",
|
||||
"type": "xychart"
|
||||
},
|
||||
{
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": {
|
||||
"mode": "continuous-BlYlRd"
|
||||
},
|
||||
"custom": {
|
||||
"axisBorderShow": false,
|
||||
"axisCenteredZero": false,
|
||||
"axisColorMode": "text",
|
||||
"axisLabel": "",
|
||||
"axisPlacement": "auto",
|
||||
"hideFrom": {
|
||||
"legend": false,
|
||||
"tooltip": false,
|
||||
"viz": false
|
||||
},
|
||||
"pointSize": {
|
||||
"fixed": 50
|
||||
},
|
||||
"scaleDistribution": {
|
||||
"type": "linear"
|
||||
},
|
||||
"show": "points"
|
||||
},
|
||||
"mappings": [],
|
||||
"thresholds": {
|
||||
"mode": "absolute",
|
||||
"steps": [
|
||||
{
|
||||
"color": "green",
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"value": 80
|
||||
}
|
||||
]
|
||||
},
|
||||
"unitScale": true
|
||||
},
|
||||
"overrides": []
|
||||
},
|
||||
"gridPos": {
|
||||
"h": 9,
|
||||
"w": 7,
|
||||
"x": 0,
|
||||
"y": 23
|
||||
},
|
||||
"id": 8,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": [],
|
||||
"displayMode": "list",
|
||||
"placement": "bottom",
|
||||
"showLegend": true
|
||||
},
|
||||
"series": [
|
||||
{
|
||||
"pointColor": {
|
||||
"field": "c",
|
||||
"fixed": "dark-green"
|
||||
},
|
||||
"x": "x",
|
||||
"y": "y"
|
||||
}
|
||||
],
|
||||
"seriesMapping": "manual",
|
||||
"tooltip": {
|
||||
"mode": "single",
|
||||
"sort": "none"
|
||||
}
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"csvContent": "x,y,c\n0,0,0\n50,50,25\n100,100,50",
|
||||
"datasource": {
|
||||
"type": "testdata",
|
||||
"uid": "PD8C576611E62080A"
|
||||
},
|
||||
"refId": "A",
|
||||
"scenarioId": "csv_content"
|
||||
}
|
||||
],
|
||||
"title": "Panel Title",
|
||||
"type": "xychart"
|
||||
}
|
||||
],
|
||||
"refresh": "",
|
||||
"schemaVersion": 39,
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {},
|
||||
"timezone": "",
|
||||
"title": "XYChart tooltip color test",
|
||||
"uid": "cb67db43-dd72-4ada-a313-53f46c20adcc",
|
||||
"version": 1,
|
||||
"weekStart": ""
|
||||
}
|
@ -124,5 +124,6 @@
|
||||
"timeseries-yaxis-ticks": (import '../dev-dashboards/panel-timeseries/timeseries-yaxis-ticks.json'),
|
||||
"trend_example": (import '../dev-dashboards/panel-trend/trend_example.json'),
|
||||
"xychart-example": (import '../dev-dashboards/panel-xychart/xychart-example.json'),
|
||||
"xychart-tooltip-color-test": (import '../dev-dashboards/panel-xychart/xychart-tooltip-color-test.json'),
|
||||
},
|
||||
}
|
||||
|
@ -163,22 +163,22 @@ function fmt(field: Field, val: number): string {
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
infoWrap: css`
|
||||
padding: 8px;
|
||||
width: 100%;
|
||||
th {
|
||||
font-weight: ${theme.typography.fontWeightMedium};
|
||||
padding: ${theme.spacing(0.25, 2)};
|
||||
}
|
||||
`,
|
||||
highlight: css`
|
||||
background: ${theme.colors.action.hover};
|
||||
`,
|
||||
xVal: css`
|
||||
font-weight: ${theme.typography.fontWeightBold};
|
||||
`,
|
||||
icon: css`
|
||||
margin-right: ${theme.spacing(1)};
|
||||
vertical-align: middle;
|
||||
`,
|
||||
infoWrap: css({
|
||||
padding: '8px',
|
||||
width: '100%',
|
||||
th: {
|
||||
fontWeight: theme.typography.fontWeightMedium,
|
||||
padding: theme.spacing(0.25, 2),
|
||||
},
|
||||
}),
|
||||
highlight: css({
|
||||
background: theme.colors.action.hover,
|
||||
}),
|
||||
xVal: css({
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
}),
|
||||
icon: css({
|
||||
marginRight: theme.spacing(1),
|
||||
verticalAlign: 'middle',
|
||||
}),
|
||||
});
|
||||
|
@ -16,6 +16,7 @@ import { config } from '@grafana/runtime';
|
||||
import {
|
||||
Portal,
|
||||
TooltipDisplayMode,
|
||||
TooltipPlugin2,
|
||||
UPlotChart,
|
||||
UPlotConfigBuilder,
|
||||
VizLayout,
|
||||
@ -23,10 +24,12 @@ import {
|
||||
VizLegendItem,
|
||||
VizTooltipContainer,
|
||||
} from '@grafana/ui';
|
||||
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
|
||||
import { FacetedData } from '@grafana/ui/src/components/uPlot/types';
|
||||
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||
|
||||
import { TooltipView } from './TooltipView';
|
||||
import { XYChartTooltip } from './XYChartTooltip';
|
||||
import { Options, SeriesMapping } from './panelcfg.gen';
|
||||
import { prepData, prepScatter, ScatterPanelInfo } from './scatter';
|
||||
import { ScatterHoverEvent, ScatterSeries } from './types';
|
||||
@ -34,13 +37,14 @@ import { ScatterHoverEvent, ScatterSeries } from './types';
|
||||
type Props = PanelProps<Options>;
|
||||
const TOOLTIP_OFFSET = 10;
|
||||
|
||||
export const XYChartPanel2 = (props: Props) => {
|
||||
export const XYChartPanel = (props: Props) => {
|
||||
const [error, setError] = useState<string | undefined>();
|
||||
const [series, setSeries] = useState<ScatterSeries[]>([]);
|
||||
const [builder, setBuilder] = useState<UPlotConfigBuilder | undefined>();
|
||||
const [facets, setFacets] = useState<FacetedData | undefined>();
|
||||
const [hover, setHover] = useState<ScatterHoverEvent | undefined>();
|
||||
const [shouldDisplayCloseButton, setShouldDisplayCloseButton] = useState<boolean>(false);
|
||||
const showNewVizTooltips = Boolean(config.featureToggles.newVizTooltips);
|
||||
|
||||
const isToolTipOpen = useRef<boolean>(false);
|
||||
const oldOptions = usePrevious(props.options);
|
||||
@ -69,9 +73,9 @@ export const XYChartPanel2 = (props: Props) => {
|
||||
props.options,
|
||||
getData,
|
||||
config.theme2,
|
||||
scatterHoverCallback,
|
||||
onUPlotClick,
|
||||
isToolTipOpen
|
||||
showNewVizTooltips ? null : scatterHoverCallback,
|
||||
showNewVizTooltips ? null : onUPlotClick,
|
||||
showNewVizTooltips ? null : isToolTipOpen
|
||||
);
|
||||
|
||||
if (info.error) {
|
||||
@ -82,7 +86,7 @@ export const XYChartPanel2 = (props: Props) => {
|
||||
setFacets(() => prepData(info, props.data.series));
|
||||
setError(undefined);
|
||||
}
|
||||
}, [props.data.series, props.options]);
|
||||
}, [props.data.series, props.options, showNewVizTooltips]);
|
||||
|
||||
const initFacets = useCallback(() => {
|
||||
setFacets(() => prepData({ error, series }, props.data.series));
|
||||
@ -187,11 +191,11 @@ export const XYChartPanel2 = (props: Props) => {
|
||||
}
|
||||
|
||||
const legendStyle = {
|
||||
flexStart: css`
|
||||
div {
|
||||
justify-content: flex-start !important;
|
||||
}
|
||||
`,
|
||||
flexStart: css({
|
||||
div: {
|
||||
justifyContent: 'flex-start',
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
return (
|
||||
@ -218,47 +222,69 @@ export const XYChartPanel2 = (props: Props) => {
|
||||
<>
|
||||
<VizLayout width={props.width} height={props.height} legend={renderLegend()}>
|
||||
{(vizWidth: number, vizHeight: number) => (
|
||||
<UPlotChart config={builder} data={facets} width={vizWidth} height={vizHeight} />
|
||||
<UPlotChart config={builder} data={facets} width={vizWidth} height={vizHeight}>
|
||||
{config.featureToggles.newVizTooltips && props.options.tooltip.mode !== TooltipDisplayMode.None && (
|
||||
<TooltipPlugin2
|
||||
config={builder}
|
||||
hoverMode={TooltipHoverMode.xyOne}
|
||||
render={(u, dataIdxs, seriesIdx, isPinned, dismiss) => {
|
||||
return (
|
||||
<XYChartTooltip
|
||||
data={props.data.series}
|
||||
dataIdxs={dataIdxs}
|
||||
allSeries={series}
|
||||
dismiss={dismiss}
|
||||
isPinned={isPinned}
|
||||
options={props.options}
|
||||
seriesIdx={seriesIdx}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</UPlotChart>
|
||||
)}
|
||||
</VizLayout>
|
||||
<Portal>
|
||||
{hover && props.options.tooltip.mode !== TooltipDisplayMode.None && (
|
||||
<VizTooltipContainer
|
||||
position={{ x: hover.pageX, y: hover.pageY }}
|
||||
offset={{ x: TOOLTIP_OFFSET, y: TOOLTIP_OFFSET }}
|
||||
allowPointerEvents={isToolTipOpen.current}
|
||||
>
|
||||
{shouldDisplayCloseButton && (
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<CloseButton
|
||||
onClick={onCloseToolTip}
|
||||
{!config.featureToggles.newVizTooltips && (
|
||||
<Portal>
|
||||
{hover && props.options.tooltip.mode !== TooltipDisplayMode.None && (
|
||||
<VizTooltipContainer
|
||||
position={{ x: hover.pageX, y: hover.pageY }}
|
||||
offset={{ x: TOOLTIP_OFFSET, y: TOOLTIP_OFFSET }}
|
||||
allowPointerEvents={isToolTipOpen.current}
|
||||
>
|
||||
{shouldDisplayCloseButton && (
|
||||
<div
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: 'auto',
|
||||
right: 'auto',
|
||||
marginRight: 0,
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<TooltipView
|
||||
options={props.options.tooltip}
|
||||
allSeries={series}
|
||||
manualSeriesConfigs={props.options.series}
|
||||
seriesMapping={props.options.seriesMapping!}
|
||||
rowIndex={hover.xIndex}
|
||||
hoveredPointIndex={hover.scatterIndex}
|
||||
data={props.data.series}
|
||||
/>
|
||||
</VizTooltipContainer>
|
||||
)}
|
||||
</Portal>
|
||||
>
|
||||
<CloseButton
|
||||
onClick={onCloseToolTip}
|
||||
style={{
|
||||
position: 'relative',
|
||||
top: 'auto',
|
||||
right: 'auto',
|
||||
marginRight: 0,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<TooltipView
|
||||
options={props.options.tooltip}
|
||||
allSeries={series}
|
||||
manualSeriesConfigs={props.options.series}
|
||||
seriesMapping={props.options.seriesMapping!}
|
||||
rowIndex={hover.xIndex}
|
||||
hoveredPointIndex={hover.scatterIndex}
|
||||
data={props.data.series}
|
||||
/>
|
||||
</VizTooltipContainer>
|
||||
)}
|
||||
</Portal>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
136
public/app/plugins/panel/xychart/XYChartTooltip.tsx
Normal file
136
public/app/plugins/panel/xychart/XYChartTooltip.tsx
Normal file
@ -0,0 +1,136 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { DataFrame, Field, getFieldDisplayName, GrafanaTheme2, LinkModel } from '@grafana/data';
|
||||
import { alpha } from '@grafana/data/src/themes/colorManipulator';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
|
||||
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
|
||||
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
|
||||
import { ColorIndicator, LabelValue } from '@grafana/ui/src/components/VizTooltip/types';
|
||||
import { getTitleFromHref } from 'app/features/explore/utils/links';
|
||||
|
||||
import { Options } from './panelcfg.gen';
|
||||
import { ScatterSeries, YValue } from './types';
|
||||
import { fmt } from './utils';
|
||||
|
||||
export interface Props {
|
||||
dataIdxs: Array<number | null>;
|
||||
seriesIdx: number | null | undefined;
|
||||
isPinned: boolean;
|
||||
dismiss: () => void;
|
||||
options: Options;
|
||||
data: DataFrame[]; // source data
|
||||
allSeries: ScatterSeries[];
|
||||
}
|
||||
|
||||
export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, allSeries, dismiss, options, isPinned }: Props) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const rowIndex = dataIdxs.find((idx) => idx !== null);
|
||||
// @todo: remove -1 when uPlot v2 arrive
|
||||
// context: first value in dataIdxs always null and represent X series
|
||||
const hoveredPointIndex = seriesIdx! - 1;
|
||||
|
||||
if (!allSeries || rowIndex == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const series = allSeries[hoveredPointIndex];
|
||||
const frame = series.frame(data);
|
||||
const xField = series.x(frame);
|
||||
const yField = series.y(frame);
|
||||
|
||||
const getHeaderLabel = (): LabelValue => {
|
||||
let label = series.name;
|
||||
if (options.seriesMapping === 'manual') {
|
||||
label = options.series?.[hoveredPointIndex]?.name ?? `Series ${hoveredPointIndex + 1}`;
|
||||
}
|
||||
|
||||
let colorThing = series.pointColor(frame);
|
||||
|
||||
if (Array.isArray(colorThing)) {
|
||||
colorThing = colorThing[rowIndex];
|
||||
}
|
||||
|
||||
return {
|
||||
label,
|
||||
value: null,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
color: alpha(colorThing as string, 0.5),
|
||||
colorIndicator: ColorIndicator.marker_md,
|
||||
};
|
||||
};
|
||||
|
||||
const getContentLabel = (): LabelValue[] => {
|
||||
let colorThing = series.pointColor(frame);
|
||||
|
||||
if (Array.isArray(colorThing)) {
|
||||
colorThing = colorThing[rowIndex];
|
||||
}
|
||||
|
||||
const yValue: YValue = {
|
||||
name: getFieldDisplayName(yField, frame),
|
||||
val: yField.values[rowIndex],
|
||||
field: yField,
|
||||
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
||||
color: alpha(colorThing as string, 0.5),
|
||||
};
|
||||
|
||||
const content: LabelValue[] = [
|
||||
{
|
||||
label: getFieldDisplayName(xField, frame),
|
||||
value: fmt(xField, xField.values[rowIndex]),
|
||||
},
|
||||
{
|
||||
label: yValue.name,
|
||||
value: fmt(yValue.field, yValue.val),
|
||||
},
|
||||
];
|
||||
|
||||
// add extra fields
|
||||
const extraFields: Field[] = frame.fields.filter((f) => f !== xField && f !== yField);
|
||||
if (extraFields) {
|
||||
extraFields.forEach((field) => {
|
||||
content.push({
|
||||
label: field.name,
|
||||
value: fmt(field, field.values[rowIndex]),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return content;
|
||||
};
|
||||
|
||||
const getLinks = (): Array<LinkModel<Field>> => {
|
||||
let links: Array<LinkModel<Field>> = [];
|
||||
if (yField.getLinks) {
|
||||
const v = yField.values[rowIndex];
|
||||
const disp = yField.display ? yField.display(v) : { text: `${v}`, numeric: +v };
|
||||
links = yField.getLinks({ calculatedValue: disp, valueRowIndex: rowIndex }).map((linkModel) => {
|
||||
if (!linkModel.title) {
|
||||
linkModel.title = getTitleFromHref(linkModel.href);
|
||||
}
|
||||
|
||||
return linkModel;
|
||||
});
|
||||
}
|
||||
return links;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<VizTooltipHeader headerLabel={getHeaderLabel()} />
|
||||
<VizTooltipContent contentLabelValue={getContentLabel()} />
|
||||
{isPinned && <VizTooltipFooter dataLinks={getLinks()} canAnnotate={false} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
wrapper: css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
width: '280px',
|
||||
}),
|
||||
});
|
@ -3,11 +3,11 @@ import { commonOptionsBuilder } from '@grafana/ui';
|
||||
|
||||
import { AutoEditor } from './AutoEditor';
|
||||
import { ManualEditor } from './ManualEditor';
|
||||
import { XYChartPanel2 } from './XYChartPanel2';
|
||||
import { XYChartPanel } from './XYChartPanel';
|
||||
import { getScatterFieldConfig } from './config';
|
||||
import { Options, FieldConfig, defaultFieldConfig } from './panelcfg.gen';
|
||||
|
||||
export const plugin = new PanelPlugin<Options, FieldConfig>(XYChartPanel2)
|
||||
export const plugin = new PanelPlugin<Options, FieldConfig>(XYChartPanel)
|
||||
.useFieldConfig(getScatterFieldConfig(defaultFieldConfig))
|
||||
.setPanelOptions((builder) => {
|
||||
builder
|
||||
@ -38,6 +38,6 @@ export const plugin = new PanelPlugin<Options, FieldConfig>(XYChartPanel2)
|
||||
showIf: (cfg) => cfg.seriesMapping === 'manual',
|
||||
});
|
||||
|
||||
commonOptionsBuilder.addTooltipOptions(builder);
|
||||
commonOptionsBuilder.addTooltipOptions(builder, true);
|
||||
commonOptionsBuilder.addLegendOptions(builder);
|
||||
});
|
||||
|
@ -46,9 +46,9 @@ export function prepScatter(
|
||||
options: Options,
|
||||
getData: () => DataFrame[],
|
||||
theme: GrafanaTheme2,
|
||||
ttip: ScatterHoverCallback,
|
||||
ttip: null | ScatterHoverCallback,
|
||||
onUPlotClick: null | ((evt?: Object) => void),
|
||||
isToolTipOpen: MutableRefObject<boolean>
|
||||
isToolTipOpen: null | MutableRefObject<boolean>
|
||||
): ScatterPanelInfo {
|
||||
let series: ScatterSeries[];
|
||||
let builder: UPlotConfigBuilder;
|
||||
@ -294,14 +294,13 @@ interface DrawBubblesOpts {
|
||||
};
|
||||
}
|
||||
|
||||
//const prepConfig: UPlotConfigPrepFnXY<Options> = ({ frames, series, theme }) => {
|
||||
const prepConfig = (
|
||||
getData: () => DataFrame[],
|
||||
scatterSeries: ScatterSeries[],
|
||||
theme: GrafanaTheme2,
|
||||
ttip: ScatterHoverCallback,
|
||||
ttip: null | ScatterHoverCallback,
|
||||
onUPlotClick: null | ((evt?: Object) => void),
|
||||
isToolTipOpen: MutableRefObject<boolean>
|
||||
isToolTipOpen: null | MutableRefObject<boolean>
|
||||
) => {
|
||||
let qt: Quadtree;
|
||||
let hRect: Rect | null;
|
||||
@ -522,8 +521,10 @@ const prepConfig = (
|
||||
});
|
||||
|
||||
const clearPopupIfOpened = () => {
|
||||
if (isToolTipOpen.current) {
|
||||
ttip(undefined);
|
||||
if (isToolTipOpen?.current) {
|
||||
if (ttip) {
|
||||
ttip(undefined);
|
||||
}
|
||||
if (onUPlotClick) {
|
||||
onUPlotClick();
|
||||
}
|
||||
@ -534,7 +535,9 @@ const prepConfig = (
|
||||
|
||||
// clip hover points/bubbles to plotting area
|
||||
builder.addHook('init', (u, r) => {
|
||||
u.over.style.overflow = 'hidden';
|
||||
if (!config.featureToggles.newVizTooltips) {
|
||||
u.over.style.overflow = 'hidden';
|
||||
}
|
||||
ref_parent = u.root.parentElement;
|
||||
|
||||
if (onUPlotClick) {
|
||||
@ -556,26 +559,28 @@ const prepConfig = (
|
||||
rect = r;
|
||||
});
|
||||
|
||||
builder.addHook('setLegend', (u) => {
|
||||
if (u.cursor.idxs != null) {
|
||||
for (let i = 0; i < u.cursor.idxs.length; i++) {
|
||||
const sel = u.cursor.idxs[i];
|
||||
if (sel != null && !isToolTipOpen.current) {
|
||||
ttip({
|
||||
scatterIndex: i - 1,
|
||||
xIndex: sel,
|
||||
pageX: rect.left + u.cursor.left!,
|
||||
pageY: rect.top + u.cursor.top!,
|
||||
});
|
||||
return; // only show the first one
|
||||
if (ttip) {
|
||||
builder.addHook('setLegend', (u) => {
|
||||
if (u.cursor.idxs != null) {
|
||||
for (let i = 0; i < u.cursor.idxs.length; i++) {
|
||||
const sel = u.cursor.idxs[i];
|
||||
if (sel != null && !isToolTipOpen?.current) {
|
||||
ttip({
|
||||
scatterIndex: i - 1,
|
||||
xIndex: sel,
|
||||
pageX: rect.left + u.cursor.left!,
|
||||
pageY: rect.top + u.cursor.top!,
|
||||
});
|
||||
return; // only show the first one
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isToolTipOpen.current) {
|
||||
ttip(undefined);
|
||||
}
|
||||
});
|
||||
if (!isToolTipOpen?.current) {
|
||||
ttip(undefined);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
builder.addHook('drawClear', (u) => {
|
||||
clearPopupIfOpened();
|
||||
@ -598,8 +603,8 @@ const prepConfig = (
|
||||
const frames = getData();
|
||||
let xField = scatterSeries[0].x(scatterSeries[0].frame(frames));
|
||||
|
||||
let config = xField.config;
|
||||
let customConfig = config.custom;
|
||||
let fieldConfig = xField.config;
|
||||
let customConfig = fieldConfig.custom;
|
||||
let scaleDistr = customConfig?.scaleDistribution;
|
||||
|
||||
builder.addScale({
|
||||
@ -610,12 +615,12 @@ const prepConfig = (
|
||||
distribution: scaleDistr?.type,
|
||||
log: scaleDistr?.log,
|
||||
linearThreshold: scaleDistr?.linearThreshold,
|
||||
min: config.min,
|
||||
max: config.max,
|
||||
min: fieldConfig.min,
|
||||
max: fieldConfig.max,
|
||||
softMin: customConfig?.axisSoftMin,
|
||||
softMax: customConfig?.axisSoftMax,
|
||||
centeredZero: customConfig?.axisCenteredZero,
|
||||
decimals: config.decimals,
|
||||
decimals: fieldConfig.decimals,
|
||||
});
|
||||
|
||||
// why does this fall back to '' instead of null or undef?
|
||||
|
@ -50,3 +50,17 @@ export interface ScatterSeries {
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface YValue {
|
||||
name: string;
|
||||
val: number;
|
||||
field: Field;
|
||||
color: string;
|
||||
}
|
||||
|
||||
export interface ExtraFacets {
|
||||
colorFacetFieldName: string;
|
||||
sizeFacetFieldName: string;
|
||||
colorFacetValue: number;
|
||||
sizeFacetValue: number;
|
||||
}
|
||||
|
9
public/app/plugins/panel/xychart/utils.ts
Normal file
9
public/app/plugins/panel/xychart/utils.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Field, formattedValueToString } from '@grafana/data';
|
||||
|
||||
export function fmt(field: Field, val: number): string {
|
||||
if (field.display) {
|
||||
return formattedValueToString(field.display(val));
|
||||
}
|
||||
|
||||
return `${val}`;
|
||||
}
|
Loading…
Reference in New Issue
Block a user