mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Heatmap: use y axis settings for units (#50998)
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
parent
86b785d039
commit
caa92320d7
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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}`;
|
||||
|
||||
|
@ -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<HeatmapPanelProps> = ({
|
||||
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();
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -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<PanelOptions> => {
|
||||
// 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<PanelOptions> => {
|
||||
// Migrating from angular
|
||||
if (!panel.pluginVersion && Object.keys(panel.options).length === 0) {
|
||||
return heatmapChangedHandler(panel, 'heatmap', { angular: panel }, panel.fieldConfig);
|
||||
}
|
||||
return panel.options;
|
||||
};
|
||||
|
@ -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: {
|
||||
|
@ -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<PanelOptions, GraphFieldConfig>(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<void, ScaleDistributionConfig>({
|
||||
id: 'scaleDistribution',
|
||||
@ -52,7 +44,7 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(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<PanelOptions, GraphFieldConfig>(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<PanelOptions, GraphFieldConfig>(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<PanelOptions, GraphFieldConfig>(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)',
|
||||
|
@ -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,
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user