mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GraphNG - shared cursor (#33433)
* Initial work * WIP add cursor in debug panel * shared cursor.sync filter * explicit uplot events * explicit uplot events * uplot events * uplot events * depend on master uplot * sync sync sync * Fix merge * Get rid of PlotSyncContext and sync tooltip positions * make sync optional * Improve shared tooltip positioning * Plugins: add level and signature badges to plugin details page (#33553) * feat(grafana-ui): badge can accept react node for text, add shield-exclamation to icons * feat(plugins): add PluginSignatureType type * feat(pluginpage): introduce PluginSignatureDetailsBadge. Fix sidebar icon margin * feat(pluginlistpage): update filterinput placeholder, introduce filter by plugin type * Variables: Removes the never refresh option (#33533) * Variables: Removes the never refresh option * Tests: fixes DashboardModel repeat tests * Tests: fixs snapshots * Tests: fixes processVariable test * Tests: fixes DashboardModel tests * PageLayout: Fixes max-width breakpoint so that it triggers only when there is room for margin+ (#33558) * Alerting: Remove datasource (name) from migration (#33544) no longer needed as of https://github.com/grafana/grafana/pull/33416 for https://github.com/grafana/alerting-squad/issues/126 * Library panels: Adds description to library panels tab (#33428) * CodeOwners: Set owners of unified alerting migration (#33571) * ButtonSelect: updates component with the new theme model (#33565) * EmptySearchResult: updates component with the new theme model (#33573) * DashboardSettings: Slight design tweak to fix page toolbar padding and align design (#33575) * DashboardSettings: Slight design tweak to fix page toolbar padding and align design * Fixed font weight * Removed comment * Update * gitignore: Ignore files for accesscontrol provisioning (#33577) * Alerting/metrics (#33547) * moves alerting metrics to their own pkg * adds grafana_alerting_alerts (by state) metric * alerts_received_{total,invalid} * embed alertmanager alerting struct in ng metrics & remove duplicated notification metrics (already embed alertmanager notifier metrics) * use silence metrics from alertmanager lib * fix - manager has metrics * updates ngalert tests * comment lint Signed-off-by: Owen Diehl <ow.diehl@gmail.com> * cleaner prom registry code * removes ngalert global metrics * new registry use in all tests * ngalert metrics impl service, hack testinfra code to prevent duplicate metric registrations * nilmetrics unexported * Add note to Snapshot API doc to specify that user has to provide the entire dashboard model (#33572) * Added note as suggested by Macus E. * Update docs/sources/http_api/snapshot.md Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> * Alerting: backend "ng" code cleanup (#33578) * AlertMigration: remove alert_rule UID db check (#33568) do not believe this is needed due to uniqueness promised by shortid lib since there is no provisioning yet. https://github.com/teris-io/shortid * Live: persisting last message in cache for broadcast scope (#32938) * Alerting: Load annotations from rule into State cache (#33542) for https://github.com/grafana/alerting-squad/issues/127 * add template for dashboard url parameters (#33549) * Update dashboard-links.md parameters with plain text like `var-something=value` can make confusion. template it to clarify . * describe way for template link. * AlertingMigration: Create alert_rule_version entry (#33585) Create the alert rule version entry during the migration so it is consistent with rules created via api. for https://github.com/grafana/alerting-squad/issues/123 * Build: Fix with cleanup call maybe? (#33590) * add selector for code editor (#33554) * broadcast over eventBus * broadcasting to eventbus (but not useing it yet) * merge master * moved to context * fix yarn.lock * update snapshot * Fix direct state mutation * Persist location state on partial updates * GraphNG- use getStream rather than subscribe * Sync LegacyGraphHoverEvent with GraphNG * Chenge plotRef signature * use subscription * subscription * one fewer file * Update types * Remove unnecessary filtering * Disable cursor sync when in edit mode * GraphNG - bring back logging Co-authored-by: Ryan McKinley <ryantxu@gmail.com> Co-authored-by: Leon Sorokin <leeoniya@gmail.com> Co-authored-by: Jack Westbrook <jack.westbrook@gmail.com> Co-authored-by: Hugo Häggmark <hugo.haggmark@grafana.com> Co-authored-by: Torkel Ödegaard <torkel@grafana.org> Co-authored-by: Kyle Brandt <kyle@grafana.com> Co-authored-by: kay delaney <45561153+kaydelaney@users.noreply.github.com> Co-authored-by: Uchechukwu Obasi <obasiuche62@gmail.com> Co-authored-by: gamab <gamab@users.noreply.github.com> Co-authored-by: Owen Diehl <ow.diehl@gmail.com> Co-authored-by: achatterjee-grafana <70489351+achatterjee-grafana@users.noreply.github.com> Co-authored-by: Ursula Kallio <73951760+osg-grafana@users.noreply.github.com> Co-authored-by: Alexander Emelin <frvzmb@gmail.com> Co-authored-by: Nagle Zhang <nagle.zhang@sap.com> Co-authored-by: Erik Sundell <erik.sundell@grafana.com>
This commit is contained in:
@@ -0,0 +1,561 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 2,
|
||||||
|
"id": 396,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "degree"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 8,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "-9.4, -8.2, -8, -8, -7, -7.9, -5.9, -6.1, -5.5, -5.8, -4.7, -4.7, -4.1, -4.5, -3.5, -4, -2.4, -3.4, -1, -1, -0.3, -0.8, 0.8, 0.5, 1.7, 1.2, 2, 1.9, 2.4, 2.3, 3.3, 3.2, 3.9, 3.8, 4.5, 4.5, 5.6, 5.1, 7.1, 6.7, 7.2, 7.1, 7.6, 7.5, 8.4, 7.9, 9, 8.5, 9.4, 9.2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Origin",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "degree"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "B-series"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "tflops"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 8,
|
||||||
|
"x": 8,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 9,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"max": 10,
|
||||||
|
"min": -10,
|
||||||
|
"noise": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"spread": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"max": 10,
|
||||||
|
"min": -10,
|
||||||
|
"noise": 1,
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"spread": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"max": 10,
|
||||||
|
"min": -10,
|
||||||
|
"noise": 1,
|
||||||
|
"refId": "C",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"spread": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Sensor 1, ranging as Origin panel",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "degree"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "B-series"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "tflops"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "A-series"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "purple",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 8,
|
||||||
|
"x": 16,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 10,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"max": 4,
|
||||||
|
"min": -4,
|
||||||
|
"noise": 0,
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"spread": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"max": 4,
|
||||||
|
"min": -4,
|
||||||
|
"noise": 0,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"spread": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Sensor 2, ranging [-4, 4]",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "degree"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "B-series"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "tflops"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 8,
|
||||||
|
"x": 8,
|
||||||
|
"y": 9
|
||||||
|
},
|
||||||
|
"id": 7,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "5, 4.6, 4.4, 3.5, 3.3, 3, 2.6, 2.6, 2.4, 2.1, 1.8, 1.8, 1.7, 1.6, 1.5, 1.5, 1.4, 0.5, 0.5, 0, -0.2, -0.2, -0.4, -0.6, -1.2, -1.6, -2.4, -2.4, -2.8, -2.9, -2.9, -3.2, -3.3, -3.4, -4.3, -4.3, -4.6, -6.1, -6.3, -6.6, -6.7, -7, -7.2, -7.3, -7.5, -7.8, -8.2, -8.3, -9.1, -9.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "6,4,2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"refId": "C",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "-3,-4,-2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Sensor 3",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "degree"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "B-series"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "tflops"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 9,
|
||||||
|
"w": 8,
|
||||||
|
"x": 16,
|
||||||
|
"y": 9
|
||||||
|
},
|
||||||
|
"id": 8,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "6,4,2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"hide": false,
|
||||||
|
"refId": "C",
|
||||||
|
"scenarioId": "csv_metric_values",
|
||||||
|
"stringInput": "-3,-4,-2"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Sensor 5",
|
||||||
|
"type": "timeseries"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": false,
|
||||||
|
"schemaVersion": 28,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [
|
||||||
|
"gdev",
|
||||||
|
"panel-tests",
|
||||||
|
"graph-ng"
|
||||||
|
],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-6h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Panel Tests - shared tooltips cursor positioning",
|
||||||
|
"uid": "ICiqZ1rGk",
|
||||||
|
"version": 51
|
||||||
|
}
|
||||||
780
devenv/dev-dashboards/panel-graph/graph-shared-tooltips.json
Normal file
780
devenv/dev-dashboards/panel-graph/graph-shared-tooltips.json
Normal file
@@ -0,0 +1,780 @@
|
|||||||
|
{
|
||||||
|
"annotations": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"builtIn": 1,
|
||||||
|
"datasource": "-- Grafana --",
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"type": "dashboard"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"editable": true,
|
||||||
|
"gnetId": null,
|
||||||
|
"graphTooltip": 1,
|
||||||
|
"links": [],
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "celsius"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Speed"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "velocitykmh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 9,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 4,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "Temperature",
|
||||||
|
"max": 25,
|
||||||
|
"min": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "Speed",
|
||||||
|
"hide": false,
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "two units",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "points",
|
||||||
|
"fillOpacity": 0,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "auto",
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "celsius"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Temperature"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "celsius"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Speed"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "unit",
|
||||||
|
"value": "velocitykmh"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 8,
|
||||||
|
"x": 9,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 13,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"max": 25,
|
||||||
|
"min": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk",
|
||||||
|
"seriesCount": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Speed vs Temperature (XY)",
|
||||||
|
"transformations": [
|
||||||
|
{
|
||||||
|
"id": "seriesToColumns",
|
||||||
|
"options": {
|
||||||
|
"byField": "time"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "organize",
|
||||||
|
"options": {
|
||||||
|
"excludeByName": {
|
||||||
|
"time": true
|
||||||
|
},
|
||||||
|
"indexByName": {},
|
||||||
|
"renameByName": {
|
||||||
|
"A-series": "Temperature",
|
||||||
|
"A-series1": "Speed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "xychart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 8,
|
||||||
|
"w": 7,
|
||||||
|
"x": 17,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"id": 2,
|
||||||
|
"options": {
|
||||||
|
"counters": {
|
||||||
|
"dataChanged": true,
|
||||||
|
"render": true,
|
||||||
|
"schemaChanged": true
|
||||||
|
},
|
||||||
|
"mode": "cursor"
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Cursor info",
|
||||||
|
"type": "debug"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "celsius"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 9,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 5,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "Temperature",
|
||||||
|
"max": 25,
|
||||||
|
"min": 5,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Only temperature",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "velocitykmh"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Speed"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "yellow",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 8,
|
||||||
|
"x": 9,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 9,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "Speed",
|
||||||
|
"hide": false,
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Only Speed",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 10,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"graph": false,
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false
|
||||||
|
},
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "never",
|
||||||
|
"spanNulls": true,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mappings": [],
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "accMS2"
|
||||||
|
},
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Speed"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "yellow",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"matcher": {
|
||||||
|
"id": "byName",
|
||||||
|
"options": "Acceleration"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"id": "color",
|
||||||
|
"value": {
|
||||||
|
"fixedColor": "purple",
|
||||||
|
"mode": "fixed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 7,
|
||||||
|
"x": 17,
|
||||||
|
"y": 8
|
||||||
|
},
|
||||||
|
"id": 11,
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom"
|
||||||
|
},
|
||||||
|
"tooltipOptions": {
|
||||||
|
"mode": "single"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"alias": "Acceleration",
|
||||||
|
"hide": false,
|
||||||
|
"refId": "B",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Panel Title",
|
||||||
|
"type": "timeseries"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": null,
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"unit": "celsius"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 14
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 8,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"max": 25,
|
||||||
|
"min": 1,
|
||||||
|
"refId": "A",
|
||||||
|
"scenarioId": "random_walk"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "flot panel (temperature)",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "celsius",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": null,
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {
|
||||||
|
"h": 6,
|
||||||
|
"w": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 14
|
||||||
|
},
|
||||||
|
"hiddenSeries": false,
|
||||||
|
"id": 10,
|
||||||
|
"legend": {
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"show": true,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"options": {
|
||||||
|
"alertThreshold": true
|
||||||
|
},
|
||||||
|
"percentage": false,
|
||||||
|
"pluginVersion": "7.5.0-pre",
|
||||||
|
"pointradius": 2,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeRegions": [],
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "flot panel (no units)",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"yaxis": {
|
||||||
|
"align": false,
|
||||||
|
"alignLevel": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"refresh": false,
|
||||||
|
"schemaVersion": 28,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [
|
||||||
|
"gdev",
|
||||||
|
"panel-tests",
|
||||||
|
"graph-ng"
|
||||||
|
],
|
||||||
|
"templating": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "2020-09-14T16:13:20.000Z",
|
||||||
|
"to": "2020-09-15T20:00:00.000Z"
|
||||||
|
},
|
||||||
|
"timepicker": {},
|
||||||
|
"timezone": "",
|
||||||
|
"title": "Panel Tests - shared tooltips",
|
||||||
|
"uid": "TX2VU59MZ",
|
||||||
|
"version": 2
|
||||||
|
}
|
||||||
@@ -1,20 +1,23 @@
|
|||||||
import { DataFrame } from '../types';
|
import { DataFrame } from '../types';
|
||||||
import { BusEventWithPayload } from './types';
|
import { BusEventWithPayload } from './types';
|
||||||
|
|
||||||
/** @alpha */
|
/**
|
||||||
|
* When hovering over an element this will identify
|
||||||
|
*
|
||||||
|
* For performance reasons, this object will usually be mutated between updates. This
|
||||||
|
* will avoid creating new objects for events that fire frequently (ie each mouse pixel)
|
||||||
|
*
|
||||||
|
* @alpha
|
||||||
|
*/
|
||||||
export interface DataHoverPayload {
|
export interface DataHoverPayload {
|
||||||
raw: any; // Original mouse event (includes pageX etc)
|
|
||||||
|
|
||||||
x: Record<string, any>; // { time: 5678 },
|
|
||||||
y: Record<string, any>; // { __fixed: 123, lengthft: 456 } // each axis|scale gets a value
|
|
||||||
|
|
||||||
data?: DataFrame; // source data
|
data?: DataFrame; // source data
|
||||||
rowIndex?: number; // the hover row
|
rowIndex?: number; // the hover row
|
||||||
columnIndex?: number; // the hover column
|
columnIndex?: number; // the hover column
|
||||||
dataId?: string; // identifying string to correlate data between publishers and subscribers
|
dataId?: string; // identifying string to correlate data between publishers and subscribers
|
||||||
|
|
||||||
// When dragging, this will capture the original state
|
// When dragging, this will capture the point when the mouse was down
|
||||||
down?: Omit<DataHoverPayload, 'down'>;
|
point: Record<string, any>; // { time: 5678, lengthft: 456 } // each axis|scale gets a value
|
||||||
|
down?: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @alpha */
|
/** @alpha */
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export interface BusEvent {
|
|||||||
export abstract class BusEventBase implements BusEvent {
|
export abstract class BusEventBase implements BusEvent {
|
||||||
readonly type: string;
|
readonly type: string;
|
||||||
readonly payload?: any;
|
readonly payload?: any;
|
||||||
|
readonly origin?: EventBus;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
|
|||||||
5
packages/grafana-data/src/types/dashboard.ts
Normal file
5
packages/grafana-data/src/types/dashboard.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export enum DashboardCursorSync {
|
||||||
|
Off,
|
||||||
|
Crosshair,
|
||||||
|
Tooltip,
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
export * from './data';
|
export * from './data';
|
||||||
export * from './dataFrame';
|
export * from './dataFrame';
|
||||||
export * from './dataLink';
|
export * from './dataLink';
|
||||||
|
export * from './dashboard';
|
||||||
export * from './annotations';
|
export * from './annotations';
|
||||||
export * from './logs';
|
export * from './logs';
|
||||||
export * from './navModel';
|
export * from './navModel';
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { AngularPanelMenuItem } from './panel';
|
|||||||
import { DataFrame } from './dataFrame';
|
import { DataFrame } from './dataFrame';
|
||||||
import { eventFactory } from '../events/eventFactory';
|
import { eventFactory } from '../events/eventFactory';
|
||||||
import { BusEventBase, BusEventWithPayload } from '../events/types';
|
import { BusEventBase, BusEventWithPayload } from '../events/types';
|
||||||
|
import { DataHoverPayload } from '../events';
|
||||||
|
|
||||||
export type AlertPayload = [string, string?];
|
export type AlertPayload = [string, string?];
|
||||||
export type AlertErrorPayload = [string, (string | Error)?];
|
export type AlertErrorPayload = [string, (string | Error)?];
|
||||||
@@ -28,7 +29,7 @@ export const PanelEvents = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/** @public */
|
/** @public */
|
||||||
export interface LegacyGraphHoverEventPayload {
|
export interface LegacyGraphHoverEventPayload extends DataHoverPayload {
|
||||||
pos: any;
|
pos: any;
|
||||||
panel: {
|
panel: {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -43,4 +44,5 @@ export class LegacyGraphHoverEvent extends BusEventWithPayload<LegacyGraphHoverE
|
|||||||
/** @alpha */
|
/** @alpha */
|
||||||
export class LegacyGraphHoverClearEvent extends BusEventBase {
|
export class LegacyGraphHoverClearEvent extends BusEventBase {
|
||||||
static type = 'graph-hover-clear';
|
static type = 'graph-hover-clear';
|
||||||
|
payload: DataHoverPayload = { point: {} };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AlignedData } from 'uplot';
|
import { AlignedData } from 'uplot';
|
||||||
import { DataFrame, FieldMatcherID, fieldMatchers, FieldType, TimeRange, TimeZone } from '@grafana/data';
|
|
||||||
import { Themeable2 } from '../../types';
|
import { Themeable2 } from '../../types';
|
||||||
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
import { findMidPointYPosition, pluginLog, preparePlotData } from '../uPlot/utils';
|
||||||
import { GraphNGLegendEvent, XYFieldMatchers } from './types';
|
import {
|
||||||
|
DataFrame,
|
||||||
|
FieldMatcherID,
|
||||||
|
fieldMatchers,
|
||||||
|
FieldType,
|
||||||
|
LegacyGraphHoverClearEvent,
|
||||||
|
LegacyGraphHoverEvent,
|
||||||
|
TimeRange,
|
||||||
|
TimeZone,
|
||||||
|
} from '@grafana/data';
|
||||||
import { preparePlotFrame } from './utils';
|
import { preparePlotFrame } from './utils';
|
||||||
import { preparePlotData } from '../uPlot/utils';
|
|
||||||
import { UPlotChart } from '../uPlot/Plot';
|
|
||||||
import { VizLegendOptions } from '../VizLegend/models.gen';
|
import { VizLegendOptions } from '../VizLegend/models.gen';
|
||||||
|
import { PanelContext, PanelContextRoot } from '../PanelChrome/PanelContext';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
import { throttleTime } from 'rxjs/operators';
|
||||||
|
import { GraphNGLegendEvent, XYFieldMatchers } from './types';
|
||||||
|
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
||||||
import { VizLayout } from '../VizLayout/VizLayout';
|
import { VizLayout } from '../VizLayout/VizLayout';
|
||||||
|
import { UPlotChart } from '../uPlot/Plot';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal -- not a public API
|
* @internal -- not a public API
|
||||||
@@ -16,10 +29,10 @@ import { VizLayout } from '../VizLayout/VizLayout';
|
|||||||
export const FIXED_UNIT = '__fixed';
|
export const FIXED_UNIT = '__fixed';
|
||||||
|
|
||||||
export interface GraphNGProps extends Themeable2 {
|
export interface GraphNGProps extends Themeable2 {
|
||||||
width: number;
|
|
||||||
height: number;
|
|
||||||
frames: DataFrame[];
|
frames: DataFrame[];
|
||||||
structureRev?: number; // a number that will change when the frames[] structure changes
|
structureRev?: number; // a number that will change when the frames[] structure changes
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
timeRange: TimeRange;
|
timeRange: TimeRange;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
legend: VizLegendOptions;
|
legend: VizLegendOptions;
|
||||||
@@ -55,9 +68,16 @@ export interface GraphNGState {
|
|||||||
* "Time as X" core component, expectes ascending x
|
* "Time as X" core component, expectes ascending x
|
||||||
*/
|
*/
|
||||||
export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
||||||
|
static contextType = PanelContextRoot;
|
||||||
|
panelContext: PanelContext = {} as PanelContext;
|
||||||
|
private plotInstance: React.RefObject<uPlot>;
|
||||||
|
|
||||||
|
private subscription = new Subscription();
|
||||||
|
|
||||||
constructor(props: GraphNGProps) {
|
constructor(props: GraphNGProps) {
|
||||||
super(props);
|
super(props);
|
||||||
this.state = this.prepState(props);
|
this.state = this.prepState(props);
|
||||||
|
this.plotInstance = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
getTimeRange = () => this.props.timeRange;
|
getTimeRange = () => this.props.timeRange;
|
||||||
@@ -74,21 +94,76 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
|||||||
y: fieldMatchers.get(FieldMatcherID.numeric).get({}),
|
y: fieldMatchers.get(FieldMatcherID.numeric).get({}),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
pluginLog('GraphNG', false, 'data aligned', alignedFrame);
|
||||||
|
|
||||||
if (alignedFrame) {
|
if (alignedFrame) {
|
||||||
state = {
|
state = {
|
||||||
alignedFrame,
|
alignedFrame,
|
||||||
alignedData: preparePlotData(alignedFrame, [FieldType.number]),
|
alignedData: preparePlotData(alignedFrame, [FieldType.number]),
|
||||||
};
|
};
|
||||||
|
pluginLog('GraphNG', false, 'data prepared', state.alignedData);
|
||||||
|
|
||||||
if (withConfig) {
|
if (withConfig) {
|
||||||
state.config = props.prepConfig(alignedFrame, this.getTimeRange);
|
state.config = props.prepConfig(alignedFrame, this.getTimeRange);
|
||||||
|
pluginLog('GraphNG', false, 'config prepared', state.config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.panelContext = this.context as PanelContext;
|
||||||
|
const { eventBus } = this.panelContext;
|
||||||
|
|
||||||
|
this.subscription.add(
|
||||||
|
eventBus
|
||||||
|
.getStream(LegacyGraphHoverEvent)
|
||||||
|
.pipe(throttleTime(50))
|
||||||
|
.subscribe({
|
||||||
|
next: (evt) => {
|
||||||
|
const u = this.plotInstance?.current;
|
||||||
|
if (u) {
|
||||||
|
// Try finding left position on time axis
|
||||||
|
const left = u.valToPos(evt.payload.point.time, 'time');
|
||||||
|
let top;
|
||||||
|
if (left) {
|
||||||
|
// find midpoint between points at current idx
|
||||||
|
top = findMidPointYPosition(u, u.posToIdx(left));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!top || !left) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u.setCursor({
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subscription.add(
|
||||||
|
eventBus
|
||||||
|
.getStream(LegacyGraphHoverClearEvent)
|
||||||
|
.pipe(throttleTime(50))
|
||||||
|
.subscribe({
|
||||||
|
next: () => {
|
||||||
|
const u = this.plotInstance?.current;
|
||||||
|
|
||||||
|
if (u) {
|
||||||
|
u.setCursor({
|
||||||
|
left: -10,
|
||||||
|
top: -10,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: GraphNGProps) {
|
componentDidUpdate(prevProps: GraphNGProps) {
|
||||||
const { frames, structureRev, timeZone, propsToDiff } = this.props;
|
const { frames, structureRev, timeZone, propsToDiff } = this.props;
|
||||||
|
|
||||||
@@ -107,6 +182,7 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
|||||||
|
|
||||||
if (shouldReconfig) {
|
if (shouldReconfig) {
|
||||||
newState.config = this.props.prepConfig(newState.alignedFrame, this.getTimeRange);
|
newState.config = this.props.prepConfig(newState.alignedFrame, this.getTimeRange);
|
||||||
|
pluginLog('GraphNG', false, 'config recreated', newState.config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +190,10 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { width, height, children, timeRange, renderLegend } = this.props;
|
const { width, height, children, timeRange, renderLegend } = this.props;
|
||||||
const { config, alignedFrame } = this.state;
|
const { config, alignedFrame } = this.state;
|
||||||
@@ -131,6 +211,7 @@ export class GraphNG extends React.Component<GraphNGProps, GraphNGState> {
|
|||||||
width={vizWidth}
|
width={vizWidth}
|
||||||
height={vizHeight}
|
height={vizHeight}
|
||||||
timeRange={timeRange}
|
timeRange={timeRange}
|
||||||
|
plotRef={(u) => ((this.plotInstance as React.MutableRefObject<uPlot>).current = u)}
|
||||||
>
|
>
|
||||||
{children ? children(config, alignedFrame) : null}
|
{children ? children(config, alignedFrame) : null}
|
||||||
</UPlotChart>
|
</UPlotChart>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Object {
|
|||||||
"width": 1,
|
"width": 1,
|
||||||
},
|
},
|
||||||
"labelFont": "12px \\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif",
|
"labelFont": "12px \\"Roboto\\", \\"Helvetica\\", \\"Arial\\", sans-serif",
|
||||||
"scale": "x",
|
"scale": "time",
|
||||||
"show": true,
|
"show": true,
|
||||||
"side": 2,
|
"side": 2,
|
||||||
"size": [Function],
|
"size": [Function],
|
||||||
@@ -79,6 +79,20 @@ Object {
|
|||||||
"stroke": [Function],
|
"stroke": [Function],
|
||||||
"width": [Function],
|
"width": [Function],
|
||||||
},
|
},
|
||||||
|
"sync": Object {
|
||||||
|
"filters": Object {
|
||||||
|
"pub": [Function],
|
||||||
|
},
|
||||||
|
"key": "__global_",
|
||||||
|
"match": Array [
|
||||||
|
[Function],
|
||||||
|
[Function],
|
||||||
|
],
|
||||||
|
"scales": Array [
|
||||||
|
"time",
|
||||||
|
"__fixed",
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"hooks": Object {},
|
"hooks": Object {},
|
||||||
"scales": Object {
|
"scales": Object {
|
||||||
@@ -91,7 +105,7 @@ Object {
|
|||||||
"range": [Function],
|
"range": [Function],
|
||||||
"time": undefined,
|
"time": undefined,
|
||||||
},
|
},
|
||||||
"x": Object {
|
"time": Object {
|
||||||
"auto": false,
|
"auto": false,
|
||||||
"dir": 1,
|
"dir": 1,
|
||||||
"ori": 0,
|
"ori": 0,
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ import { preparePlotFrame } from './utils';
|
|||||||
import { preparePlotConfigBuilder } from '../TimeSeries/utils';
|
import { preparePlotConfigBuilder } from '../TimeSeries/utils';
|
||||||
import {
|
import {
|
||||||
createTheme,
|
createTheme,
|
||||||
|
DashboardCursorSync,
|
||||||
DefaultTimeZone,
|
DefaultTimeZone,
|
||||||
|
EventBusSrv,
|
||||||
FieldConfig,
|
FieldConfig,
|
||||||
FieldMatcherID,
|
FieldMatcherID,
|
||||||
fieldMatchers,
|
fieldMatchers,
|
||||||
@@ -194,6 +196,8 @@ describe('GraphNG utils', () => {
|
|||||||
theme: createTheme(),
|
theme: createTheme(),
|
||||||
timeZone: DefaultTimeZone,
|
timeZone: DefaultTimeZone,
|
||||||
getTimeRange: getDefaultTimeRange,
|
getTimeRange: getDefaultTimeRange,
|
||||||
|
eventBus: new EventBusSrv(),
|
||||||
|
sync: DashboardCursorSync.Tooltip,
|
||||||
}).getConfig();
|
}).getConfig();
|
||||||
expect(result).toMatchSnapshot();
|
expect(result).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { XYFieldMatchers } from './types';
|
|||||||
import {
|
import {
|
||||||
ArrayVector,
|
ArrayVector,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
|
EventBus,
|
||||||
FieldType,
|
FieldType,
|
||||||
GrafanaTheme2,
|
GrafanaTheme2,
|
||||||
outerJoinDataFrames,
|
outerJoinDataFrames,
|
||||||
@@ -9,13 +10,14 @@ import {
|
|||||||
TimeZone,
|
TimeZone,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { nullToUndefThreshold } from './nullToUndefThreshold';
|
import { nullToUndefThreshold } from './nullToUndefThreshold';
|
||||||
export interface PrepConfigOpts {
|
|
||||||
|
export type PrepConfigOpts<T extends Record<string, any> = {}> = {
|
||||||
frame: DataFrame;
|
frame: DataFrame;
|
||||||
theme: GrafanaTheme2;
|
theme: GrafanaTheme2;
|
||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
getTimeRange: () => TimeRange;
|
getTimeRange: () => TimeRange;
|
||||||
[prop: string]: any;
|
eventBus: EventBus;
|
||||||
}
|
} & T;
|
||||||
|
|
||||||
function applySpanNullsThresholds(frames: DataFrame[]) {
|
function applySpanNullsThresholds(frames: DataFrame[]) {
|
||||||
for (const frame of frames) {
|
for (const frame of frames) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { EventBusSrv, EventBus } from '@grafana/data';
|
import { EventBusSrv, EventBus, DashboardCursorSync } from '@grafana/data';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { SeriesVisibilityChangeMode } from '.';
|
import { SeriesVisibilityChangeMode } from '.';
|
||||||
|
|
||||||
@@ -6,6 +6,9 @@ import { SeriesVisibilityChangeMode } from '.';
|
|||||||
export interface PanelContext {
|
export interface PanelContext {
|
||||||
eventBus: EventBus;
|
eventBus: EventBus;
|
||||||
|
|
||||||
|
/** Dashboard panels sync */
|
||||||
|
sync?: DashboardCursorSync;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a component wants to change the color for a series
|
* Called when a component wants to change the color for a series
|
||||||
*
|
*
|
||||||
@@ -16,7 +19,7 @@ export interface PanelContext {
|
|||||||
onToggleSeriesVisibility?: (label: string, mode: SeriesVisibilityChangeMode) => void;
|
onToggleSeriesVisibility?: (label: string, mode: SeriesVisibilityChangeMode) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PanelContextRoot = React.createContext<PanelContext>({
|
export const PanelContextRoot = React.createContext<PanelContext>({
|
||||||
eventBus: new EventBusSrv(),
|
eventBus: new EventBusSrv(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,18 +6,20 @@ import { PlotLegend } from '../uPlot/PlotLegend';
|
|||||||
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
import { LegendDisplayMode } from '../VizLegend/models.gen';
|
||||||
import { preparePlotConfigBuilder } from './utils';
|
import { preparePlotConfigBuilder } from './utils';
|
||||||
import { withTheme2 } from '../../themes/ThemeContext';
|
import { withTheme2 } from '../../themes/ThemeContext';
|
||||||
|
import { PanelContext, PanelContextRoot } from '../PanelChrome/PanelContext';
|
||||||
|
|
||||||
const propsToDiff: string[] = [];
|
const propsToDiff: string[] = [];
|
||||||
|
|
||||||
type TimeSeriesProps = Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend'>;
|
type TimeSeriesProps = Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend'>;
|
||||||
|
|
||||||
export class UnthemedTimeSeries extends React.Component<TimeSeriesProps> {
|
export class UnthemedTimeSeries extends React.Component<TimeSeriesProps> {
|
||||||
|
static contextType = PanelContextRoot;
|
||||||
|
panelContext: PanelContext = {} as PanelContext;
|
||||||
|
|
||||||
prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => {
|
prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => {
|
||||||
return preparePlotConfigBuilder({
|
const { eventBus, sync } = this.context;
|
||||||
frame: alignedFrame,
|
const { theme, timeZone } = this.props;
|
||||||
getTimeRange,
|
return preparePlotConfigBuilder({ frame: alignedFrame, theme, timeZone, getTimeRange, eventBus, sync });
|
||||||
...this.props,
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
renderLegend = (config: UPlotConfigBuilder) => {
|
renderLegend = (config: UPlotConfigBuilder) => {
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
import {
|
import {
|
||||||
|
DashboardCursorSync,
|
||||||
DataFrame,
|
DataFrame,
|
||||||
|
DataHoverClearEvent,
|
||||||
|
DataHoverEvent,
|
||||||
|
DataHoverPayload,
|
||||||
FieldConfig,
|
FieldConfig,
|
||||||
FieldType,
|
FieldType,
|
||||||
formattedValueToString,
|
formattedValueToString,
|
||||||
@@ -9,7 +13,6 @@ import {
|
|||||||
getFieldSeriesColor,
|
getFieldSeriesColor,
|
||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
|
|
||||||
import { PrepConfigOpts } from '../GraphNG/utils';
|
|
||||||
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
||||||
import { FIXED_UNIT } from '../GraphNG/GraphNG';
|
import { FIXED_UNIT } from '../GraphNG/GraphNG';
|
||||||
import {
|
import {
|
||||||
@@ -22,6 +25,7 @@ import {
|
|||||||
ScaleOrientation,
|
ScaleOrientation,
|
||||||
} from '../uPlot/config';
|
} from '../uPlot/config';
|
||||||
import { collectStackingGroups } from '../uPlot/utils';
|
import { collectStackingGroups } from '../uPlot/utils';
|
||||||
|
import { PrepConfigOpts } from '../GraphNG/utils';
|
||||||
|
|
||||||
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
|
const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1));
|
||||||
|
|
||||||
@@ -31,9 +35,9 @@ const defaultConfig: GraphFieldConfig = {
|
|||||||
axisPlacement: AxisPlacement.Auto,
|
axisPlacement: AxisPlacement.Auto,
|
||||||
};
|
};
|
||||||
|
|
||||||
type PrepConfig = (opts: PrepConfigOpts) => UPlotConfigBuilder;
|
type PrepConfig = (opts: PrepConfigOpts<{ sync: DashboardCursorSync }>) => UPlotConfigBuilder;
|
||||||
|
|
||||||
export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, getTimeRange }) => {
|
export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, getTimeRange, eventBus, sync }) => {
|
||||||
const builder = new UPlotConfigBuilder(timeZone);
|
const builder = new UPlotConfigBuilder(timeZone);
|
||||||
|
|
||||||
// X is the first field in the aligned frame
|
// X is the first field in the aligned frame
|
||||||
@@ -44,20 +48,24 @@ export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, g
|
|||||||
|
|
||||||
let seriesIndex = 0;
|
let seriesIndex = 0;
|
||||||
|
|
||||||
|
let xScaleKey = '_x';
|
||||||
|
let yScaleKey = '';
|
||||||
|
|
||||||
if (xField.type === FieldType.time) {
|
if (xField.type === FieldType.time) {
|
||||||
|
xScaleKey = 'time';
|
||||||
builder.addScale({
|
builder.addScale({
|
||||||
scaleKey: 'x',
|
scaleKey: xScaleKey,
|
||||||
orientation: ScaleOrientation.Horizontal,
|
orientation: ScaleOrientation.Horizontal,
|
||||||
direction: ScaleDirection.Right,
|
direction: ScaleDirection.Right,
|
||||||
isTime: true,
|
isTime: true,
|
||||||
range: () => {
|
range: () => {
|
||||||
const timeRange = getTimeRange();
|
const r = getTimeRange();
|
||||||
return [timeRange.from.valueOf(), timeRange.to.valueOf()];
|
return [r.from.valueOf(), r.to.valueOf()];
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addAxis({
|
builder.addAxis({
|
||||||
scaleKey: 'x',
|
scaleKey: xScaleKey,
|
||||||
isTime: true,
|
isTime: true,
|
||||||
placement: AxisPlacement.Bottom,
|
placement: AxisPlacement.Bottom,
|
||||||
timeZone,
|
timeZone,
|
||||||
@@ -65,14 +73,18 @@ export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, g
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Not time!
|
// Not time!
|
||||||
|
if (xField.config.unit) {
|
||||||
|
xScaleKey = xField.config.unit;
|
||||||
|
}
|
||||||
|
|
||||||
builder.addScale({
|
builder.addScale({
|
||||||
scaleKey: 'x',
|
scaleKey: xScaleKey,
|
||||||
orientation: ScaleOrientation.Horizontal,
|
orientation: ScaleOrientation.Horizontal,
|
||||||
direction: ScaleDirection.Right,
|
direction: ScaleDirection.Right,
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addAxis({
|
builder.addAxis({
|
||||||
scaleKey: 'x',
|
scaleKey: xScaleKey,
|
||||||
placement: AxisPlacement.Bottom,
|
placement: AxisPlacement.Bottom,
|
||||||
theme,
|
theme,
|
||||||
});
|
});
|
||||||
@@ -82,7 +94,7 @@ export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, g
|
|||||||
|
|
||||||
let indexByName: Map<string, number> | undefined = undefined;
|
let indexByName: Map<string, number> | undefined = undefined;
|
||||||
|
|
||||||
for (let i = 0; i < frame.fields.length; i++) {
|
for (let i = 1; i < frame.fields.length; i++) {
|
||||||
const field = frame.fields[i];
|
const field = frame.fields[i];
|
||||||
const config = field.config as FieldConfig<GraphFieldConfig>;
|
const config = field.config as FieldConfig<GraphFieldConfig>;
|
||||||
const customConfig: GraphFieldConfig = {
|
const customConfig: GraphFieldConfig = {
|
||||||
@@ -114,6 +126,10 @@ export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, g
|
|||||||
softMax: customConfig.axisSoftMax,
|
softMax: customConfig.axisSoftMax,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!yScaleKey) {
|
||||||
|
yScaleKey = scaleKey;
|
||||||
|
}
|
||||||
|
|
||||||
if (customConfig.axisPlacement !== AxisPlacement.Hidden) {
|
if (customConfig.axisPlacement !== AxisPlacement.Hidden) {
|
||||||
builder.addAxis({
|
builder.addAxis({
|
||||||
scaleKey,
|
scaleKey,
|
||||||
@@ -183,7 +199,6 @@ export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, g
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
collectStackingGroups(field, stackingGroups, seriesIndex);
|
collectStackingGroups(field, stackingGroups, seriesIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,6 +212,45 @@ export const preparePlotConfigBuilder: PrepConfig = ({ frame, theme, timeZone, g
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.scaleKeys = [xScaleKey, yScaleKey];
|
||||||
|
|
||||||
|
if (sync !== DashboardCursorSync.Off) {
|
||||||
|
const payload: DataHoverPayload = {
|
||||||
|
point: {
|
||||||
|
[xScaleKey]: null,
|
||||||
|
[yScaleKey]: null,
|
||||||
|
},
|
||||||
|
data: frame,
|
||||||
|
};
|
||||||
|
const hoverEvent = new DataHoverEvent(payload);
|
||||||
|
builder.setCursor({
|
||||||
|
sync: {
|
||||||
|
key: '__global_',
|
||||||
|
filters: {
|
||||||
|
pub: (type: string, src: uPlot, x: number, y: number, w: number, h: number, dataIdx: number) => {
|
||||||
|
payload.columnIndex = dataIdx;
|
||||||
|
if (x < 0 && y < 0) {
|
||||||
|
payload.point[xScaleKey] = null;
|
||||||
|
payload.point[yScaleKey] = null;
|
||||||
|
eventBus.publish(new DataHoverClearEvent(payload));
|
||||||
|
} else {
|
||||||
|
// convert the points
|
||||||
|
payload.point[xScaleKey] = src.posToVal(x, xScaleKey);
|
||||||
|
payload.point[yScaleKey] = src.posToVal(y, yScaleKey);
|
||||||
|
eventBus.publish(hoverEvent);
|
||||||
|
hoverEvent.payload.down = undefined;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// ??? setSeries: syncMode === DashboardCursorSync.Tooltip,
|
||||||
|
scales: builder.scaleKeys,
|
||||||
|
match: [() => true, () => true],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { GraphNG, GraphNGProps } from '../GraphNG/GraphNG';
|
|||||||
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from '../uPlot/config/UPlotConfigBuilder';
|
||||||
import { preparePlotConfigBuilder } from './utils';
|
import { preparePlotConfigBuilder } from './utils';
|
||||||
import { BarValueVisibility, TimelineMode } from './types';
|
import { BarValueVisibility, TimelineMode } from './types';
|
||||||
|
import { PanelContext, PanelContextRoot } from '../PanelChrome/PanelContext';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @alpha
|
* @alpha
|
||||||
@@ -19,10 +20,17 @@ export interface TimelineProps extends Omit<GraphNGProps, 'prepConfig' | 'propsT
|
|||||||
const propsToDiff = ['mode', 'rowHeight', 'colWidth', 'showValue'];
|
const propsToDiff = ['mode', 'rowHeight', 'colWidth', 'showValue'];
|
||||||
|
|
||||||
export class UnthemedTimelineChart extends React.Component<TimelineProps> {
|
export class UnthemedTimelineChart extends React.Component<TimelineProps> {
|
||||||
|
static contextType = PanelContextRoot;
|
||||||
|
panelContext: PanelContext = {} as PanelContext;
|
||||||
|
|
||||||
prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => {
|
prepConfig = (alignedFrame: DataFrame, getTimeRange: () => TimeRange) => {
|
||||||
|
this.panelContext = this.context as PanelContext;
|
||||||
|
const { eventBus } = this.panelContext;
|
||||||
|
|
||||||
return preparePlotConfigBuilder({
|
return preparePlotConfigBuilder({
|
||||||
frame: alignedFrame,
|
frame: alignedFrame,
|
||||||
getTimeRange,
|
getTimeRange,
|
||||||
|
eventBus,
|
||||||
...this.props,
|
...this.props,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ export function preparePlotFrame(data: DataFrame[], dimFields: XYFieldMatchers)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PrepConfigOptsTimeline extends PrepConfigOpts {
|
type PrepConfig = (
|
||||||
mode: TimelineMode;
|
opts: PrepConfigOpts<{
|
||||||
rowHeight: number;
|
mode: TimelineMode;
|
||||||
colWidth?: number;
|
rowHeight: number;
|
||||||
showValue: BarValueVisibility;
|
colWidth?: number;
|
||||||
}
|
showValue: BarValueVisibility;
|
||||||
|
}>
|
||||||
type PrepConfig = (opts: PrepConfigOptsTimeline) => UPlotConfigBuilder;
|
) => UPlotConfigBuilder;
|
||||||
|
|
||||||
export const preparePlotConfigBuilder: PrepConfig = ({
|
export const preparePlotConfigBuilder: PrepConfig = ({
|
||||||
frame,
|
frame,
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ export const VizTooltipContainer: React.FC<VizTooltipContainerProps> = ({
|
|||||||
left: 0,
|
left: 0,
|
||||||
top: 0,
|
top: 0,
|
||||||
transform: `translate3d(${placement.x}px, ${placement.y}px, 0)`,
|
transform: `translate3d(${placement.x}px, ${placement.y}px, 0)`,
|
||||||
|
transition: 'all ease-out 0.1s',
|
||||||
}}
|
}}
|
||||||
{...otherProps}
|
{...otherProps}
|
||||||
className={cx(styles.wrapper, className)}
|
className={cx(styles.wrapper, className)}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { createRef } from 'react';
|
import React, { createRef, MutableRefObject } from 'react';
|
||||||
import uPlot, { Options } from 'uplot';
|
import uPlot, { Options } from 'uplot';
|
||||||
import { PlotContext, PlotContextType } from './context';
|
import { PlotContext, PlotContextType } from './context';
|
||||||
import { DEFAULT_PLOT_CONFIG } from './utils';
|
import { DEFAULT_PLOT_CONFIG } from './utils';
|
||||||
@@ -28,6 +28,7 @@ type UPlotChartState = {
|
|||||||
*/
|
*/
|
||||||
export class UPlotChart extends React.Component<PlotProps, UPlotChartState> {
|
export class UPlotChart extends React.Component<PlotProps, UPlotChartState> {
|
||||||
plotContainer = createRef<HTMLDivElement>();
|
plotContainer = createRef<HTMLDivElement>();
|
||||||
|
plotCanvasBBox = createRef<DOMRect>();
|
||||||
|
|
||||||
constructor(props: PlotProps) {
|
constructor(props: PlotProps) {
|
||||||
super(props);
|
super(props);
|
||||||
@@ -35,20 +36,27 @@ export class UPlotChart extends React.Component<PlotProps, UPlotChartState> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
ctx: {
|
ctx: {
|
||||||
plot: null,
|
plot: null,
|
||||||
|
getCanvasBoundingBox: () => this.plotCanvasBBox.current,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
reinitPlot() {
|
reinitPlot() {
|
||||||
let { ctx } = this.state;
|
let { ctx } = this.state;
|
||||||
|
let { width, height, plotRef } = this.props;
|
||||||
|
|
||||||
ctx.plot?.destroy();
|
ctx.plot?.destroy();
|
||||||
|
|
||||||
let { width, height } = this.props;
|
|
||||||
|
|
||||||
if (width === 0 && height === 0) {
|
if (width === 0 && height === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.props.config.addHook('setSize', (u) => {
|
||||||
|
const canvas = u.root.querySelector<HTMLDivElement>('.u-over');
|
||||||
|
if (!canvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
(this.plotCanvasBBox as MutableRefObject<any>).current = canvas.getBoundingClientRect();
|
||||||
|
});
|
||||||
|
|
||||||
const config: Options = {
|
const config: Options = {
|
||||||
...DEFAULT_PLOT_CONFIG,
|
...DEFAULT_PLOT_CONFIG,
|
||||||
@@ -58,11 +66,19 @@ export class UPlotChart extends React.Component<PlotProps, UPlotChartState> {
|
|||||||
...this.props.config.getConfig(),
|
...this.props.config.getConfig(),
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setState({
|
const plot = new uPlot(config, this.props.data, this.plotContainer!.current!);
|
||||||
|
|
||||||
|
if (plotRef) {
|
||||||
|
plotRef(plot);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState((s) => ({
|
||||||
|
...s,
|
||||||
ctx: {
|
ctx: {
|
||||||
plot: new uPlot(config, this.props.data, this.plotContainer!.current!),
|
...s.ctx,
|
||||||
|
plot,
|
||||||
},
|
},
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
@@ -82,7 +98,7 @@ export class UPlotChart extends React.Component<PlotProps, UPlotChartState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: PlotProps, prevState: object) {
|
componentDidUpdate(prevProps: PlotProps) {
|
||||||
let { ctx } = this.state;
|
let { ctx } = this.state;
|
||||||
|
|
||||||
if (!sameDims(prevProps, this.props)) {
|
if (!sameDims(prevProps, this.props)) {
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ export class UPlotConfigBuilder {
|
|||||||
this.tz = getTimeZoneInfo(timeZone, Date.now())?.ianaName;
|
this.tz = getTimeZoneInfo(timeZone, Date.now())?.ianaName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exposed to let the container know the primary scale keys
|
||||||
|
scaleKeys: [string, string] = ['', ''];
|
||||||
|
|
||||||
addHook<T extends keyof Hooks.Defs>(type: T, hook: Hooks.Defs[T]) {
|
addHook<T extends keyof Hooks.Defs>(type: T, hook: Hooks.Defs[T]) {
|
||||||
pluginLog('UPlotConfigBuilder', false, 'addHook', type);
|
pluginLog('UPlotConfigBuilder', false, 'addHook', type);
|
||||||
|
|
||||||
@@ -81,7 +84,7 @@ export class UPlotConfigBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCursor(cursor?: Cursor) {
|
setCursor(cursor?: Cursor) {
|
||||||
this.cursor = cursor;
|
this.cursor = { ...this.cursor, ...cursor };
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelect(select: Select) {
|
setSelect(select: Select) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import uPlot from 'uplot';
|
import uPlot from 'uplot';
|
||||||
|
|
||||||
export interface PlotContextType {
|
export interface PlotContextType {
|
||||||
plot: uPlot | null;
|
plot: uPlot | null;
|
||||||
|
getCanvasBoundingBox: () => DOMRect | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||||
import { Portal } from '../../Portal/Portal';
|
import { Portal } from '../../Portal/Portal';
|
||||||
import { usePlotContext } from '../context';
|
import { usePlotContext } from '../context';
|
||||||
import {
|
import {
|
||||||
@@ -12,8 +12,9 @@ import {
|
|||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { SeriesTable, SeriesTableRowProps, TooltipDisplayMode, VizTooltipContainer } from '../../VizTooltip';
|
import { SeriesTable, SeriesTableRowProps, TooltipDisplayMode, VizTooltipContainer } from '../../VizTooltip';
|
||||||
import { UPlotConfigBuilder } from '../config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from '../config/UPlotConfigBuilder';
|
||||||
import { pluginLog } from '../utils';
|
import { findMidPointYPosition, pluginLog } from '../utils';
|
||||||
import { useTheme2 } from '../../../themes/ThemeContext';
|
import { useTheme2 } from '../../../themes/ThemeContext';
|
||||||
|
import uPlot from 'uplot';
|
||||||
|
|
||||||
interface TooltipPluginProps {
|
interface TooltipPluginProps {
|
||||||
mode?: TooltipDisplayMode;
|
mode?: TooltipDisplayMode;
|
||||||
@@ -22,6 +23,8 @@ interface TooltipPluginProps {
|
|||||||
config: UPlotConfigBuilder;
|
config: UPlotConfigBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TOOLTIP_OFFSET = 10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @alpha
|
* @alpha
|
||||||
*/
|
*/
|
||||||
@@ -33,46 +36,41 @@ export const TooltipPlugin: React.FC<TooltipPluginProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const plotCtx = usePlotContext();
|
const plotCtx = usePlotContext();
|
||||||
const plotCanvas = useRef<HTMLDivElement>();
|
|
||||||
const plotCanvasBBox = useRef<any>({ left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 });
|
|
||||||
const [focusedSeriesIdx, setFocusedSeriesIdx] = useState<number | null>(null);
|
const [focusedSeriesIdx, setFocusedSeriesIdx] = useState<number | null>(null);
|
||||||
const [focusedPointIdx, setFocusedPointIdx] = useState<number | null>(null);
|
const [focusedPointIdx, setFocusedPointIdx] = useState<number | null>(null);
|
||||||
const [coords, setCoords] = useState<{ viewport: CartesianCoords2D; plotCanvas: CartesianCoords2D } | null>(null);
|
|
||||||
|
const [coords, setCoords] = useState<CartesianCoords2D | null>(null);
|
||||||
|
|
||||||
|
const pluginId = `TooltipPlugin`;
|
||||||
|
|
||||||
// Debug logs
|
// Debug logs
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
pluginLog('TooltipPlugin', true, `Focused series: ${focusedSeriesIdx}, focused point: ${focusedPointIdx}`);
|
pluginLog(pluginId, true, `Focused series: ${focusedSeriesIdx}, focused point: ${focusedPointIdx}`);
|
||||||
}, [focusedPointIdx, focusedSeriesIdx]);
|
}, [focusedPointIdx, focusedSeriesIdx]);
|
||||||
|
|
||||||
// Add uPlot hooks to the config, or re-add when the config changed
|
// Add uPlot hooks to the config, or re-add when the config changed
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
const onMouseCapture = (e: MouseEvent) => {
|
|
||||||
setCoords({
|
|
||||||
plotCanvas: {
|
|
||||||
x: e.clientX - plotCanvasBBox.current.left,
|
|
||||||
y: e.clientY - plotCanvasBBox.current.top,
|
|
||||||
},
|
|
||||||
viewport: {
|
|
||||||
x: e.clientX,
|
|
||||||
y: e.clientY,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
config.addHook('init', (u) => {
|
|
||||||
const canvas = u.root.querySelector<HTMLDivElement>('.u-over');
|
|
||||||
plotCanvas.current = canvas || undefined;
|
|
||||||
plotCanvas.current?.addEventListener('mousemove', onMouseCapture);
|
|
||||||
plotCanvas.current?.addEventListener('mouseleave', () => {});
|
|
||||||
});
|
|
||||||
|
|
||||||
config.addHook('setCursor', (u) => {
|
config.addHook('setCursor', (u) => {
|
||||||
setFocusedPointIdx(u.cursor.idx === undefined ? null : u.cursor.idx);
|
const bbox = plotCtx.getCanvasBoundingBox();
|
||||||
|
if (!bbox) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { x, y } = positionTooltip(u, bbox);
|
||||||
|
|
||||||
|
if (x !== undefined && y !== undefined) {
|
||||||
|
setCoords({ x, y });
|
||||||
|
} else {
|
||||||
|
setCoords(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFocusedPointIdx(u.cursor.idx === undefined ? u.posToIdx(u.cursor.left || 0) : u.cursor.idx);
|
||||||
});
|
});
|
||||||
|
|
||||||
config.addHook('setSeries', (_, idx) => {
|
config.addHook('setSeries', (_, idx) => {
|
||||||
setFocusedSeriesIdx(idx);
|
setFocusedSeriesIdx(idx);
|
||||||
});
|
});
|
||||||
}, [config]);
|
}, [plotCtx, config]);
|
||||||
|
|
||||||
const plotInstance = plotCtx.plot;
|
const plotInstance = plotCtx.plot;
|
||||||
if (!plotInstance || focusedPointIdx === null) {
|
if (!plotInstance || focusedPointIdx === null) {
|
||||||
@@ -142,15 +140,52 @@ export const TooltipPlugin: React.FC<TooltipPluginProps> = ({
|
|||||||
tooltip = <SeriesTable series={series} timestamp={xVal} />;
|
tooltip = <SeriesTable series={series} timestamp={xVal} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tooltip || !coords) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<VizTooltipContainer position={{ x: coords.viewport.x, y: coords.viewport.y }} offset={{ x: 10, y: 10 }}>
|
{tooltip && coords && (
|
||||||
{tooltip}
|
<VizTooltipContainer position={{ x: coords.x, y: coords.y }} offset={{ x: TOOLTIP_OFFSET, y: TOOLTIP_OFFSET }}>
|
||||||
</VizTooltipContainer>
|
{tooltip}
|
||||||
|
</VizTooltipContainer>
|
||||||
|
)}
|
||||||
</Portal>
|
</Portal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isCursourOutsideCanvas({ left, top }: uPlot.Cursor, canvas: DOMRect) {
|
||||||
|
if (left === undefined || top === undefined) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return left < 0 || left > canvas.width || top < 0 || top > canvas.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given uPlot cursor position, figure out position of the tooltip withing the canvas bbox
|
||||||
|
* Tooltip is positioned relatively to a viewport
|
||||||
|
* @internal
|
||||||
|
**/
|
||||||
|
export function positionTooltip(u: uPlot, bbox: DOMRect) {
|
||||||
|
let x, y;
|
||||||
|
const cL = u.cursor.left || 0;
|
||||||
|
const cT = u.cursor.top || 0;
|
||||||
|
|
||||||
|
if (isCursourOutsideCanvas(u.cursor, bbox)) {
|
||||||
|
const idx = u.posToIdx(cL);
|
||||||
|
// when cursor outside of uPlot's canvas
|
||||||
|
if (cT < 0 || cT > bbox.height) {
|
||||||
|
let pos = findMidPointYPosition(u, idx);
|
||||||
|
|
||||||
|
if (pos) {
|
||||||
|
y = bbox.top + pos;
|
||||||
|
if (cL >= 0 && cL <= bbox.width) {
|
||||||
|
// find x-scale position for a current cursor left position
|
||||||
|
x = bbox.left + u.valToPos(u.data[0][u.posToIdx(cL)], u.series[0].scale!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x = bbox.left + cL;
|
||||||
|
y = bbox.top + cT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x, y };
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Options, AlignedData } from 'uplot';
|
import uPlot, { Options, AlignedData } from 'uplot';
|
||||||
import { TimeRange } from '@grafana/data';
|
import { TimeRange } from '@grafana/data';
|
||||||
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
import { UPlotConfigBuilder } from './config/UPlotConfigBuilder';
|
||||||
|
|
||||||
@@ -19,6 +19,8 @@ export interface PlotProps {
|
|||||||
config: UPlotConfigBuilder;
|
config: UPlotConfigBuilder;
|
||||||
timeRange: TimeRange;
|
timeRange: TimeRange;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
// Reference to uPlot instance
|
||||||
|
plotRef?: (u: uPlot) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class PlotConfigBuilder<P, T> {
|
export abstract class PlotConfigBuilder<P, T> {
|
||||||
|
|||||||
@@ -106,6 +106,57 @@ export function collectStackingGroups(f: Field, groups: Map<string, number[]>, s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds y axis midpoind for point at given idx (css pixels relative to uPlot canvas)
|
||||||
|
* @internal
|
||||||
|
**/
|
||||||
|
|
||||||
|
export function findMidPointYPosition(u: uPlot, idx: number) {
|
||||||
|
let y;
|
||||||
|
let sMaxIdx = 1;
|
||||||
|
let sMinIdx = 1;
|
||||||
|
// assume min/max being values of 1st series
|
||||||
|
let max = u.data[1][idx];
|
||||||
|
let min = u.data[1][idx];
|
||||||
|
|
||||||
|
// find min max values AND ids of the corresponding series to get the scales
|
||||||
|
for (let i = 1; i < u.data.length; i++) {
|
||||||
|
const sData = u.data[i];
|
||||||
|
const sVal = sData[idx];
|
||||||
|
if (sVal !== null) {
|
||||||
|
if (max === null) {
|
||||||
|
max = sVal;
|
||||||
|
} else {
|
||||||
|
if (sVal > max) {
|
||||||
|
max = u.data[i][idx];
|
||||||
|
sMaxIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min === null) {
|
||||||
|
min = sVal;
|
||||||
|
} else {
|
||||||
|
if (sVal < min) {
|
||||||
|
min = u.data[i][idx];
|
||||||
|
sMinIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (min === null && max === null) {
|
||||||
|
// no tooltip to show
|
||||||
|
y = undefined;
|
||||||
|
} else if (min !== null && max !== null) {
|
||||||
|
// find median position
|
||||||
|
y = (u.valToPos(min, u.series[sMinIdx].scale!) + u.valToPos(max, u.series[sMaxIdx].scale!)) / 2;
|
||||||
|
} else {
|
||||||
|
// snap tooltip to min OR max point, one of thos is not null :)
|
||||||
|
y = u.valToPos((min || max)!, u.series[(sMaxIdx || sMinIdx)!].scale!);
|
||||||
|
}
|
||||||
|
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
// Dev helpers
|
// Dev helpers
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface Logger {
|
|||||||
logger: (...t: any[]) => void;
|
logger: (...t: any[]) => void;
|
||||||
enable: () => void;
|
enable: () => void;
|
||||||
disable: () => void;
|
disable: () => void;
|
||||||
|
isEnabled: () => boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@@ -29,5 +30,6 @@ export const createLogger = (name: string): Logger => {
|
|||||||
},
|
},
|
||||||
enable: () => (LOGGIN_ENABLED = true),
|
enable: () => (LOGGIN_ENABLED = true),
|
||||||
disable: () => (LOGGIN_ENABLED = false),
|
disable: () => (LOGGIN_ENABLED = false),
|
||||||
|
isEnabled: () => LOGGIN_ENABLED,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { SelectableValue, TimeZone } from '@grafana/data';
|
import { TimeZone } from '@grafana/data';
|
||||||
import { Select, TagsInput, Input, Field, CollapsableSection, RadioButtonGroup } from '@grafana/ui';
|
import { TagsInput, Input, Field, CollapsableSection, RadioButtonGroup } from '@grafana/ui';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
import { FolderPicker } from 'app/core/components/Select/FolderPicker';
|
||||||
import { DashboardModel } from '../../state/DashboardModel';
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
@@ -30,8 +30,8 @@ export const GeneralSettings: React.FC<Props> = ({ dashboard }) => {
|
|||||||
dashboard[event.currentTarget.name as 'title' | 'description'] = event.currentTarget.value;
|
dashboard[event.currentTarget.name as 'title' | 'description'] = event.currentTarget.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onTooltipChange = (graphTooltip: SelectableValue<number>) => {
|
const onTooltipChange = (graphTooltip: number) => {
|
||||||
dashboard.graphTooltip = graphTooltip.value;
|
dashboard.graphTooltip = graphTooltip;
|
||||||
setRenderCounter(renderCounter + 1);
|
setRenderCounter(renderCounter + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -117,12 +117,7 @@ export const GeneralSettings: React.FC<Props> = ({ dashboard }) => {
|
|||||||
label="Graph tooltip"
|
label="Graph tooltip"
|
||||||
description="Controls tooltip and hover highlight behavior across different panels"
|
description="Controls tooltip and hover highlight behavior across different panels"
|
||||||
>
|
>
|
||||||
<Select
|
<RadioButtonGroup onChange={onTooltipChange} options={GRAPH_TOOLTIP_OPTIONS} value={dashboard.graphTooltip} />
|
||||||
onChange={onTooltipChange}
|
|
||||||
options={GRAPH_TOOLTIP_OPTIONS}
|
|
||||||
width={40}
|
|
||||||
value={dashboard.graphTooltip}
|
|
||||||
/>
|
|
||||||
</Field>
|
</Field>
|
||||||
</CollapsableSection>
|
</CollapsableSection>
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { DashboardModel, PanelModel } from '../state';
|
|||||||
import { PANEL_BORDER } from 'app/core/constants';
|
import { PANEL_BORDER } from 'app/core/constants';
|
||||||
import {
|
import {
|
||||||
AbsoluteTimeRange,
|
AbsoluteTimeRange,
|
||||||
|
DashboardCursorSync,
|
||||||
EventBusSrv,
|
EventBusSrv,
|
||||||
EventFilterOptions,
|
EventFilterOptions,
|
||||||
FieldConfigSource,
|
FieldConfigSource,
|
||||||
@@ -76,6 +77,7 @@ export class PanelChrome extends Component<Props, State> {
|
|||||||
renderCounter: 0,
|
renderCounter: 0,
|
||||||
refreshWhenInView: false,
|
refreshWhenInView: false,
|
||||||
context: {
|
context: {
|
||||||
|
sync: props.isEditing ? DashboardCursorSync.Off : props.dashboard.graphTooltip,
|
||||||
eventBus,
|
eventBus,
|
||||||
onSeriesColorChange: this.onSeriesColorChange,
|
onSeriesColorChange: this.onSeriesColorChange,
|
||||||
onToggleSeriesVisibility: this.onSeriesVisibilityChange,
|
onToggleSeriesVisibility: this.onSeriesVisibilityChange,
|
||||||
@@ -139,7 +141,23 @@ export class PanelChrome extends Component<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const { isInView } = this.props;
|
const { isInView, isEditing } = this.props;
|
||||||
|
|
||||||
|
if (prevProps.dashboard.graphTooltip !== this.props.dashboard.graphTooltip) {
|
||||||
|
this.setState((s) => {
|
||||||
|
return {
|
||||||
|
context: { ...s.context, sync: isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEditing !== prevProps.isEditing) {
|
||||||
|
this.setState((s) => {
|
||||||
|
return {
|
||||||
|
context: { ...s.context, sync: isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip },
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// View state has changed
|
// View state has changed
|
||||||
if (isInView !== prevProps.isInView) {
|
if (isInView !== prevProps.isInView) {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { DashboardMigrator } from './DashboardMigrator';
|
|||||||
import {
|
import {
|
||||||
AnnotationQuery,
|
AnnotationQuery,
|
||||||
AppEvent,
|
AppEvent,
|
||||||
|
DashboardCursorSync,
|
||||||
dateTimeFormat,
|
dateTimeFormat,
|
||||||
dateTimeFormatTimeAgo,
|
dateTimeFormatTimeAgo,
|
||||||
DateTimeInput,
|
DateTimeInput,
|
||||||
@@ -74,7 +75,7 @@ export class DashboardModel {
|
|||||||
style: any;
|
style: any;
|
||||||
timezone: any;
|
timezone: any;
|
||||||
editable: any;
|
editable: any;
|
||||||
graphTooltip: any;
|
graphTooltip: DashboardCursorSync;
|
||||||
time: any;
|
time: any;
|
||||||
private originalTime: any;
|
private originalTime: any;
|
||||||
timepicker: any;
|
timepicker: any;
|
||||||
|
|||||||
69
public/app/plugins/panel/debug/CursorView.tsx
Normal file
69
public/app/plugins/panel/debug/CursorView.tsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import {
|
||||||
|
EventBus,
|
||||||
|
LegacyGraphHoverEvent,
|
||||||
|
LegacyGraphHoverClearEvent,
|
||||||
|
DataHoverEvent,
|
||||||
|
DataHoverClearEvent,
|
||||||
|
DataHoverPayload,
|
||||||
|
BusEventWithPayload,
|
||||||
|
} from '@grafana/data';
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { Subscription } from 'rxjs';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
eventBus: EventBus;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
event?: BusEventWithPayload<DataHoverPayload>;
|
||||||
|
}
|
||||||
|
export class CursorView extends Component<Props, State> {
|
||||||
|
subscription = new Subscription();
|
||||||
|
state: State = {};
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { eventBus } = this.props;
|
||||||
|
|
||||||
|
this.subscription.add(
|
||||||
|
eventBus.subscribe(DataHoverEvent, (event) => {
|
||||||
|
this.setState({ event });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subscription.add(
|
||||||
|
eventBus.subscribe(DataHoverClearEvent, (event) => {
|
||||||
|
this.setState({ event });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subscription.add(
|
||||||
|
eventBus.subscribe(LegacyGraphHoverEvent, (event) => {
|
||||||
|
this.setState({ event });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.subscription.add(
|
||||||
|
eventBus.subscribe(LegacyGraphHoverClearEvent, (event) => {
|
||||||
|
this.setState({ event });
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.subscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { event } = this.state;
|
||||||
|
if (!event) {
|
||||||
|
return <div>no events yet</div>;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2>Origin: {(event.origin as any)?.path}</h2>
|
||||||
|
<span>Type: {event.type}</span>
|
||||||
|
<pre>{JSON.stringify(event.payload.point, null, ' ')}</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +1,22 @@
|
|||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { PanelProps } from '@grafana/data';
|
import { PanelProps } from '@grafana/data';
|
||||||
|
|
||||||
import { DebugPanelOptions, DebugMode, UpdateCounters } from './types';
|
import { DebugPanelOptions, DebugMode } from './types';
|
||||||
import { EventBusLoggerPanel } from './EventBusLogger';
|
import { EventBusLoggerPanel } from './EventBusLogger';
|
||||||
import { RenderInfoViewer } from './RenderInfoViewer';
|
import { RenderInfoViewer } from './RenderInfoViewer';
|
||||||
|
import { CursorView } from './CursorView';
|
||||||
|
|
||||||
type Props = PanelProps<DebugPanelOptions>;
|
type Props = PanelProps<DebugPanelOptions>;
|
||||||
|
|
||||||
export class DebugPanel extends Component<Props> {
|
export class DebugPanel extends Component<Props> {
|
||||||
// Intentionally not state to avoid overhead -- yes, things will be 1 tick behind
|
|
||||||
lastRender = Date.now();
|
|
||||||
counters: UpdateCounters = {
|
|
||||||
render: 0,
|
|
||||||
dataChanged: 0,
|
|
||||||
schemaChanged: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
shouldComponentUpdate(prevProps: Props) {
|
|
||||||
const { data, options } = this.props;
|
|
||||||
|
|
||||||
if (prevProps.data !== data) {
|
|
||||||
this.counters.dataChanged++;
|
|
||||||
|
|
||||||
if (options.counters?.schemaChanged) {
|
|
||||||
if (data.structureRev !== prevProps.data.structureRev) {
|
|
||||||
this.counters.schemaChanged++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; // always render?
|
|
||||||
}
|
|
||||||
|
|
||||||
resetCounters = () => {
|
|
||||||
this.counters = {
|
|
||||||
render: 0,
|
|
||||||
dataChanged: 0,
|
|
||||||
schemaChanged: 0,
|
|
||||||
};
|
|
||||||
this.setState(this.state); // force update
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { options } = this.props;
|
const { options } = this.props;
|
||||||
if (options.mode === DebugMode.Events) {
|
if (options.mode === DebugMode.Events) {
|
||||||
return <EventBusLoggerPanel eventBus={this.props.eventBus} />;
|
return <EventBusLoggerPanel eventBus={this.props.eventBus} />;
|
||||||
}
|
}
|
||||||
|
if (options.mode === DebugMode.Cursor) {
|
||||||
|
return <CursorView eventBus={this.props.eventBus} />;
|
||||||
|
}
|
||||||
|
|
||||||
return <RenderInfoViewer {...this.props} />;
|
return <RenderInfoViewer {...this.props} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ export const plugin = new PanelPlugin<DebugPanelOptions>(DebugPanel).useFieldCon
|
|||||||
options: [
|
options: [
|
||||||
{ label: 'Render', value: DebugMode.Render },
|
{ label: 'Render', value: DebugMode.Render },
|
||||||
{ label: 'Events', value: DebugMode.Events },
|
{ label: 'Events', value: DebugMode.Events },
|
||||||
|
{ label: 'Cursor', value: DebugMode.Cursor },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ export type UpdateCounters = {
|
|||||||
export enum DebugMode {
|
export enum DebugMode {
|
||||||
Render = 'render',
|
Render = 'render',
|
||||||
Events = 'events',
|
Events = 'events',
|
||||||
|
Cursor = 'cursor',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DebugPanelOptions {
|
export interface DebugPanelOptions {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
|
|||||||
const self = this;
|
const self = this;
|
||||||
const ctrl = scope.ctrl;
|
const ctrl = scope.ctrl;
|
||||||
const panel = ctrl.panel;
|
const panel = ctrl.panel;
|
||||||
|
const hoverEvent = new LegacyGraphHoverEvent({ pos: {}, point: {}, panel: this.panel });
|
||||||
|
|
||||||
const $tooltip = $('<div class="graph-tooltip">');
|
const $tooltip = $('<div class="graph-tooltip">');
|
||||||
|
|
||||||
@@ -159,7 +160,10 @@ export default function GraphTooltip(this: any, elem: any, dashboard: any, scope
|
|||||||
|
|
||||||
// broadcast to other graph panels that we are hovering!
|
// broadcast to other graph panels that we are hovering!
|
||||||
pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height();
|
pos.panelRelY = (pos.pageY - elem.offset().top) / elem.height();
|
||||||
dashboard.events.publish(new LegacyGraphHoverEvent({ pos: pos, panel: panel }));
|
hoverEvent.payload.pos = pos;
|
||||||
|
hoverEvent.payload.panel = panel;
|
||||||
|
hoverEvent.payload.point['time'] = (pos as any).x;
|
||||||
|
dashboard.events.publish(hoverEvent);
|
||||||
});
|
});
|
||||||
|
|
||||||
elem.bind('plotclick', (event: any, pos: any, item: any) => {
|
elem.bind('plotclick', (event: any, pos: any, item: any) => {
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ export class HeatmapRenderer {
|
|||||||
margin: any;
|
margin: any;
|
||||||
dataRangeWidingFactor: number;
|
dataRangeWidingFactor: number;
|
||||||
|
|
||||||
|
hoverEvent: LegacyGraphHoverEvent;
|
||||||
|
|
||||||
constructor(private scope: any, private elem: any, attrs: any, private ctrl: any) {
|
constructor(private scope: any, private elem: any, attrs: any, private ctrl: any) {
|
||||||
// $heatmap is JQuery object, but heatmap is D3
|
// $heatmap is JQuery object, but heatmap is D3
|
||||||
this.$heatmap = this.elem.find('.heatmap-panel');
|
this.$heatmap = this.elem.find('.heatmap-panel');
|
||||||
@@ -91,6 +93,8 @@ export class HeatmapRenderer {
|
|||||||
this.$heatmap.on('mousedown', this.onMouseDown.bind(this));
|
this.$heatmap.on('mousedown', this.onMouseDown.bind(this));
|
||||||
this.$heatmap.on('mousemove', this.onMouseMove.bind(this));
|
this.$heatmap.on('mousemove', this.onMouseMove.bind(this));
|
||||||
this.$heatmap.on('mouseleave', this.onMouseLeave.bind(this));
|
this.$heatmap.on('mouseleave', this.onMouseLeave.bind(this));
|
||||||
|
|
||||||
|
this.hoverEvent = new LegacyGraphHoverEvent({ pos: {}, point: {}, panel: this.panel });
|
||||||
}
|
}
|
||||||
|
|
||||||
onGraphHoverClear() {
|
onGraphHoverClear() {
|
||||||
@@ -740,7 +744,10 @@ export class HeatmapRenderer {
|
|||||||
// Set minimum offset to prevent showing legend from another panel
|
// Set minimum offset to prevent showing legend from another panel
|
||||||
pos.panelRelY = Math.max(pos.offset.y / this.height, 0.001);
|
pos.panelRelY = Math.max(pos.offset.y / this.height, 0.001);
|
||||||
// broadcast to other graph panels that we are hovering
|
// broadcast to other graph panels that we are hovering
|
||||||
this.ctrl.dashboard.events.publish(new LegacyGraphHoverEvent({ pos: pos, panel: this.panel }));
|
this.hoverEvent.payload.pos = pos;
|
||||||
|
this.hoverEvent.payload.panel = this.panel;
|
||||||
|
this.hoverEvent.payload.point['time'] = (pos as any).x;
|
||||||
|
this.ctrl.dashboard.events.publish(this.hoverEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
limitSelection(x2: number) {
|
limitSelection(x2: number) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Field, PanelProps } from '@grafana/data';
|
import { DashboardCursorSync, Field, PanelProps } from '@grafana/data';
|
||||||
import { TimeSeries, TooltipPlugin, ZoomPlugin } from '@grafana/ui';
|
import { TooltipDisplayMode, usePanelContext, TimeSeries, TooltipPlugin, ZoomPlugin } from '@grafana/ui';
|
||||||
import { getFieldLinksForExplore } from 'app/features/explore/utils/links';
|
import { getFieldLinksForExplore } from 'app/features/explore/utils/links';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { AnnotationsPlugin } from './plugins/AnnotationsPlugin';
|
import { AnnotationsPlugin } from './plugins/AnnotationsPlugin';
|
||||||
@@ -11,6 +11,7 @@ interface TimeSeriesPanelProps extends PanelProps<Options> {}
|
|||||||
|
|
||||||
export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
||||||
data,
|
data,
|
||||||
|
id,
|
||||||
timeRange,
|
timeRange,
|
||||||
timeZone,
|
timeZone,
|
||||||
width,
|
width,
|
||||||
@@ -23,6 +24,8 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
|||||||
return getFieldLinksForExplore({ field, rowIndex, range: timeRange });
|
return getFieldLinksForExplore({ field, rowIndex, range: timeRange });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { sync } = usePanelContext();
|
||||||
|
|
||||||
if (!data || !data.series?.length) {
|
if (!data || !data.series?.length) {
|
||||||
return (
|
return (
|
||||||
<div className="panel-empty">
|
<div className="panel-empty">
|
||||||
@@ -48,7 +51,7 @@ export const TimeSeriesPanel: React.FC<TimeSeriesPanelProps> = ({
|
|||||||
<TooltipPlugin
|
<TooltipPlugin
|
||||||
data={alignedDataFrame}
|
data={alignedDataFrame}
|
||||||
config={config}
|
config={config}
|
||||||
mode={options.tooltipOptions.mode}
|
mode={sync === DashboardCursorSync.Tooltip ? TooltipDisplayMode.Multi : options.tooltipOptions.mode}
|
||||||
timeZone={timeZone}
|
timeZone={timeZone}
|
||||||
/>
|
/>
|
||||||
<ContextMenuPlugin
|
<ContextMenuPlugin
|
||||||
|
|||||||
Reference in New Issue
Block a user