Heatmap: Support heatmap rows with non-timeseries X axis (#60929)

Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
Ryan McKinley
2023-02-01 15:16:34 -08:00
committed by GitHub
parent d4f4a83574
commit 4a8763d7b6
5 changed files with 385 additions and 20 deletions

View File

@@ -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,

View File

@@ -237,7 +237,7 @@ const HeatmapHoverCell = ({ data, hover, showHistogram }: Props) => {
<>
<div>
<div>{xDisp(xBucketMin)}</div>
<div>{xDisp(xBucketMax)}</div>
{data.xLayout !== HeatmapCellLayout.unknown && <div>{xDisp(xBucketMax)}</div>}
</div>
{showHistogram && (
<canvas

View File

@@ -14,6 +14,7 @@ import {
incrRoundDn,
incrRoundUp,
TimeRange,
FieldType,
} from '@grafana/data';
import { AxisPlacement, ScaleDirection, ScaleDistribution, ScaleOrientation } from '@grafana/schema';
import { UPlotConfigBuilder } from '@grafana/ui';
@@ -98,7 +99,13 @@ export function prepConfig(opts: PrepConfigOpts) {
} = opts;
const xScaleKey = 'x';
const xScaleUnit = 'time';
let xScaleUnit = 'time';
let isTime = true;
if (dataRef.current?.heatmap?.fields[0].type !== FieldType.time) {
xScaleUnit = dataRef.current?.heatmap?.fields[0].config?.unit ?? 'x';
isTime = false;
}
const pxRatio = devicePixelRatio;
@@ -145,22 +152,24 @@ export function prepConfig(opts: PrepConfigOpts) {
u.setSelect({ left: 0, top: 0, width: 0, height: 0 }, false);
});
// 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);
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,
});