diff --git a/docs/sources/developers/kinds/composable/heatmap/panelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/heatmap/panelcfg/schema-reference.md
index 4427b6c99e9..87b7fbfdd21 100644
--- a/docs/sources/developers/kinds/composable/heatmap/panelcfg/schema-reference.md
+++ b/docs/sources/developers/kinds/composable/heatmap/panelcfg/schema-reference.md
@@ -126,7 +126,7 @@ Controls tooltip options
| Property | Type | Required | Default | Description |
|------------------|---------|----------|---------|----------------------------------------------------------------|
-| `show` | boolean | **Yes** | | Controls if the tooltip is shown |
+| `mode` | string | **Yes** | | TODO docs
Possible values are: `single`, `multi`, `none`. |
| `showColorScale` | boolean | No | | Controls if the tooltip shows a color scale in header |
| `yHistogram` | boolean | No | | Controls if the tooltip shows a histogram of the y-axis values |
@@ -138,7 +138,7 @@ Controls tooltip options
| `exemplars` | [ExemplarConfig](#exemplarconfig) | **Yes** | | Controls exemplar options |
| `legend` | [HeatmapLegend](#heatmaplegend) | **Yes** | | Controls legend options |
| `showValue` | string | **Yes** | | | *{
layout: ui.HeatmapCellLayout & "auto" // TODO: fix after remove when https://github.com/grafana/cuetsy/issues/74 is fixed
}
Controls the display of the value in the cell |
-| `tooltip` | [HeatmapTooltip](#heatmaptooltip) | **Yes** | | Controls tooltip options |
+| `tooltip` | [object](#tooltip) | **Yes** | `map[mode:single showColorScale:false yHistogram:false]` | Controls tooltip options |
| `yAxis` | [YAxisConfig](#yaxisconfig) | **Yes** | | Configuration options for the yAxis |
| `calculate` | boolean | No | `false` | Controls if the heatmap should be calculated from data |
| `calculation` | [HeatmapCalculationOptions](#heatmapcalculationoptions) | No | | |
@@ -237,4 +237,12 @@ Filters values between a given range
|----------|-----------------------------------|----------|---------|-------------|
| `object` | Possible types are: [](#), [](#). | | |
+### Tooltip
+
+Controls tooltip options
+
+| Property | Type | Required | Default | Description |
+|----------|-----------------------------------|----------|---------|-------------|
+| `object` | Possible types are: [](#), [](#). | | |
+
diff --git a/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts
index 4ed9a2f5450..ab654c3d887 100644
--- a/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts
+++ b/packages/grafana-schema/src/raw/composable/heatmap/panelcfg/x/HeatmapPanelCfg_types.gen.ts
@@ -130,9 +130,9 @@ export interface FilterValueRange {
*/
export interface HeatmapTooltip {
/**
- * Controls if the tooltip is shown
+ * Controls how the tooltip is shown
*/
- show: boolean;
+ mode: ui.TooltipDisplayMode;
/**
* Controls if the tooltip shows a color scale in header
*/
@@ -266,7 +266,7 @@ export const defaultOptions: Partial = {
},
showValue: ui.VisibilityMode.Auto,
tooltip: {
- show: true,
+ mode: ui.TooltipDisplayMode.Single,
yHistogram: false,
showColorScale: false,
},
diff --git a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx
index 76cd2eca5e3..c5968dafdbc 100644
--- a/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx
+++ b/packages/grafana-ui/src/components/uPlot/plugins/TooltipPlugin2.tsx
@@ -134,6 +134,7 @@ export const TooltipPlugin2 = ({ config, hoverMode, render, clientZoom = false,
winHeight = htmlEl.clientHeight - 5;
});
+ let seriesIdxs: Array = plot?.cursor.idxs!.slice()!;
let closestSeriesIdx: number | null = null;
let pendingRender = false;
@@ -192,9 +193,7 @@ export const TooltipPlugin2 = ({ config, hoverMode, render, clientZoom = false,
style: _style,
isPinned: _isPinned,
isHovering: _isHovering,
- contents: _isHovering
- ? renderRef.current(_plot!, _plot!.cursor.idxs!, closestSeriesIdx, _isPinned, dismiss)
- : null,
+ contents: _isHovering ? renderRef.current(_plot!, seriesIdxs, closestSeriesIdx, _isPinned, dismiss) : null,
dismiss,
};
@@ -324,12 +323,12 @@ export const TooltipPlugin2 = ({ config, hoverMode, render, clientZoom = false,
// fires on data value hovers/unhovers (before setSeries)
config.addHook('setLegend', (u) => {
- let hoveredSeriesIdx = _plot!.cursor.idxs!.findIndex((v, i) => i > 0 && v != null);
+ seriesIdxs = _plot?.cursor!.idxs!.slice()!;
+
+ let hoveredSeriesIdx = seriesIdxs.findIndex((v, i) => i > 0 && v != null);
let _isHoveringNow = hoveredSeriesIdx !== -1;
- // in mode: 2 uPlot won't fire the proximity-based setSeries (below)
- // so we set closestSeriesIdx here instead
- // TODO: setSeries only fires for TimeSeries & Trend...not state timeline or statsus history
+ // setSeries may not fire if focus.prox is not set, so we set closestSeriesIdx here instead
if (hoverMode === TooltipHoverMode.xyOne) {
closestSeriesIdx = hoveredSeriesIdx;
}
diff --git a/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx b/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx
index f5ea9eee339..2eff6a8a4a6 100644
--- a/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx
+++ b/public/app/plugins/panel/heatmap/HeatmapHoverView.tsx
@@ -16,7 +16,7 @@ import {
ScopedVars,
} from '@grafana/data';
import { HeatmapCellLayout } from '@grafana/schema';
-import { useStyles2 } from '@grafana/ui';
+import { TooltipDisplayMode, useStyles2 } from '@grafana/ui';
import { VizTooltipContent } from '@grafana/ui/src/components/VizTooltip/VizTooltipContent';
import { VizTooltipFooter } from '@grafana/ui/src/components/VizTooltip/VizTooltipFooter';
import { VizTooltipHeader } from '@grafana/ui/src/components/VizTooltip/VizTooltipHeader';
@@ -31,6 +31,7 @@ import { renderHistogram } from './renderHistogram';
import { formatMilliseconds, getFieldFromData, getHoverCellColor, getSparseCellMinMax } from './tooltip/utils';
interface Props {
+ mode: TooltipDisplayMode;
dataIdxs: Array;
seriesIdx: number | null | undefined;
dataRef: React.MutableRefObject;
@@ -65,11 +66,10 @@ const HeatmapHoverCell = ({
showHistogram,
isPinned,
canAnnotate,
- panelData,
showColorScale = false,
scopedVars,
replaceVars,
- dismiss,
+ mode,
}: Props) => {
const index = dataIdxs[1]!;
const data = dataRef.current;
@@ -102,8 +102,6 @@ const HeatmapHoverCell = ({
const meta = readHeatmapRowsCustomMeta(data.heatmap);
const yDisp = yField?.display ? (v: string) => formattedValueToString(yField.display!(v)) : (v: string) => `${v}`;
- const yValueIdx = index % data.yBucketCount! ?? 0;
-
let interval = xField?.config.interval;
let yBucketMin: string;
@@ -114,9 +112,15 @@ const HeatmapHoverCell = ({
let nonNumericOrdinalDisplay: string | undefined = undefined;
- if (isSparse) {
- ({ xBucketMin, xBucketMax, yBucketMin, yBucketMax } = getSparseCellMinMax(data!, index));
- } else {
+ let contentLabelValue: LabelValue[] = [];
+
+ const getYValueIndex = (idx: number) => {
+ return idx % data.yBucketCount! ?? 0;
+ };
+
+ let yValueIdx = getYValueIndex(index);
+
+ const getData = (idx: number = index) => {
if (meta.yOrdinalDisplay) {
const yMinIdx = data.yLayout === HeatmapCellLayout.le ? yValueIdx - 1 : yValueIdx;
const yMaxIdx = data.yLayout === HeatmapCellLayout.le ? yValueIdx : yValueIdx + 1;
@@ -154,15 +158,130 @@ const HeatmapHoverCell = ({
}
if (data.xLayout === HeatmapCellLayout.le) {
- xBucketMax = xVals[index];
+ xBucketMax = xVals[idx];
xBucketMin = xBucketMax - data.xBucketSize!;
} else {
- xBucketMin = xVals[index];
+ xBucketMin = xVals[idx];
xBucketMax = xBucketMin + data.xBucketSize!;
}
+ };
+
+ if (isSparse) {
+ ({ xBucketMin, xBucketMax, yBucketMin, yBucketMax } = getSparseCellMinMax(data!, index));
+ } else {
+ getData();
}
- const count = countVals?.[index];
+ const { cellColor, colorPalette } = getHoverCellColor(data, index);
+
+ const getDisplayData = (fromIdx: number, toIdx: number) => {
+ let vals = [];
+ for (let idx = fromIdx; idx <= toIdx; idx++) {
+ if (!countVals?.[idx]) {
+ continue;
+ }
+
+ const color = getHoverCellColor(data, idx).cellColor;
+ count = getCountValue(idx);
+
+ if (isSparse) {
+ ({ xBucketMin, xBucketMax, yBucketMin, yBucketMax } = getSparseCellMinMax(data!, idx));
+ } else {
+ yValueIdx = getYValueIndex(idx);
+ getData(idx);
+ }
+
+ const { label, value } = getContentLabels()[0];
+
+ vals.push({
+ label,
+ value,
+ color: color ?? '#FFF',
+ isActive: index === idx,
+ });
+ }
+
+ return vals;
+ };
+
+ const getContentLabels = (): LabelValue[] => {
+ const isMulti = mode === TooltipDisplayMode.Multi && !isPinned;
+
+ if (nonNumericOrdinalDisplay) {
+ return isMulti
+ ? [{ label: `Name ${nonNumericOrdinalDisplay}`, value: data.display!(count) }]
+ : [{ label: 'Name', value: nonNumericOrdinalDisplay }];
+ }
+
+ switch (data.yLayout) {
+ case HeatmapCellLayout.unknown:
+ return isMulti
+ ? [{ label: yDisp(yBucketMin), value: data.display!(count) }]
+ : [{ label: '', value: yDisp(yBucketMin) }];
+ }
+
+ return isMulti
+ ? [
+ {
+ label: `Bucket ${yDisp(yBucketMin)}` + '-' + `${yDisp(yBucketMax)}`,
+ value: data.display!(count),
+ },
+ ]
+ : [
+ {
+ label: 'Bucket',
+ value: `${yDisp(yBucketMin)}` + '-' + `${yDisp(yBucketMax)}`,
+ },
+ ];
+ };
+
+ const getCountValue = (idx: number) => {
+ return countVals?.[idx];
+ };
+
+ let count = getCountValue(index);
+
+ if (mode === TooltipDisplayMode.Single || isPinned) {
+ const fromToInt: LabelValue[] = interval ? [{ label: 'Duration', value: formatMilliseconds(interval) }] : [];
+
+ contentLabelValue = [
+ {
+ label: getFieldDisplayName(countField, data.heatmap),
+ value: data.display!(count),
+ color: cellColor ?? '#FFF',
+ colorPlacement: ColorPlacement.trailing,
+ colorIndicator: ColorIndicator.value,
+ },
+ ...getContentLabels(),
+ ...fromToInt,
+ ];
+ }
+
+ if (mode === TooltipDisplayMode.Multi && !isPinned) {
+ let xVal = xField.values[index];
+ let fromIdx = index;
+ let toIdx = index;
+
+ while (xField.values[fromIdx - 1] === xVal) {
+ fromIdx--;
+ }
+
+ while (xField.values[toIdx + 1] === xVal) {
+ toIdx++;
+ }
+
+ const vals: LabelValue[] = getDisplayData(fromIdx, toIdx);
+ vals.forEach((val) => {
+ contentLabelValue.push({
+ label: val.label,
+ value: val.value,
+ color: val.color ?? '#FFF',
+ colorIndicator: ColorIndicator.value,
+ colorPlacement: ColorPlacement.trailing,
+ isActive: val.isActive,
+ });
+ });
+ }
const visibleFields = data.heatmap?.fields.filter((f) => !Boolean(f.config.custom?.hideFrom?.tooltip));
const links: Array> = [];
@@ -203,7 +322,7 @@ const HeatmapHoverCell = ({
useEffect(
() => {
- if (showHistogram && xVals != null && countVals != null) {
+ if (showHistogram && xVals != null && countVals != null && mode === TooltipDisplayMode.Single) {
renderHistogram(can, histCanWidth, histCanHeight, xVals, countVals, index, data.yBucketCount!);
}
},
@@ -211,26 +330,6 @@ const HeatmapHoverCell = ({
[index]
);
- const { cellColor, colorPalette } = getHoverCellColor(data, index);
-
- const getContentLabels = (): LabelValue[] => {
- if (nonNumericOrdinalDisplay) {
- return [{ label: 'Name', value: nonNumericOrdinalDisplay }];
- }
-
- switch (data.yLayout) {
- case HeatmapCellLayout.unknown:
- return [{ label: '', value: yDisp(yBucketMin) }];
- }
-
- return [
- {
- label: 'Bucket',
- value: `${yDisp(yBucketMin)}` + '-' + `${yDisp(yBucketMax)}`,
- },
- ];
- };
-
const getHeaderLabel = (): LabelValue => {
return {
label: '',
@@ -239,23 +338,15 @@ const HeatmapHoverCell = ({
};
const getContentLabelValue = (): LabelValue[] => {
- const fromToInt: LabelValue[] = interval ? [{ label: 'Duration', value: formatMilliseconds(interval) }] : [];
-
- return [
- {
- label: getFieldDisplayName(countField, data.heatmap),
- value: data.display!(count),
- color: cellColor ?? '#FFF',
- colorPlacement: ColorPlacement.trailing,
- colorIndicator: ColorIndicator.value,
- },
- ...getContentLabels(),
- ...fromToInt,
- ];
+ return contentLabelValue;
};
const getCustomContent = () => {
let content: ReactElement[] = [];
+ if (mode !== TooltipDisplayMode.Single) {
+ return content;
+ }
+
// Histogram
if (showHistogram) {
content.push(
diff --git a/public/app/plugins/panel/heatmap/HeatmapPanel.tsx b/public/app/plugins/panel/heatmap/HeatmapPanel.tsx
index 5e4e545925c..a3592699709 100644
--- a/public/app/plugins/panel/heatmap/HeatmapPanel.tsx
+++ b/public/app/plugins/panel/heatmap/HeatmapPanel.tsx
@@ -18,6 +18,7 @@ import {
Portal,
ScaleDistribution,
TooltipPlugin2,
+ TooltipDisplayMode,
ZoomPlugin,
UPlotChart,
usePanelContext,
@@ -167,7 +168,7 @@ export const HeatmapPanel = ({
theme,
eventBus,
onhover: !showNewVizTooltips ? onhover : null,
- onclick: !showNewVizTooltips && options.tooltip.show ? onclick : null,
+ onclick: !showNewVizTooltips && options.tooltip.mode !== TooltipDisplayMode.None ? onclick : null,
isToolTipOpen,
timeZone,
getTimeRange: () => timeRangeRef.current,
@@ -232,7 +233,7 @@ export const HeatmapPanel = ({
{/*children ? children(config, alignedFrame) : null*/}
{!showNewVizTooltips && }
- {showNewVizTooltips && options.tooltip.show && (
+ {showNewVizTooltips && options.tooltip.mode !== TooltipDisplayMode.None && (
{
return (
{!showNewVizTooltips && (
- {hover && options.tooltip.show && (
+ {hover && options.tooltip.mode !== TooltipDisplayMode.None && (
{
},
"showValue": "never",
"tooltip": {
- "show": true,
+ "mode": "single",
"yHistogram": true,
},
"yAxis": {
@@ -131,7 +131,7 @@ describe('Heatmap Migrations', () => {
},
"showValue": "never",
"tooltip": {
- "show": false,
+ "mode": "none",
"yHistogram": false,
},
"yAxis": {
diff --git a/public/app/plugins/panel/heatmap/migrations.ts b/public/app/plugins/panel/heatmap/migrations.ts
index 5445ab13745..325159e5758 100644
--- a/public/app/plugins/panel/heatmap/migrations.ts
+++ b/public/app/plugins/panel/heatmap/migrations.ts
@@ -7,6 +7,7 @@ import {
HeatmapCalculationMode,
HeatmapCalculationOptions,
} from '@grafana/schema';
+import { TooltipDisplayMode } from '@grafana/ui';
import { colorSchemes } from './palettes';
import { Options, defaultOptions, HeatmapColorMode } from './types';
@@ -17,6 +18,20 @@ export const heatmapMigrationHandler = (panel: PanelModel): Partial =>
if (Object.keys(panel.options ?? {}).length === 0) {
return heatmapChangedHandler(panel, 'heatmap', { angular: panel }, panel.fieldConfig);
}
+
+ // multi tooltip mode in 10.3+
+ let showTooltip = panel.options?.tooltip?.show;
+ if (showTooltip !== undefined) {
+ if (showTooltip === true) {
+ panel.options.tooltip.mode = TooltipDisplayMode.Single;
+ } else if (showTooltip === false) {
+ panel.options.tooltip.mode = TooltipDisplayMode.None;
+ }
+
+ // Remove old tooltip option
+ delete panel.options.tooltip?.show;
+ }
+
return panel.options;
};
@@ -111,7 +126,7 @@ export function angularToReactHeatmap(angular: any): { fieldConfig: FieldConfigS
},
showValue: VisibilityMode.Never,
tooltip: {
- show: Boolean(angular.tooltip?.show),
+ mode: Boolean(angular.tooltip?.show) ? TooltipDisplayMode.Single : TooltipDisplayMode.None,
yHistogram: Boolean(angular.tooltip?.showHistogram),
},
exemplars: {
diff --git a/public/app/plugins/panel/heatmap/module.tsx b/public/app/plugins/panel/heatmap/module.tsx
index da2d05509f6..30d7adfa451 100644
--- a/public/app/plugins/panel/heatmap/module.tsx
+++ b/public/app/plugins/panel/heatmap/module.tsx
@@ -9,6 +9,7 @@ import {
ScaleDistributionConfig,
HeatmapCellLayout,
} from '@grafana/schema';
+import { TooltipDisplayMode } from '@grafana/ui';
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';
@@ -391,11 +392,18 @@ export const plugin = new PanelPlugin(HeatmapPanel)
category = ['Tooltip'];
- builder.addBooleanSwitch({
- path: 'tooltip.show',
- name: 'Show tooltip',
- defaultValue: defaultOptions.tooltip.show,
+ builder.addRadio({
+ path: 'tooltip.mode',
+ name: 'Tooltip mode',
category,
+ defaultValue: TooltipDisplayMode.Single,
+ settings: {
+ options: [
+ { value: TooltipDisplayMode.Single, label: 'Single' },
+ { value: TooltipDisplayMode.Multi, label: 'All' },
+ { value: TooltipDisplayMode.None, label: 'Hidden' },
+ ],
+ },
});
builder.addBooleanSwitch({
@@ -403,7 +411,7 @@ export const plugin = new PanelPlugin(HeatmapPanel)
name: 'Show histogram (Y axis)',
defaultValue: defaultOptions.tooltip.yHistogram,
category,
- showIf: (opts) => opts.tooltip.show,
+ showIf: (opts) => opts.tooltip.mode !== TooltipDisplayMode.None,
});
builder.addBooleanSwitch({
@@ -411,7 +419,7 @@ export const plugin = new PanelPlugin(HeatmapPanel)
name: 'Show color scale',
defaultValue: defaultOptions.tooltip.showColorScale,
category,
- showIf: (opts) => opts.tooltip.show && config.featureToggles.newVizTooltips,
+ showIf: (opts) => opts.tooltip.mode !== TooltipDisplayMode.None && config.featureToggles.newVizTooltips,
});
category = ['Legend'];
diff --git a/public/app/plugins/panel/heatmap/panelcfg.cue b/public/app/plugins/panel/heatmap/panelcfg.cue
index 687eefdb41e..13f690f82de 100644
--- a/public/app/plugins/panel/heatmap/panelcfg.cue
+++ b/public/app/plugins/panel/heatmap/panelcfg.cue
@@ -78,8 +78,8 @@ composableKinds: PanelCfg: lineage: {
} @cuetsy(kind="interface")
// Controls tooltip options
HeatmapTooltip: {
- // Controls if the tooltip is shown
- show: bool
+ // Controls how the tooltip is shown
+ mode: ui.TooltipDisplayMode
// Controls if the tooltip shows a histogram of the y-axis values
yHistogram?: bool
// Controls if the tooltip shows a color scale in header
@@ -145,7 +145,7 @@ composableKinds: PanelCfg: lineage: {
}
// Controls tooltip options
tooltip: HeatmapTooltip | *{
- show: true
+ mode: ui.TooltipDisplayMode & (*"single" | _)
yHistogram: false
showColorScale: false
}
diff --git a/public/app/plugins/panel/heatmap/panelcfg.gen.ts b/public/app/plugins/panel/heatmap/panelcfg.gen.ts
index 7699ec9e120..b5589b134e3 100644
--- a/public/app/plugins/panel/heatmap/panelcfg.gen.ts
+++ b/public/app/plugins/panel/heatmap/panelcfg.gen.ts
@@ -127,9 +127,9 @@ export interface FilterValueRange {
*/
export interface HeatmapTooltip {
/**
- * Controls if the tooltip is shown
+ * Controls how the tooltip is shown
*/
- show: boolean;
+ mode: ui.TooltipDisplayMode;
/**
* Controls if the tooltip shows a color scale in header
*/
@@ -263,7 +263,7 @@ export const defaultOptions: Partial = {
},
showValue: ui.VisibilityMode.Auto,
tooltip: {
- show: true,
+ mode: ui.TooltipDisplayMode.Single,
yHistogram: false,
showColorScale: false,
},