diff --git a/public/app/features/transformers/calculateHeatmap/heatmap.ts b/public/app/features/transformers/calculateHeatmap/heatmap.ts index 9d1a2a03b50..df157c60dd3 100644 --- a/public/app/features/transformers/calculateHeatmap/heatmap.ts +++ b/public/app/features/transformers/calculateHeatmap/heatmap.ts @@ -11,6 +11,8 @@ import { DataFrameType, getFieldDisplayName, Field, + getValueFormat, + formattedValueToString, } from '@grafana/data'; import { ScaleDistribution } from '@grafana/schema'; @@ -49,21 +51,24 @@ export function sortAscStrInf(aName?: string | null, bName?: string | null) { return parseNumeric(aName) - parseNumeric(bName); } -export interface HeatmapScanlinesCustomMeta { +export interface HeatmapRowsCustomMeta { /** This provides the lookup values */ yOrdinalDisplay: string[]; yOrdinalLabel?: string[]; yMatchWithLabel?: string; + yZeroDisplay?: string; } /** simple utility to get heatmap metadata from a frame */ -export function readHeatmapScanlinesCustomMeta(frame?: DataFrame): HeatmapScanlinesCustomMeta { - return (frame?.meta?.custom ?? {}) as HeatmapScanlinesCustomMeta; +export function readHeatmapRowsCustomMeta(frame?: DataFrame): HeatmapRowsCustomMeta { + return (frame?.meta?.custom ?? {}) as HeatmapRowsCustomMeta; } export interface RowsHeatmapOptions { frame: DataFrame; value?: string; // the field value name + unit?: string; + decimals?: number; layout?: HeatmapCellLayout; } @@ -117,13 +122,35 @@ export function rowsToCellsHeatmap(opts: RowsHeatmapOptions): DataFrame { break; } - const custom: HeatmapScanlinesCustomMeta = { + const custom: HeatmapRowsCustomMeta = { yOrdinalDisplay: yFields.map((f) => getFieldDisplayName(f, opts.frame)), yMatchWithLabel: Object.keys(yFields[0].labels ?? {})[0], }; if (custom.yMatchWithLabel) { custom.yOrdinalLabel = yFields.map((f) => f.labels?.[custom.yMatchWithLabel!] ?? ''); + if (custom.yMatchWithLabel === 'le') { + custom.yZeroDisplay = '0.0'; + } } + + // Format the labels as a value + // TODO: this leaves the internally prepended '0.0' without this formatting treatment + if (opts.unit?.length || opts.decimals != null) { + const fmt = getValueFormat(opts.unit ?? 'short'); + if (custom.yZeroDisplay) { + custom.yZeroDisplay = formattedValueToString(fmt(0, opts.decimals)); + } + custom.yOrdinalDisplay = custom.yOrdinalDisplay.map((name) => { + let num = +name; + + if (!Number.isNaN(num)) { + return formattedValueToString(fmt(num, opts.decimals)); + } + + return name; + }); + } + return { length: xs.length, refId: opts.frame.refId, @@ -293,9 +320,6 @@ export function calculateHeatmapFromData(frames: DataFrame[], options: HeatmapCa }; //console.timeEnd('calculateHeatmapFromData'); - - //console.log({ tiles: frame.length }); - return frame; } diff --git a/public/app/plugins/panel/heatmap-new/HeatmapHoverView.tsx b/public/app/plugins/panel/heatmap-new/HeatmapHoverView.tsx index 46207be7eb8..5efa2c5fd72 100644 --- a/public/app/plugins/panel/heatmap-new/HeatmapHoverView.tsx +++ b/public/app/plugins/panel/heatmap-new/HeatmapHoverView.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useRef } from 'react'; import { DataFrameType, Field, FieldType, formattedValueToString, getFieldDisplayName, LinkModel } from '@grafana/data'; import { LinkButton, VerticalGroup } from '@grafana/ui'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; -import { readHeatmapScanlinesCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; +import { readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; import { HeatmapCellLayout } from 'app/features/transformers/calculateHeatmap/models.gen'; import { DataHoverView } from '../geomap/components/DataHoverView'; @@ -47,7 +47,7 @@ const HeatmapHoverCell = ({ data, hover, showHistogram }: Props) => { const countVals = countField?.values.toArray(); // labeled buckets - const meta = readHeatmapScanlinesCustomMeta(data.heatmap); + const meta = readHeatmapRowsCustomMeta(data.heatmap); const yDispSrc = meta.yOrdinalDisplay ?? yVals; const yDisp = yField?.display ? (v: any) => formattedValueToString(yField.display!(v)) : (v: any) => `${v}`; diff --git a/public/app/plugins/panel/heatmap-new/HeatmapPanel.tsx b/public/app/plugins/panel/heatmap-new/HeatmapPanel.tsx index 3f5c8aa1bda..6cafe6d6db1 100644 --- a/public/app/plugins/panel/heatmap-new/HeatmapPanel.tsx +++ b/public/app/plugins/panel/heatmap-new/HeatmapPanel.tsx @@ -16,7 +16,7 @@ import { } from '@grafana/ui'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; import { ColorScale } from 'app/core/components/ColorScale/ColorScale'; -import { readHeatmapScanlinesCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; +import { readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; import { HeatmapHoverView } from './HeatmapHoverView'; import { prepareHeatmapData } from './fields'; @@ -59,7 +59,7 @@ export const HeatmapPanel: React.FC = ({ let exemplarsXFacet: number[] = []; // "Time" field let exemplarsyFacet: number[] = []; - const meta = readHeatmapScanlinesCustomMeta(info.heatmap); + const meta = readHeatmapRowsCustomMeta(info.heatmap); if (info.exemplars?.length && meta.yMatchWithLabel) { exemplarsXFacet = info.exemplars?.fields[0].values.toArray(); diff --git a/public/app/plugins/panel/heatmap-new/fields.ts b/public/app/plugins/panel/heatmap-new/fields.ts index 9234d544061..2e50f4e022c 100644 --- a/public/app/plugins/panel/heatmap-new/fields.ts +++ b/public/app/plugins/panel/heatmap-new/fields.ts @@ -1,17 +1,23 @@ import { DataFrame, DataFrameType, + Field, + FieldType, formattedValueToString, getDisplayProcessor, - getValueFormat, GrafanaTheme2, outerJoinDataFrames, PanelData, + ValueFormatter, } from '@grafana/data'; -import { calculateHeatmapFromData, rowsToCellsHeatmap } from 'app/features/transformers/calculateHeatmap/heatmap'; +import { + calculateHeatmapFromData, + readHeatmapRowsCustomMeta, + rowsToCellsHeatmap, +} from 'app/features/transformers/calculateHeatmap/heatmap'; import { HeatmapCellLayout } from 'app/features/transformers/calculateHeatmap/models.gen'; -import { PanelOptions } from './models.gen'; +import { CellValues, PanelOptions } from './models.gen'; export interface HeatmapData { heatmap?: DataFrame; // data we will render @@ -43,8 +49,7 @@ export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme const exemplars = data.annotations?.find((f) => f.name === 'exemplar'); if (options.calculate) { - // TODO, check for error etc - return getHeatmapData(calculateHeatmapFromData(frames, options.calculation ?? {}), exemplars, theme); + return getHeatmapData(calculateHeatmapFromData(frames, options.calculation ?? {}), exemplars, options, theme); } // Check for known heatmap types @@ -52,10 +57,10 @@ export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme for (const frame of frames) { switch (frame.meta?.type) { case DataFrameType.HeatmapSparse: - return getSparseHeatmapData(frame, exemplars, theme); + return getSparseHeatmapData(frame, exemplars, options, theme); case DataFrameType.HeatmapCells: - return getHeatmapData(frame, exemplars, theme); + return getHeatmapData(frame, exemplars, options, theme); case DataFrameType.HeatmapRows: rowsHeatmap = frame; // the default format @@ -75,12 +80,23 @@ export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme } } - return getHeatmapData(rowsToCellsHeatmap({ ...options.rowsFrame, frame: rowsHeatmap }), exemplars, theme); + return getHeatmapData( + rowsToCellsHeatmap({ + unit: options.yAxis?.unit, // used to format the ordinal lookup values + decimals: options.yAxis?.decimals, + ...options.rowsFrame, + frame: rowsHeatmap, + }), + exemplars, + options, + theme + ); } const getSparseHeatmapData = ( frame: DataFrame, exemplars: DataFrame | undefined, + options: PanelOptions, theme: GrafanaTheme2 ): HeatmapData => { if (frame.meta?.type !== DataFrameType.HeatmapSparse) { @@ -90,7 +106,12 @@ const getSparseHeatmapData = ( }; } - const disp = frame.fields[3].display ?? getValueFormat('short'); + // y axis tick label display + updateFieldDisplay(frame.fields[1], options.yAxis, theme); + + // cell value display + const disp = updateFieldDisplay(frame.fields[3], options.cellValues, theme); + return { heatmap: frame, exemplars, @@ -98,7 +119,12 @@ const getSparseHeatmapData = ( }; }; -const getHeatmapData = (frame: DataFrame, exemplars: DataFrame | undefined, theme: GrafanaTheme2): HeatmapData => { +const getHeatmapData = ( + frame: DataFrame, + exemplars: DataFrame | undefined, + options: PanelOptions, + theme: GrafanaTheme2 +): HeatmapData => { if (frame.meta?.type !== DataFrameType.HeatmapCells) { return { warning: 'Expected heatmap scanlines format', @@ -110,11 +136,54 @@ const getHeatmapData = (frame: DataFrame, exemplars: DataFrame | undefined, them return { heatmap: frame }; } - // Y field values (display is used in the axis) - if (!frame.fields[1].display) { - frame.fields[1].display = getDisplayProcessor({ field: frame.fields[1], theme }); + const meta = readHeatmapRowsCustomMeta(frame); + let xName: string | undefined = undefined; + let yName: string | undefined = undefined; + let valueField: Field | undefined = undefined; + + // validate field display properties + for (const field of frame.fields) { + switch (field.name) { + case 'y': + yName = field.name; + + case 'yMin': + case 'yMax': { + if (!yName) { + yName = field.name; + } + if (meta.yOrdinalDisplay == null) { + updateFieldDisplay(field, options.yAxis, theme); + } + break; + } + + case 'x': + case 'xMin': + case 'xMax': + xName = field.name; + break; + + default: { + if (field.type === FieldType.number && !valueField) { + valueField = field; + } + } + } } + if (!yName) { + return { warning: 'Missing Y field', heatmap: frame }; + } + if (!yName) { + return { warning: 'Missing X field', heatmap: frame }; + } + if (!valueField) { + return { warning: 'Missing value field', heatmap: frame }; + } + + const disp = updateFieldDisplay(valueField, options.cellValues, theme); + // infer bucket sizes from data (for now) // the 'heatmap-scanlines' dense frame format looks like: // x: 1,1,1,1,2,2,2,2 @@ -132,11 +201,6 @@ const getHeatmapData = (frame: DataFrame, exemplars: DataFrame | undefined, them let yBinIncr = ys[1] - ys[0]; let xBinIncr = xs[yBinQty] - xs[0]; - // The "count" field - const disp = frame.fields[2].display ?? getValueFormat('short'); - const xName = frame.fields[0].name; - const yName = frame.fields[1].name; - const data: HeatmapData = { heatmap: frame, exemplars: exemplars?.length ? exemplars : undefined, @@ -156,3 +220,21 @@ const getHeatmapData = (frame: DataFrame, exemplars: DataFrame | undefined, them return data; }; + +function updateFieldDisplay(field: Field, opts: CellValues | undefined, theme: GrafanaTheme2): ValueFormatter { + if (opts?.unit?.length || opts?.decimals != null) { + const { unit, decimals } = opts; + field.display = undefined; + field.config = { ...field.config }; + if (unit?.length) { + field.config.unit = unit; + } + if (decimals != null) { + field.config.decimals = decimals; + } + } + if (!field.display) { + field.display = getDisplayProcessor({ field, theme }); + } + return field.display; +} diff --git a/public/app/plugins/panel/heatmap-new/migrations.test.ts b/public/app/plugins/panel/heatmap-new/migrations.test.ts index fae97bea3ce..44f92e3851a 100644 --- a/public/app/plugins/panel/heatmap-new/migrations.test.ts +++ b/public/app/plugins/panel/heatmap-new/migrations.test.ts @@ -21,10 +21,7 @@ describe('Heatmap Migrations', () => { expect(panel).toMatchInlineSnapshot(` Object { "fieldConfig": Object { - "defaults": Object { - "decimals": 6, - "unit": "short", - }, + "defaults": Object {}, "overrides": Array [], }, "options": Object { @@ -45,6 +42,9 @@ describe('Heatmap Migrations', () => { }, "cellGap": 2, "cellRadius": 10, + "cellValues": Object { + "decimals": undefined, + }, "color": Object { "exponent": 0.5, "fill": "dark-orange", @@ -75,9 +75,11 @@ describe('Heatmap Migrations', () => { "yAxis": Object { "axisPlacement": "left", "axisWidth": 400, + "decimals": 6, "max": 22, "min": 7, "reverse": false, + "unit": "short", }, }, } diff --git a/public/app/plugins/panel/heatmap-new/migrations.ts b/public/app/plugins/panel/heatmap-new/migrations.ts index 40cd601a67d..915a4dfe010 100644 --- a/public/app/plugins/panel/heatmap-new/migrations.ts +++ b/public/app/plugins/panel/heatmap-new/migrations.ts @@ -9,6 +9,15 @@ import { import { PanelOptions, defaultPanelOptions, HeatmapColorMode } from './models.gen'; import { colorSchemes } from './palettes'; +/** Called when the version number changes */ +export const heatmapMigrationHandler = (panel: PanelModel): Partial => { + // Migrating from angular + if (Object.keys(panel.options).length === 0) { + return heatmapChangedHandler(panel, 'heatmap', { angular: panel }, panel.fieldConfig); + } + return panel.options; +}; + /** * This is called when the panel changes from another panel */ @@ -21,6 +30,14 @@ export const heatmapChangedHandler: PanelTypeChangedHandler = (panel, prevPlugin panel.fieldConfig = fieldConfig; // Mutates the incoming panel return options; } + // alpha for 8.5+, then beta at 9.0.1 + if (prevPluginId === 'heatmap-new') { + const { bucketFrame, ...options } = panel.options; + if (bucketFrame) { + return { ...options, rowsFrame: bucketFrame }; + } + return panel.options; + } return {}; }; @@ -60,9 +77,6 @@ export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigS }, }; } - - fieldConfig.defaults.unit = oldYAxis.format; - fieldConfig.defaults.decimals = oldYAxis.decimals; } const options: PanelOptions = { @@ -77,9 +91,14 @@ export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigS yAxis: { axisPlacement: oldYAxis.show === false ? AxisPlacement.Hidden : AxisPlacement.Left, reverse: Boolean(angular.reverseYBuckets), - axisWidth: oldYAxis.width ? +oldYAxis.width : undefined, + axisWidth: asNumber(oldYAxis.width), min: oldYAxis.min, max: oldYAxis.max, + unit: oldYAxis.format, + decimals: oldYAxis.decimals, + }, + cellValues: { + decimals: asNumber(angular.tooltipDecimals), }, rowsFrame: { layout: getHeatmapCellLayout(angular.yBucketBound), @@ -146,11 +165,3 @@ function asNumber(v: any, defaultValue?: number): number | undefined { const num = +v; return isNaN(num) ? defaultValue : num; } - -export const heatmapMigrationHandler = (panel: PanelModel): Partial => { - // Migrating from angular - if (!panel.pluginVersion && Object.keys(panel.options).length === 0) { - return heatmapChangedHandler(panel, 'heatmap', { angular: panel }, panel.fieldConfig); - } - return panel.options; -}; diff --git a/public/app/plugins/panel/heatmap-new/models.gen.ts b/public/app/plugins/panel/heatmap-new/models.gen.ts index dc5fdbe44bf..9b97a10fb75 100644 --- a/public/app/plugins/panel/heatmap-new/models.gen.ts +++ b/public/app/plugins/panel/heatmap-new/models.gen.ts @@ -39,6 +39,11 @@ export interface YAxisConfig extends AxisConfig { max?: number; } +export interface CellValues { + unit?: string; + decimals?: number; +} + export interface FilterValueRange { le?: number; ge?: number; @@ -72,8 +77,10 @@ export interface PanelOptions { cellGap?: number; // was cardPadding cellRadius?: number; // was cardRadius (not used, but migrated from angular) - + cellValues?: CellValues; + yAxis: YAxisConfig; + legend: HeatmapLegend; tooltip: HeatmapTooltip; @@ -95,6 +102,9 @@ export const defaultPanelOptions: PanelOptions = { }, yAxis: { axisPlacement: AxisPlacement.Left, + }, + cellValues: { + }, showValue: VisibilityMode.Auto, tooltip: { diff --git a/public/app/plugins/panel/heatmap-new/module.tsx b/public/app/plugins/panel/heatmap-new/module.tsx index 5a4e225eafa..7d4f81db27a 100644 --- a/public/app/plugins/panel/heatmap-new/module.tsx +++ b/public/app/plugins/panel/heatmap-new/module.tsx @@ -6,7 +6,7 @@ import { AxisPlacement, GraphFieldConfig, ScaleDistribution, ScaleDistributionCo 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 { readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; import { HeatmapCellLayout } from 'app/features/transformers/calculateHeatmap/models.gen'; import { HeatmapPanel } from './HeatmapPanel'; @@ -18,15 +18,7 @@ import { HeatmapSuggestionsSupplier } from './suggestions'; export const plugin = new PanelPlugin(HeatmapPanel) .useFieldConfig({ - // This keeps: unit, decimals, displayName - disableStandardOptions: [ - FieldConfigProperty.Color, - FieldConfigProperty.Thresholds, - FieldConfigProperty.Min, - FieldConfigProperty.Max, - FieldConfigProperty.Mappings, - FieldConfigProperty.NoValue, - ], + disableStandardOptions: Object.values(FieldConfigProperty).filter((v) => v !== FieldConfigProperty.Links), useCustomConfig: (builder) => { builder.addCustomEditor({ id: 'scaleDistribution', @@ -52,7 +44,7 @@ export const plugin = new PanelPlugin(HeatmapPan try { const v = prepareHeatmapData({ series: context.data } as PanelData, opts, config.theme2); - isOrdinalY = readHeatmapScanlinesCustomMeta(v.heatmap).yOrdinalDisplay != null; + isOrdinalY = readHeatmapRowsCustomMeta(v.heatmap).yOrdinalDisplay != null; } catch {} let category = ['Heatmap']; @@ -90,6 +82,22 @@ export const plugin = new PanelPlugin(HeatmapPan }, }); + builder + .addUnitPicker({ + category, + path: 'yAxis.unit', + name: 'Unit', + defaultValue: undefined, + }) + .addNumberInput({ + category, + path: 'yAxis.decimals', + name: 'Decimals', + settings: { + placeholder: 'Auto', + }, + }); + if (!isOrdinalY) { // if undefined, then show the min+max builder @@ -269,7 +277,35 @@ export const plugin = new PanelPlugin(HeatmapPan category, }); - category = ['Display']; + category = ['Cell display']; + + if (!opts.calculate) { + builder.addTextInput({ + path: 'rowsFrame.value', + name: 'Value name', + defaultValue: defaultPanelOptions.rowsFrame?.value, + settings: { + placeholder: 'Value', + }, + category, + }); + } + + builder + .addUnitPicker({ + category, + path: 'cellValues.unit', + name: 'Unit', + defaultValue: undefined, + }) + .addNumberInput({ + category, + path: 'cellValues.decimals', + name: 'Decimals', + settings: { + placeholder: 'Auto', + }, + }); builder // .addRadio({ @@ -333,18 +369,6 @@ export const plugin = new PanelPlugin(HeatmapPan category, }); - if (!opts.calculate) { - builder.addTextInput({ - path: 'rowsFrame.value', - name: 'Cell value name', - defaultValue: defaultPanelOptions.rowsFrame?.value, - settings: { - placeholder: 'Value', - }, - category, - }); - } - builder.addBooleanSwitch({ path: 'tooltip.yHistogram', name: 'Show histogram (Y axis)', diff --git a/public/app/plugins/panel/heatmap-new/utils.ts b/public/app/plugins/panel/heatmap-new/utils.ts index 546c15432fe..9132037e1c4 100644 --- a/public/app/plugins/panel/heatmap-new/utils.ts +++ b/public/app/plugins/panel/heatmap-new/utils.ts @@ -17,7 +17,7 @@ import { } from '@grafana/data'; import { AxisPlacement, ScaleDirection, ScaleDistribution, ScaleOrientation } from '@grafana/schema'; import { UPlotConfigBuilder } from '@grafana/ui'; -import { readHeatmapScanlinesCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; +import { readHeatmapRowsCustomMeta } from 'app/features/transformers/calculateHeatmap/heatmap'; import { HeatmapCellLayout } from 'app/features/transformers/calculateHeatmap/models.gen'; import { pointWithin, Quadtree, Rect } from '../barchart/quadtree'; @@ -256,11 +256,16 @@ export function prepConfig(opts: PrepConfigOpts) { theme: theme, }); - const yFieldConfig = dataRef.current?.heatmap?.fields[1]?.config?.custom as PanelFieldConfig | undefined; + const yField = dataRef.current?.heatmap?.fields[1]!; + if (!yField) { + return builder; // early abort (avoids error) + } + + const yFieldConfig = yField.config?.custom as PanelFieldConfig | undefined; 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; + const isOrdianalY = readHeatmapRowsCustomMeta(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) @@ -383,7 +388,7 @@ export function prepConfig(opts: PrepConfigOpts) { }, }); - const disp = dataRef.current?.heatmap?.fields[1].display ?? getValueFormat('short'); + const dispY = yField.display ?? getValueFormat('short'); builder.addAxis({ scaleKey: yScaleKey, @@ -392,10 +397,10 @@ export function prepConfig(opts: PrepConfigOpts) { size: yAxisConfig.axisWidth || null, label: yAxisConfig.axisLabel, theme: theme, - formatValue: (v: any) => formattedValueToString(disp(v)), + formatValue: (v: any) => formattedValueToString(dispY(v)), splits: isOrdianalY ? (self: uPlot) => { - const meta = readHeatmapScanlinesCustomMeta(dataRef.current?.heatmap); + const meta = readHeatmapRowsCustomMeta(dataRef.current?.heatmap); if (!meta.yOrdinalDisplay) { return [0, 1]; //? } @@ -423,20 +428,15 @@ export function prepConfig(opts: PrepConfigOpts) { : undefined, values: isOrdianalY ? (self: uPlot, splits) => { - const meta = readHeatmapScanlinesCustomMeta(dataRef.current?.heatmap); + const meta = readHeatmapRowsCustomMeta(dataRef.current?.heatmap); if (meta.yOrdinalDisplay) { - return splits.map((v) => { - const txt = meta.yOrdinalDisplay[v]; - if (!txt && v < 0) { - // Check prometheus style labels - if ('le' === meta.yMatchWithLabel) { - return '0.0'; - } - } - return txt; - }); + return splits.map((v) => + v < 0 + ? meta.yZeroDisplay ?? '' // Check prometheus style labels + : meta.yOrdinalDisplay[v] ?? '' + ); } - return splits.map((v) => `${v}`); + return splits; } : undefined, });