mirror of
https://github.com/grafana/grafana.git
synced 2024-12-02 05:29:42 -06:00
Heatmap (new): exemplars tooltip stub (#48795)
This commit is contained in:
parent
2d6ab03e4f
commit
42098a7a27
@ -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) => (
|
||||||
|
@ -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: {
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user