Heatmap (new): exemplars tooltip stub (#48795)

This commit is contained in:
Ryan McKinley 2022-05-06 07:21:14 -07:00 committed by GitHub
parent 2d6ab03e4f
commit 42098a7a27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 21 deletions

View File

@ -1,6 +1,6 @@
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { Field, FieldType, formattedValueToString, getFieldDisplayName, LinkModel } from '@grafana/data'; import { DataFrameView, Field, FieldType, formattedValueToString, getFieldDisplayName, LinkModel } from '@grafana/data';
import { LinkButton, VerticalGroup } from '@grafana/ui'; import { LinkButton, VerticalGroup } from '@grafana/ui';
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv'; import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
@ -153,6 +153,30 @@ export const HeatmapHoverView = ({ data, hover, showHistogram }: Props) => {
[hover.index] [hover.index]
); );
const renderExemplars = () => {
const exemplarIndex = data.exemplarsMappings?.lookup; //?.[hover.index];
if (!exemplarIndex || !data.exemplars) {
return null;
}
const ids = exemplarIndex[hover.index];
if (ids) {
const view = new DataFrameView(data.exemplars);
return (
<ul>
{ids.map((id) => (
<li key={id}>
<pre>{JSON.stringify(view.get(id), null, 2)}</pre>
</li>
))}
</ul>
);
}
// should not show anything... but for debugging
return <div>EXEMPLARS: {JSON.stringify(exemplarIndex)}</div>;
};
return ( return (
<> <>
<div> <div>
@ -175,6 +199,7 @@ export const HeatmapHoverView = ({ data, hover, showHistogram }: Props) => {
{getFieldDisplayName(countField!, data.heatmap)}: {count} {getFieldDisplayName(countField!, data.heatmap)}: {count}
</div> </div>
</div> </div>
{renderExemplars()}
{links.length > 0 && ( {links.length > 0 && (
<VerticalGroup> <VerticalGroup>
{links.map((link, i) => ( {links.map((link, i) => (

View File

@ -1,6 +1,6 @@
import { createTheme, ArrayVector, DataFrameType, FieldType } from '@grafana/data'; import { createTheme, ArrayVector, DataFrameType, FieldType } from '@grafana/data';
import { BucketLayout, getAnnotationMapping, HEATMAP_NOT_SCANLINES_ERROR } from './fields'; import { BucketLayout, getExemplarsMapping, HEATMAP_NOT_SCANLINES_ERROR } from './fields';
import { PanelOptions } from './models.gen'; import { PanelOptions } from './models.gen';
const theme = createTheme(); const theme = createTheme();
@ -16,7 +16,7 @@ describe('Heatmap data', () => {
describe('creating a heatmap data mapping', () => { describe('creating a heatmap data mapping', () => {
describe('generates a simple data mapping with orderly data', () => { describe('generates a simple data mapping with orderly data', () => {
const mapping = getAnnotationMapping( const mapping = getExemplarsMapping(
{ {
heatmap: { heatmap: {
name: 'test', name: 'test',
@ -139,7 +139,7 @@ describe('creating a heatmap data mapping', () => {
// In this case, we are just finding proper values, but don't care if a values // In this case, we are just finding proper values, but don't care if a values
// exists in the bucket in the original data or not. Therefore, we should see // exists in the bucket in the original data or not. Therefore, we should see
// a value mapped into the second mapping bucket containing the value '8'. // a value mapped into the second mapping bucket containing the value '8'.
const mapping = getAnnotationMapping(heatmap, rawData); const mapping = getExemplarsMapping(heatmap, rawData);
expect(mapping.lookup.length).toEqual(3); expect(mapping.lookup.length).toEqual(3);
expect(mapping.lookup[0]).toEqual([1, 4]); expect(mapping.lookup[0]).toEqual([1, 4]);
expect(mapping.lookup[1]).toEqual([8]); expect(mapping.lookup[1]).toEqual([8]);
@ -148,7 +148,7 @@ describe('creating a heatmap data mapping', () => {
}); });
describe('Handles a larger data set that will not fill all buckets', () => { describe('Handles a larger data set that will not fill all buckets', () => {
const mapping = getAnnotationMapping( const mapping = getExemplarsMapping(
{ {
heatmap: { heatmap: {
name: 'test', name: 'test',
@ -276,7 +276,7 @@ describe('creating a heatmap data mapping', () => {
it('Will not process heatmap buckets', () => { it('Will not process heatmap buckets', () => {
expect(() => expect(() =>
getAnnotationMapping( getExemplarsMapping(
{ {
...heatmap, ...heatmap,
heatmap: { heatmap: {
@ -291,7 +291,7 @@ describe('creating a heatmap data mapping', () => {
).toThrow(HEATMAP_NOT_SCANLINES_ERROR); ).toThrow(HEATMAP_NOT_SCANLINES_ERROR);
expect(() => expect(() =>
getAnnotationMapping( getExemplarsMapping(
{ {
...heatmap, ...heatmap,
heatmap: { heatmap: {
@ -306,7 +306,7 @@ describe('creating a heatmap data mapping', () => {
).toThrow(HEATMAP_NOT_SCANLINES_ERROR); ).toThrow(HEATMAP_NOT_SCANLINES_ERROR);
expect(() => expect(() =>
getAnnotationMapping( getExemplarsMapping(
{ {
...heatmap, ...heatmap,
heatmap: { heatmap: {

View File

@ -32,8 +32,8 @@ export const HEATMAP_NOT_SCANLINES_ERROR = 'A calculated heatmap was expected, b
export interface HeatmapData { export interface HeatmapData {
// List of heatmap frames // List of heatmap frames
heatmap?: DataFrame; heatmap?: DataFrame;
annotations?: DataFrame; exemplars?: DataFrame;
annotationMappings?: HeatmapDataMapping; exemplarsMappings?: HeatmapDataMapping;
yAxisValues?: Array<number | string | null>; yAxisValues?: Array<number | string | null>;
@ -61,17 +61,17 @@ export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme
const { source } = options; const { source } = options;
const annotations = data.annotations?.[0]; // TODO: Maybe join on time with frames const exemplars = data.annotations?.find((f) => f.name === 'exemplar');
if (source === HeatmapSourceMode.Calculate) { if (source === HeatmapSourceMode.Calculate) {
// TODO, check for error etc // TODO, check for error etc
return getHeatmapData(calculateHeatmapFromData(frames, options.heatmap ?? {}), annotations, theme); return getHeatmapData(calculateHeatmapFromData(frames, options.heatmap ?? {}), exemplars, theme);
} }
// Find a well defined heatmap // Find a well defined heatmap
let scanlinesHeatmap = frames.find((f) => f.meta?.type === DataFrameType.HeatmapScanlines); let scanlinesHeatmap = frames.find((f) => f.meta?.type === DataFrameType.HeatmapScanlines);
if (scanlinesHeatmap) { if (scanlinesHeatmap) {
return getHeatmapData(scanlinesHeatmap, annotations, theme); return getHeatmapData(scanlinesHeatmap, exemplars, theme);
} }
let bucketsHeatmap = frames.find((f) => f.meta?.type === DataFrameType.HeatmapBuckets); let bucketsHeatmap = frames.find((f) => f.meta?.type === DataFrameType.HeatmapBuckets);
@ -80,16 +80,16 @@ export function prepareHeatmapData(data: PanelData, options: PanelOptions, theme
yAxisValues: frames[0].fields.flatMap((field) => yAxisValues: frames[0].fields.flatMap((field) =>
field.type === FieldType.number ? getFieldDisplayName(field) : [] field.type === FieldType.number ? getFieldDisplayName(field) : []
), ),
...getHeatmapData(bucketsToScanlines(bucketsHeatmap), annotations, theme), ...getHeatmapData(bucketsToScanlines(bucketsHeatmap), exemplars, theme),
}; };
} }
if (source === HeatmapSourceMode.Data) { if (source === HeatmapSourceMode.Data) {
return getHeatmapData(bucketsToScanlines(frames[0]), annotations, theme); return getHeatmapData(bucketsToScanlines(frames[0]), exemplars, theme);
} }
// TODO, check for error etc // TODO, check for error etc
return getHeatmapData(calculateHeatmapFromData(frames, options.heatmap ?? {}), annotations, theme); return getHeatmapData(calculateHeatmapFromData(frames, options.heatmap ?? {}), exemplars, theme);
} }
const getHeatmapFields = (dataFrame: DataFrame): Array<Field | undefined> => { const getHeatmapFields = (dataFrame: DataFrame): Array<Field | undefined> => {
@ -100,7 +100,7 @@ const getHeatmapFields = (dataFrame: DataFrame): Array<Field | undefined> => {
return [xField, yField, countField]; return [xField, yField, countField];
}; };
export const getAnnotationMapping = (heatmapData: HeatmapData, rawData: DataFrame): HeatmapDataMapping => { export const getExemplarsMapping = (heatmapData: HeatmapData, rawData: DataFrame): HeatmapDataMapping => {
if (heatmapData.heatmap?.meta?.type !== DataFrameType.HeatmapScanlines) { if (heatmapData.heatmap?.meta?.type !== DataFrameType.HeatmapScanlines) {
throw HEATMAP_NOT_SCANLINES_ERROR; throw HEATMAP_NOT_SCANLINES_ERROR;
} }
@ -152,7 +152,7 @@ export const getAnnotationMapping = (heatmapData: HeatmapData, rawData: DataFram
return mapping; return mapping;
}; };
const getHeatmapData = (frame: DataFrame, annotations: DataFrame | undefined, theme: GrafanaTheme2): HeatmapData => { const getHeatmapData = (frame: DataFrame, exemplars: DataFrame | undefined, theme: GrafanaTheme2): HeatmapData => {
if (frame.meta?.type !== DataFrameType.HeatmapScanlines) { if (frame.meta?.type !== DataFrameType.HeatmapScanlines) {
return { return {
warning: 'Expected heatmap scanlines format', warning: 'Expected heatmap scanlines format',
@ -190,7 +190,7 @@ const getHeatmapData = (frame: DataFrame, annotations: DataFrame | undefined, th
const disp = frame.fields[2].display ?? getValueFormat('short'); const disp = frame.fields[2].display ?? getValueFormat('short');
const data: HeatmapData = { const data: HeatmapData = {
heatmap: frame, heatmap: frame,
annotations, exemplars,
xBucketSize: xBinIncr, xBucketSize: xBinIncr,
yBucketSize: yBinIncr, yBucketSize: yBinIncr,
xBucketCount: xBinQty, xBucketCount: xBinQty,
@ -203,8 +203,9 @@ const getHeatmapData = (frame: DataFrame, annotations: DataFrame | undefined, th
display: (v) => formattedValueToString(disp(v)), display: (v) => formattedValueToString(disp(v)),
}; };
if (annotations) { if (exemplars) {
data.annotationMappings = getAnnotationMapping(data, annotations); data.exemplarsMappings = getExemplarsMapping(data, exemplars);
console.log('EXEMPLARS', data.exemplarsMappings, data.exemplars);
} }
return data; return data;