mirror of
https://github.com/grafana/grafana.git
synced 2025-02-09 23:16:16 -06:00
Heatmap: improve new heatmap options (#49201)
This commit is contained in:
parent
2053d37d7a
commit
d0d4d8af7f
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user