Heatmap: improve new heatmap options (#49201)

This commit is contained in:
Ryan McKinley 2022-05-18 16:54:32 -07:00 committed by GitHub
parent 2053d37d7a
commit d0d4d8af7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 64 deletions

View File

@ -190,6 +190,10 @@ export function calculateHeatmapFromData(frames: DataFrame[], options: HeatmapCa
throw 'no heatmap fields found';
}
if (!xs.length || !ys.length) {
throw 'no values found';
}
const heat2d = heatmap(xs, ys, {
xSorted: true,
xTime: xField.type === FieldType.time,

View File

@ -3,15 +3,7 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
import { DataFrameType, GrafanaTheme2, PanelProps, reduceField, ReducerID, TimeRange } from '@grafana/data';
import { PanelDataErrorView } from '@grafana/runtime';
import {
Portal,
UPlotChart,
useStyles2,
useTheme2,
VizLayout,
VizTooltipContainer,
LegendDisplayMode,
} from '@grafana/ui';
import { Portal, UPlotChart, useStyles2, useTheme2, VizLayout, VizTooltipContainer } from '@grafana/ui';
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
@ -42,7 +34,13 @@ export const HeatmapPanel: React.FC<HeatmapPanelProps> = ({
let timeRangeRef = useRef<TimeRange>(timeRange);
timeRangeRef.current = timeRange;
const info = useMemo(() => prepareHeatmapData(data, options, theme), [data, options, theme]);
const info = useMemo(() => {
try {
return prepareHeatmapData(data, options, theme);
} catch (ex) {
return { warning: `${ex}` };
}
}, [data, options, theme]);
const facets = useMemo(() => [null, info.heatmap?.fields.map((f) => f.values.toArray())], [info.heatmap]);
@ -86,7 +84,10 @@ export const HeatmapPanel: React.FC<HeatmapPanelProps> = ({
onhover: onhover,
onclick: options.tooltip.show ? onclick : null,
onzoom: (evt) => {
onChangeTimeRange({ from: evt.xMin, to: evt.xMax });
const delta = evt.xMax - evt.xMin;
if (delta > 1) {
onChangeTimeRange({ from: evt.xMin, to: evt.xMax });
}
},
isToolTipOpen,
timeZone,
@ -99,7 +100,7 @@ export const HeatmapPanel: React.FC<HeatmapPanelProps> = ({
}, [options, data.structureRev]);
const renderLegend = () => {
if (options.legend.displayMode === LegendDisplayMode.Hidden || !info.heatmap) {
if (!info.heatmap || !options.legend.show) {
return null;
}

View File

@ -32,7 +32,7 @@ describe('Heatmap Migrations', () => {
"fill": "dark-orange",
"mode": "scheme",
"scale": "exponential",
"scheme": "Oranges",
"scheme": "BuGn",
"steps": 256,
},
"heatmap": Object {
@ -46,9 +46,7 @@ describe('Heatmap Migrations', () => {
},
},
"legend": Object {
"calcs": Array [],
"displayMode": "list",
"placement": "bottom",
"show": true,
},
"showValue": "never",
"source": "calculate",

View File

@ -1,11 +1,12 @@
import { FieldConfigSource, PanelModel, PanelTypeChangedHandler } from '@grafana/data';
import { LegendDisplayMode, VisibilityMode } from '@grafana/schema';
import { VisibilityMode } from '@grafana/schema';
import {
HeatmapCalculationMode,
HeatmapCalculationOptions,
} from 'app/features/transformers/calculateHeatmap/models.gen';
import { HeatmapSourceMode, PanelOptions, defaultPanelOptions } from './models.gen';
import { HeatmapSourceMode, PanelOptions, defaultPanelOptions, HeatmapColorMode } from './models.gen';
import { colorSchemes } from './palettes';
/**
* This is called when the panel changes from another panel
@ -59,9 +60,7 @@ export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigS
yAxisLabels: angular.yBucketBound,
yAxisReverse: angular.reverseYBuckets,
legend: {
displayMode: angular.legend.show ? LegendDisplayMode.List : LegendDisplayMode.Hidden,
calcs: [],
placement: 'bottom',
show: Boolean(angular.legend.show),
},
showValue: VisibilityMode.Never,
tooltip: {
@ -70,6 +69,27 @@ export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigS
},
};
// Migrate color options
const color = angular.color;
switch (color?.mode) {
case 'spectrum': {
options.color.mode = HeatmapColorMode.Scheme;
const current = color.colorScheme as string;
let scheme = colorSchemes.find((v) => v.name === current);
if (!scheme) {
scheme = colorSchemes.find((v) => current.indexOf(v.name) >= 0);
}
options.color.scheme = scheme ? scheme.name : defaultPanelOptions.color.scheme;
break;
}
case 'opacity': {
options.color.mode = HeatmapColorMode.Opacity;
options.color.scale = color.scale;
break;
}
}
return { fieldConfig, options };
}
@ -79,6 +99,9 @@ function asNumber(v: any): number | undefined {
}
export const heatmapMigrationHandler = (panel: PanelModel): Partial<PanelOptions> => {
// Nothing yet
// Migrating from angular
if (!panel.pluginVersion && Object.keys(panel.options).length === 0) {
return heatmapChangedHandler(panel, 'heatmap', { angular: panel }, panel.fieldConfig);
}
return panel.options;
};

View File

@ -3,7 +3,7 @@
// It is currenty hand written but will serve as the target for cuetsy
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import { HideableFieldConfig, LegendDisplayMode, OptionsWithLegend, VisibilityMode } from '@grafana/schema';
import { HideableFieldConfig, VisibilityMode } from '@grafana/schema';
import { HeatmapCalculationOptions } from 'app/features/transformers/calculateHeatmap/models.gen';
export const modelVersion = Object.freeze([1, 0]);
@ -42,8 +42,11 @@ export interface HeatmapTooltip {
show: boolean;
yHistogram?: boolean;
}
export interface HeatmapLegend {
show: boolean;
}
export interface PanelOptions extends OptionsWithLegend {
export interface PanelOptions {
source: HeatmapSourceMode;
color: HeatmapColorOptions;
@ -56,6 +59,7 @@ export interface PanelOptions extends OptionsWithLegend {
hideThreshold?: number; // was hideZeroBuckets
yAxisLabels?: string;
yAxisReverse?: boolean;
legend: HeatmapLegend;
tooltip: HeatmapTooltip;
}
@ -71,15 +75,13 @@ export const defaultPanelOptions: PanelOptions = {
steps: 64,
},
showValue: VisibilityMode.Auto,
legend: {
displayMode: LegendDisplayMode.Hidden,
placement: 'bottom',
calcs: [],
},
tooltip: {
show: true,
yHistogram: false,
},
legend: {
show: true,
},
cellGap: 1,
};

View File

@ -1,14 +1,13 @@
import React from 'react';
import { Field, FieldType, PanelPlugin } from '@grafana/data';
import { Field, FieldConfigProperty, FieldType, PanelPlugin } from '@grafana/data';
import { config } from '@grafana/runtime';
import { GraphFieldConfig, VisibilityMode } from '@grafana/schema';
import { commonOptionsBuilder } from '@grafana/ui';
import { GraphFieldConfig } from '@grafana/schema';
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
import { addHeatmapCalculationOptions } from 'app/features/transformers/calculateHeatmap/editor/helper';
import { HeatmapPanel } from './HeatmapPanel';
import { heatmapChangedHandler } from './migrations';
import { heatmapChangedHandler, heatmapMigrationHandler } from './migrations';
import {
PanelOptions,
defaultPanelOptions,
@ -20,9 +19,11 @@ import { colorSchemes, quantizeScheme } from './palettes';
import { HeatmapSuggestionsSupplier } from './suggestions';
export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPanel)
.useFieldConfig()
.useFieldConfig({
disableStandardOptions: [FieldConfigProperty.Color, FieldConfigProperty.Thresholds],
})
.setPanelChangeHandler(heatmapChangedHandler)
// .setMigrationHandler(heatmapMigrationHandler)
.setMigrationHandler(heatmapMigrationHandler)
.setPanelOptions((builder, context) => {
const opts = context.options ?? defaultPanelOptions;
@ -155,19 +156,19 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPan
category = ['Display'];
builder
.addRadio({
path: 'showValue',
name: 'Show values',
defaultValue: defaultPanelOptions.showValue,
category,
settings: {
options: [
{ value: VisibilityMode.Auto, label: 'Auto' },
{ value: VisibilityMode.Always, label: 'Always' },
{ value: VisibilityMode.Never, label: 'Never' },
],
},
})
// .addRadio({
// path: 'showValue',
// name: 'Show values',
// defaultValue: defaultPanelOptions.showValue,
// category,
// settings: {
// options: [
// { value: VisibilityMode.Auto, label: 'Auto' },
// { value: VisibilityMode.Always, label: 'Always' },
// { value: VisibilityMode.Never, label: 'Never' },
// ],
// },
// })
.addNumberInput({
path: 'hideThreshold',
name: 'Hide cell counts <=',
@ -194,20 +195,20 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPan
// max: 100,
// },
// })
.addRadio({
path: 'yAxisLabels',
name: 'Axis labels',
defaultValue: 'auto',
category,
settings: {
options: [
{ value: 'auto', label: 'Auto' },
{ value: 'middle', label: 'Middle' },
{ value: 'bottom', label: 'Bottom' },
{ value: 'top', label: 'Top' },
],
},
})
// .addRadio({
// path: 'yAxisLabels',
// name: 'Axis labels',
// defaultValue: 'auto',
// category,
// settings: {
// options: [
// { value: 'auto', label: 'Auto' },
// { value: 'middle', label: 'Middle' },
// { value: 'bottom', label: 'Bottom' },
// { value: 'top', label: 'Top' },
// ],
// },
// })
.addBooleanSwitch({
path: 'yAxisReverse',
name: 'Reverse buckets',
@ -232,7 +233,12 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPan
showIf: (opts) => opts.tooltip.show,
});
// custom legend?
commonOptionsBuilder.addLegendOptions(builder);
category = ['Legend'];
builder.addBooleanSwitch({
path: 'legend.show',
name: 'Show legend',
defaultValue: defaultPanelOptions.legend.show,
category,
});
})
.setSuggestionsSupplier(new HeatmapSuggestionsSupplier());