From 7c175e019c865b694e43d515574bcf3dd89c0575 Mon Sep 17 00:00:00 2001 From: Leon Sorokin Date: Wed, 15 Jun 2022 17:24:51 -0500 Subject: [PATCH] HeatmapNG: support y min/max config (#50900) Co-authored-by: Ryan McKinley --- .../app/plugins/panel/heatmap-new/module.tsx | 15 ++++-- public/app/plugins/panel/heatmap-new/utils.ts | 46 +++++++++++++++++-- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/public/app/plugins/panel/heatmap-new/module.tsx b/public/app/plugins/panel/heatmap-new/module.tsx index dec0077e66f..5a4e225eafa 100644 --- a/public/app/plugins/panel/heatmap-new/module.tsx +++ b/public/app/plugins/panel/heatmap-new/module.tsx @@ -1,14 +1,16 @@ import React from 'react'; -import { FieldConfigProperty, FieldType, identityOverrideProcessor, PanelPlugin } from '@grafana/data'; +import { FieldConfigProperty, FieldType, identityOverrideProcessor, PanelData, PanelPlugin } from '@grafana/data'; import { config } from '@grafana/runtime'; import { AxisPlacement, GraphFieldConfig, ScaleDistribution, ScaleDistributionConfig } from '@grafana/schema'; import { addHideFrom, ScaleDistributionEditor } from '@grafana/ui/src/options/builder'; import { ColorScale } from 'app/core/components/ColorScale/ColorScale'; import { addHeatmapCalculationOptions } from 'app/features/transformers/calculateHeatmap/editor/helper'; +import { readHeatmapScanlinesCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; import { HeatmapCellLayout } from 'app/features/transformers/calculateHeatmap/models.gen'; import { HeatmapPanel } from './HeatmapPanel'; +import { prepareHeatmapData } from './fields'; import { heatmapChangedHandler, heatmapMigrationHandler } from './migrations'; import { PanelOptions, defaultPanelOptions, HeatmapColorMode, HeatmapColorScale } from './models.gen'; import { colorSchemes, quantizeScheme } from './palettes'; @@ -46,6 +48,13 @@ export const plugin = new PanelPlugin(HeatmapPan .setPanelOptions((builder, context) => { const opts = context.options ?? defaultPanelOptions; + let isOrdinalY = false; + + try { + const v = prepareHeatmapData({ series: context.data } as PanelData, opts, config.theme2); + isOrdinalY = readHeatmapScanlinesCustomMeta(v.heatmap).yOrdinalDisplay != null; + } catch {} + let category = ['Heatmap']; builder.addRadio({ @@ -81,8 +90,8 @@ export const plugin = new PanelPlugin(HeatmapPan }, }); - // TODO: support clamping the min/max range when there is a real axis - if (false && opts.calculate) { + if (!isOrdinalY) { + // if undefined, then show the min+max builder .addNumberInput({ path: 'yAxis.min', diff --git a/public/app/plugins/panel/heatmap-new/utils.ts b/public/app/plugins/panel/heatmap-new/utils.ts index d9be050641e..546c15432fe 100644 --- a/public/app/plugins/panel/heatmap-new/utils.ts +++ b/public/app/plugins/panel/heatmap-new/utils.ts @@ -260,6 +260,7 @@ export function prepConfig(opts: PrepConfigOpts) { const yScale = yFieldConfig?.scaleDistribution ?? { type: ScaleDistribution.Linear }; const yAxisReverse = Boolean(yAxisConfig.reverse); const shouldUseLogScale = yScale.type !== ScaleDistribution.Linear || heatmapType === DataFrameType.HeatmapSparse; + const isOrdianalY = readHeatmapScanlinesCustomMeta(dataRef.current?.heatmap).yOrdinalDisplay != null; // random to prevent syncing y in other heatmaps // TODO: try to match TimeSeries y keygen algo to sync with TimeSeries panels (when not isOrdianalY) @@ -275,11 +276,34 @@ export function prepConfig(opts: PrepConfigOpts) { distribution: shouldUseLogScale ? ScaleDistribution.Log : ScaleDistribution.Linear, log: yScale.log ?? 2, range: - // sparse already accounts for le/ge by explicit yMin & yMax cell bounds, so use default log ranging + // sparse already accounts for le/ge by explicit yMin & yMax cell bounds, so no need to expand y range heatmapType === DataFrameType.HeatmapSparse - ? undefined + ? (u, dataMin, dataMax) => { + let scaleMin: number | null, scaleMax: number | null; + + [scaleMin, scaleMax] = shouldUseLogScale + ? uPlot.rangeLog(dataMin, dataMax, (yScale.log ?? 2) as unknown as uPlot.Scale.LogBase, true) + : [dataMin, dataMax]; + + if (shouldUseLogScale && !isOrdianalY) { + let { min: explicitMin, max: explicitMax } = yAxisConfig; + + // guard against <= 0 + if (explicitMin != null && explicitMin > 0) { + scaleMin = explicitMin; + } + + if (explicitMax != null && explicitMax > 0) { + scaleMax = explicitMax; + } + } + + return [scaleMin, scaleMax]; + } : // dense and ordinal only have one of yMin|yMax|y, so expand range by one cell in the direction of le/ge/unknown (u, dataMin, dataMax) => { + let { min: explicitMin, max: explicitMax } = yAxisConfig; + // logarithmic expansion if (shouldUseLogScale) { let yExp = u.scales[yScaleKey].log!; @@ -316,6 +340,17 @@ export function prepConfig(opts: PrepConfigOpts) { dataMin /= yExp / 2; dataMax *= yExp / 2; } + + if (!isOrdianalY) { + // guard against <= 0 + if (explicitMin != null && explicitMin > 0) { + dataMin = explicitMin; + } + + if (explicitMax != null && explicitMax > 0) { + dataMax = explicitMax; + } + } } // linear expansion else { @@ -337,12 +372,17 @@ export function prepConfig(opts: PrepConfigOpts) { } else { // how to expand scale range if inferred non-regular or log buckets? } + + if (!isOrdianalY) { + dataMin = explicitMin ?? dataMin; + dataMax = explicitMax ?? dataMax; + } } + return [dataMin, dataMax]; }, }); - const isOrdianalY = readHeatmapScanlinesCustomMeta(dataRef.current?.heatmap).yOrdinalDisplay != null; const disp = dataRef.current?.heatmap?.fields[1].display ?? getValueFormat('short'); builder.addAxis({