From 0413fea8d24d7155d39004ceb4623f9d2ca6035f Mon Sep 17 00:00:00 2001 From: "Grot (@grafanabot)" <43478413+grafanabot@users.noreply.github.com> Date: Thu, 2 Feb 2023 06:27:09 -0500 Subject: [PATCH] [v9.4.x] Heatmap: Support heatmap rows with non-timeseries X axis (#62733) Heatmap: Support heatmap rows with non-timeseries X axis (#60929) Co-authored-by: Leon Sorokin (cherry picked from commit 4a8763d7b666ba2856bd29220e6887f3e6047f91) Co-authored-by: Ryan McKinley --- .../panel-heatmap/heatmap-x.json | 326 ++++++++++++++++++ devenv/jsonnet/dev-dashboards.libsonnet | 7 + .../transformers/calculateHeatmap/heatmap.ts | 2 +- .../panel/heatmap/HeatmapHoverView.tsx | 2 +- public/app/plugins/panel/heatmap/utils.ts | 68 +++- 5 files changed, 385 insertions(+), 20 deletions(-) create mode 100644 devenv/dev-dashboards/panel-heatmap/heatmap-x.json diff --git a/devenv/dev-dashboards/panel-heatmap/heatmap-x.json b/devenv/dev-dashboards/panel-heatmap/heatmap-x.json new file mode 100644 index 00000000000..7c9260f0017 --- /dev/null +++ b/devenv/dev-dashboards/panel-heatmap/heatmap-x.json @@ -0,0 +1,326 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 116, + "links": [], + "liveNow": false, + "panels": [ + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 10, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "x,y1,y2\n1,8,12\n2,6,13\n3,7,9\n5,9,7\n6,5,9", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Raw heatmap rows", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 14, + "x": 10, + "y": 0 + }, + "id": 4, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "show": true, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 2, + "refId": "A" + } + ], + "title": "Row heatmap", + "type": "heatmap" + }, + { + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "custom": { + "align": "auto", + "displayMode": "auto", + "inspect": false + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 10, + "x": 0, + "y": 9 + }, + "id": 5, + "options": { + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "csvContent": "x,y,count\n1,4,10\n1,6,11\n2,5,30\n2,4,22\n3,6,17", + "datasource": { + "type": "testdata", + "uid": "PD8C576611E62080A" + }, + "refId": "A", + "scenarioId": "csv_content" + } + ], + "title": "Raw heatmap cells", + "type": "table" + }, + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "fieldConfig": { + "defaults": { + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "scaleDistribution": { + "type": "linear" + } + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 14, + "x": 10, + "y": 9 + }, + "id": 6, + "options": { + "calculate": false, + "cellGap": 1, + "color": { + "exponent": 0.5, + "fill": "dark-orange", + "mode": "scheme", + "reverse": false, + "scale": "exponential", + "scheme": "Oranges", + "steps": 64 + }, + "exemplars": { + "color": "rgba(255,0,255,0.7)" + }, + "filterValues": { + "le": 1e-9 + }, + "legend": { + "show": true + }, + "rowsFrame": { + "layout": "auto" + }, + "tooltip": { + "show": true, + "yHistogram": false + }, + "yAxis": { + "axisPlacement": "left", + "reverse": false + } + }, + "pluginVersion": "9.4.0-pre", + "targets": [ + { + "datasource": { + "type": "datasource", + "uid": "-- Dashboard --" + }, + "panelId": 5, + "refId": "A" + } + ], + "title": "Cells heatmap", + "type": "heatmap" + } + ], + "revision": 1, + "schemaVersion": 37, + "style": "dark", + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Heatmap X axis", + "uid": "5Y0jv6pVz", + "version": 3, + "weekStart": "" + } + diff --git a/devenv/jsonnet/dev-dashboards.libsonnet b/devenv/jsonnet/dev-dashboards.libsonnet index 6591cc019ce..4434c26994d 100644 --- a/devenv/jsonnet/dev-dashboards.libsonnet +++ b/devenv/jsonnet/dev-dashboards.libsonnet @@ -303,6 +303,13 @@ local dashboard = grafana.dashboard; id: 0, } }, + dashboard.new('heatmap-x', import '../dev-dashboards/panel-heatmap/heatmap-x.json') + + resource.addMetadata('folder', 'dev-dashboards') + + { + spec+: { + id: 0, + } + }, dashboard.new('histogram_tests', import '../dev-dashboards/panel-histogram/histogram_tests.json') + resource.addMetadata('folder', 'dev-dashboards') + { diff --git a/public/app/features/transformers/calculateHeatmap/heatmap.ts b/public/app/features/transformers/calculateHeatmap/heatmap.ts index da20e34cedc..c9cb151c245 100644 --- a/public/app/features/transformers/calculateHeatmap/heatmap.ts +++ b/public/app/features/transformers/calculateHeatmap/heatmap.ts @@ -191,7 +191,7 @@ export function rowsToCellsHeatmap(opts: RowsHeatmapOptions): DataFrame { }, fields: [ { - name: 'xMax', + name: xField.type === FieldType.time ? 'xMax' : 'x', type: xField.type, values: new ArrayVector(xs), config: xField.config, diff --git a/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx b/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx index b0a3c5c0764..d0d0f19d179 100644 --- a/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx +++ b/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx @@ -237,7 +237,7 @@ const HeatmapHoverCell = ({ data, hover, showHistogram }: Props) => { <>
{xDisp(xBucketMin)}
-
{xDisp(xBucketMax)}
+ {data.xLayout !== HeatmapCellLayout.unknown &&
{xDisp(xBucketMax)}
}
{showHistogram && ( { - //let [min, max] = (u.scales!.x!.range! as uPlot.Range.Function)(u, 0, 100, xScaleKey); + if (isTime) { + // this is a tmp hack because in mode: 2, uplot does not currently call scales.x.range() for setData() calls + // scales.x.range() typically reads back from drilled-down panelProps.timeRange via getTimeRange() + builder.addHook('setData', (u) => { + //let [min, max] = (u.scales!.x!.range! as uPlot.Range.Function)(u, 0, 100, xScaleKey); - let { min: xMin, max: xMax } = u.scales!.x; + let { min: xMin, max: xMax } = u.scales!.x; - let min = getTimeRange().from.valueOf(); - let max = getTimeRange().to.valueOf(); + let min = getTimeRange().from.valueOf(); + let max = getTimeRange().to.valueOf(); - if (xMin !== min || xMax !== max) { - queueMicrotask(() => { - u.setScale(xScaleKey, { min, max }); - }); - } - }); + if (xMin !== min || xMax !== max) { + queueMicrotask(() => { + u.setScale(xScaleKey, { min, max }); + }); + } + }); + } // rect of .u-over (grid area) builder.addHook('syncRect', (u, r) => { @@ -236,19 +245,42 @@ export function prepConfig(opts: PrepConfigOpts) { builder.addScale({ scaleKey: xScaleKey, - isTime: true, + isTime, orientation: ScaleOrientation.Horizontal, direction: ScaleDirection.Right, // TODO: expand by x bucket size and layout - range: () => { - return [getTimeRange().from.valueOf(), getTimeRange().to.valueOf()]; + range: (u, dataMin, dataMax) => { + if (isTime) { + return [getTimeRange().from.valueOf(), getTimeRange().to.valueOf()]; + } else { + if (dataRef.current?.xLayout === HeatmapCellLayout.le) { + return [dataMin - dataRef.current?.xBucketSize!, dataMax]; + } else if (dataRef.current?.xLayout === HeatmapCellLayout.ge) { + return [dataMin, dataMax + dataRef.current?.xBucketSize!]; + } else { + let offset = dataRef.current?.xBucketSize! / 2; + + return [dataMin - offset, dataMax + offset]; + } + } }, }); + let incrs; + + if (!isTime) { + incrs = []; + + for (let i = 0; i < 10; i++) { + incrs.push(i * dataRef.current?.xBucketSize!); + } + } + builder.addAxis({ scaleKey: xScaleKey, placement: AxisPlacement.Bottom, - isTime: true, + incrs, + isTime, theme: theme, timeZone, });