mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -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';
|
throw 'no heatmap fields found';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!xs.length || !ys.length) {
|
||||||
|
throw 'no values found';
|
||||||
|
}
|
||||||
|
|
||||||
const heat2d = heatmap(xs, ys, {
|
const heat2d = heatmap(xs, ys, {
|
||||||
xSorted: true,
|
xSorted: true,
|
||||||
xTime: xField.type === FieldType.time,
|
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 { DataFrameType, GrafanaTheme2, PanelProps, reduceField, ReducerID, TimeRange } from '@grafana/data';
|
||||||
import { PanelDataErrorView } from '@grafana/runtime';
|
import { PanelDataErrorView } from '@grafana/runtime';
|
||||||
import {
|
import { Portal, UPlotChart, useStyles2, useTheme2, VizLayout, VizTooltipContainer } from '@grafana/ui';
|
||||||
Portal,
|
|
||||||
UPlotChart,
|
|
||||||
useStyles2,
|
|
||||||
useTheme2,
|
|
||||||
VizLayout,
|
|
||||||
VizTooltipContainer,
|
|
||||||
LegendDisplayMode,
|
|
||||||
} from '@grafana/ui';
|
|
||||||
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
|
||||||
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
|
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
|
||||||
|
|
||||||
@ -42,7 +34,13 @@ export const HeatmapPanel: React.FC<HeatmapPanelProps> = ({
|
|||||||
let timeRangeRef = useRef<TimeRange>(timeRange);
|
let timeRangeRef = useRef<TimeRange>(timeRange);
|
||||||
timeRangeRef.current = 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]);
|
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,
|
onhover: onhover,
|
||||||
onclick: options.tooltip.show ? onclick : null,
|
onclick: options.tooltip.show ? onclick : null,
|
||||||
onzoom: (evt) => {
|
onzoom: (evt) => {
|
||||||
|
const delta = evt.xMax - evt.xMin;
|
||||||
|
if (delta > 1) {
|
||||||
onChangeTimeRange({ from: evt.xMin, to: evt.xMax });
|
onChangeTimeRange({ from: evt.xMin, to: evt.xMax });
|
||||||
|
}
|
||||||
},
|
},
|
||||||
isToolTipOpen,
|
isToolTipOpen,
|
||||||
timeZone,
|
timeZone,
|
||||||
@ -99,7 +100,7 @@ export const HeatmapPanel: React.FC<HeatmapPanelProps> = ({
|
|||||||
}, [options, data.structureRev]);
|
}, [options, data.structureRev]);
|
||||||
|
|
||||||
const renderLegend = () => {
|
const renderLegend = () => {
|
||||||
if (options.legend.displayMode === LegendDisplayMode.Hidden || !info.heatmap) {
|
if (!info.heatmap || !options.legend.show) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ describe('Heatmap Migrations', () => {
|
|||||||
"fill": "dark-orange",
|
"fill": "dark-orange",
|
||||||
"mode": "scheme",
|
"mode": "scheme",
|
||||||
"scale": "exponential",
|
"scale": "exponential",
|
||||||
"scheme": "Oranges",
|
"scheme": "BuGn",
|
||||||
"steps": 256,
|
"steps": 256,
|
||||||
},
|
},
|
||||||
"heatmap": Object {
|
"heatmap": Object {
|
||||||
@ -46,9 +46,7 @@ describe('Heatmap Migrations', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"legend": Object {
|
"legend": Object {
|
||||||
"calcs": Array [],
|
"show": true,
|
||||||
"displayMode": "list",
|
|
||||||
"placement": "bottom",
|
|
||||||
},
|
},
|
||||||
"showValue": "never",
|
"showValue": "never",
|
||||||
"source": "calculate",
|
"source": "calculate",
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { FieldConfigSource, PanelModel, PanelTypeChangedHandler } from '@grafana/data';
|
import { FieldConfigSource, PanelModel, PanelTypeChangedHandler } from '@grafana/data';
|
||||||
import { LegendDisplayMode, VisibilityMode } from '@grafana/schema';
|
import { VisibilityMode } from '@grafana/schema';
|
||||||
import {
|
import {
|
||||||
HeatmapCalculationMode,
|
HeatmapCalculationMode,
|
||||||
HeatmapCalculationOptions,
|
HeatmapCalculationOptions,
|
||||||
} from 'app/features/transformers/calculateHeatmap/models.gen';
|
} 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
|
* This is called when the panel changes from another panel
|
||||||
@ -59,9 +60,7 @@ export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigS
|
|||||||
yAxisLabels: angular.yBucketBound,
|
yAxisLabels: angular.yBucketBound,
|
||||||
yAxisReverse: angular.reverseYBuckets,
|
yAxisReverse: angular.reverseYBuckets,
|
||||||
legend: {
|
legend: {
|
||||||
displayMode: angular.legend.show ? LegendDisplayMode.List : LegendDisplayMode.Hidden,
|
show: Boolean(angular.legend.show),
|
||||||
calcs: [],
|
|
||||||
placement: 'bottom',
|
|
||||||
},
|
},
|
||||||
showValue: VisibilityMode.Never,
|
showValue: VisibilityMode.Never,
|
||||||
tooltip: {
|
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 };
|
return { fieldConfig, options };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,6 +99,9 @@ function asNumber(v: any): number | undefined {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const heatmapMigrationHandler = (panel: PanelModel): Partial<PanelOptions> => {
|
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;
|
return panel.options;
|
||||||
};
|
};
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// It is currenty hand written but will serve as the target for cuetsy
|
// 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';
|
import { HeatmapCalculationOptions } from 'app/features/transformers/calculateHeatmap/models.gen';
|
||||||
|
|
||||||
export const modelVersion = Object.freeze([1, 0]);
|
export const modelVersion = Object.freeze([1, 0]);
|
||||||
@ -42,8 +42,11 @@ export interface HeatmapTooltip {
|
|||||||
show: boolean;
|
show: boolean;
|
||||||
yHistogram?: boolean;
|
yHistogram?: boolean;
|
||||||
}
|
}
|
||||||
|
export interface HeatmapLegend {
|
||||||
|
show: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PanelOptions extends OptionsWithLegend {
|
export interface PanelOptions {
|
||||||
source: HeatmapSourceMode;
|
source: HeatmapSourceMode;
|
||||||
|
|
||||||
color: HeatmapColorOptions;
|
color: HeatmapColorOptions;
|
||||||
@ -56,6 +59,7 @@ export interface PanelOptions extends OptionsWithLegend {
|
|||||||
hideThreshold?: number; // was hideZeroBuckets
|
hideThreshold?: number; // was hideZeroBuckets
|
||||||
yAxisLabels?: string;
|
yAxisLabels?: string;
|
||||||
yAxisReverse?: boolean;
|
yAxisReverse?: boolean;
|
||||||
|
legend: HeatmapLegend;
|
||||||
|
|
||||||
tooltip: HeatmapTooltip;
|
tooltip: HeatmapTooltip;
|
||||||
}
|
}
|
||||||
@ -71,15 +75,13 @@ export const defaultPanelOptions: PanelOptions = {
|
|||||||
steps: 64,
|
steps: 64,
|
||||||
},
|
},
|
||||||
showValue: VisibilityMode.Auto,
|
showValue: VisibilityMode.Auto,
|
||||||
legend: {
|
|
||||||
displayMode: LegendDisplayMode.Hidden,
|
|
||||||
placement: 'bottom',
|
|
||||||
calcs: [],
|
|
||||||
},
|
|
||||||
tooltip: {
|
tooltip: {
|
||||||
show: true,
|
show: true,
|
||||||
yHistogram: false,
|
yHistogram: false,
|
||||||
},
|
},
|
||||||
|
legend: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
cellGap: 1,
|
cellGap: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import React from 'react';
|
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 { config } from '@grafana/runtime';
|
||||||
import { GraphFieldConfig, VisibilityMode } from '@grafana/schema';
|
import { GraphFieldConfig } from '@grafana/schema';
|
||||||
import { commonOptionsBuilder } from '@grafana/ui';
|
|
||||||
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
|
import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
|
||||||
import { addHeatmapCalculationOptions } from 'app/features/transformers/calculateHeatmap/editor/helper';
|
import { addHeatmapCalculationOptions } from 'app/features/transformers/calculateHeatmap/editor/helper';
|
||||||
|
|
||||||
import { HeatmapPanel } from './HeatmapPanel';
|
import { HeatmapPanel } from './HeatmapPanel';
|
||||||
import { heatmapChangedHandler } from './migrations';
|
import { heatmapChangedHandler, heatmapMigrationHandler } from './migrations';
|
||||||
import {
|
import {
|
||||||
PanelOptions,
|
PanelOptions,
|
||||||
defaultPanelOptions,
|
defaultPanelOptions,
|
||||||
@ -20,9 +19,11 @@ import { colorSchemes, quantizeScheme } from './palettes';
|
|||||||
import { HeatmapSuggestionsSupplier } from './suggestions';
|
import { HeatmapSuggestionsSupplier } from './suggestions';
|
||||||
|
|
||||||
export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPanel)
|
export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPanel)
|
||||||
.useFieldConfig()
|
.useFieldConfig({
|
||||||
|
disableStandardOptions: [FieldConfigProperty.Color, FieldConfigProperty.Thresholds],
|
||||||
|
})
|
||||||
.setPanelChangeHandler(heatmapChangedHandler)
|
.setPanelChangeHandler(heatmapChangedHandler)
|
||||||
// .setMigrationHandler(heatmapMigrationHandler)
|
.setMigrationHandler(heatmapMigrationHandler)
|
||||||
.setPanelOptions((builder, context) => {
|
.setPanelOptions((builder, context) => {
|
||||||
const opts = context.options ?? defaultPanelOptions;
|
const opts = context.options ?? defaultPanelOptions;
|
||||||
|
|
||||||
@ -155,19 +156,19 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPan
|
|||||||
category = ['Display'];
|
category = ['Display'];
|
||||||
|
|
||||||
builder
|
builder
|
||||||
.addRadio({
|
// .addRadio({
|
||||||
path: 'showValue',
|
// path: 'showValue',
|
||||||
name: 'Show values',
|
// name: 'Show values',
|
||||||
defaultValue: defaultPanelOptions.showValue,
|
// defaultValue: defaultPanelOptions.showValue,
|
||||||
category,
|
// category,
|
||||||
settings: {
|
// settings: {
|
||||||
options: [
|
// options: [
|
||||||
{ value: VisibilityMode.Auto, label: 'Auto' },
|
// { value: VisibilityMode.Auto, label: 'Auto' },
|
||||||
{ value: VisibilityMode.Always, label: 'Always' },
|
// { value: VisibilityMode.Always, label: 'Always' },
|
||||||
{ value: VisibilityMode.Never, label: 'Never' },
|
// { value: VisibilityMode.Never, label: 'Never' },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
.addNumberInput({
|
.addNumberInput({
|
||||||
path: 'hideThreshold',
|
path: 'hideThreshold',
|
||||||
name: 'Hide cell counts <=',
|
name: 'Hide cell counts <=',
|
||||||
@ -194,20 +195,20 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPan
|
|||||||
// max: 100,
|
// max: 100,
|
||||||
// },
|
// },
|
||||||
// })
|
// })
|
||||||
.addRadio({
|
// .addRadio({
|
||||||
path: 'yAxisLabels',
|
// path: 'yAxisLabels',
|
||||||
name: 'Axis labels',
|
// name: 'Axis labels',
|
||||||
defaultValue: 'auto',
|
// defaultValue: 'auto',
|
||||||
category,
|
// category,
|
||||||
settings: {
|
// settings: {
|
||||||
options: [
|
// options: [
|
||||||
{ value: 'auto', label: 'Auto' },
|
// { value: 'auto', label: 'Auto' },
|
||||||
{ value: 'middle', label: 'Middle' },
|
// { value: 'middle', label: 'Middle' },
|
||||||
{ value: 'bottom', label: 'Bottom' },
|
// { value: 'bottom', label: 'Bottom' },
|
||||||
{ value: 'top', label: 'Top' },
|
// { value: 'top', label: 'Top' },
|
||||||
],
|
// ],
|
||||||
},
|
// },
|
||||||
})
|
// })
|
||||||
.addBooleanSwitch({
|
.addBooleanSwitch({
|
||||||
path: 'yAxisReverse',
|
path: 'yAxisReverse',
|
||||||
name: 'Reverse buckets',
|
name: 'Reverse buckets',
|
||||||
@ -232,7 +233,12 @@ export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(HeatmapPan
|
|||||||
showIf: (opts) => opts.tooltip.show,
|
showIf: (opts) => opts.tooltip.show,
|
||||||
});
|
});
|
||||||
|
|
||||||
// custom legend?
|
category = ['Legend'];
|
||||||
commonOptionsBuilder.addLegendOptions(builder);
|
builder.addBooleanSwitch({
|
||||||
|
path: 'legend.show',
|
||||||
|
name: 'Show legend',
|
||||||
|
defaultValue: defaultPanelOptions.legend.show,
|
||||||
|
category,
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.setSuggestionsSupplier(new HeatmapSuggestionsSupplier());
|
.setSuggestionsSupplier(new HeatmapSuggestionsSupplier());
|
||||||
|
Loading…
Reference in New Issue
Block a user