grafana/public/app/plugins/panel/xychart/TooltipView.tsx
Victor Marin 3361f2c62d
XYChart further improvements (#55152)
* Tooltip shows all data facets. Renamed options

* Add per series line style

* Remove line style option from manual panel options

* Refactored tooltip view

* sets selected after switch to manual

* remove facet prefixes

* in manual mode pull series names from config options, not y facet

* unused import

* Point size

* x & y axes labels

* Fix manual series prep

* betterer

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
2022-09-27 18:18:42 +03:00

177 lines
4.7 KiB
TypeScript

import { css } from '@emotion/css';
import React from 'react';
import {
DataFrame,
Field,
formattedValueToString,
getFieldDisplayName,
GrafanaTheme2,
LinkModel,
TimeRange,
} from '@grafana/data';
import { LinkButton, usePanelContext, useStyles2, VerticalGroup, VizTooltipOptions } from '@grafana/ui';
import { getFieldLinksForExplore } from 'app/features/explore/utils/links';
import { ScatterSeriesConfig, SeriesMapping } from './models.gen';
import { ScatterSeries } from './types';
interface YValue {
name: string;
val: number;
field: Field;
color: string;
}
interface ExtraFacets {
colorFacetFieldName: string;
sizeFacetFieldName: string;
colorFacetValue: number;
sizeFacetValue: number;
}
export interface Props {
allSeries: ScatterSeries[];
data: DataFrame[]; // source data
manualSeriesConfigs: ScatterSeriesConfig[] | undefined;
rowIndex?: number; // the hover row
seriesMapping: SeriesMapping;
hoveredPointIndex: number; // the hovered point
options: VizTooltipOptions;
range: TimeRange;
}
export const TooltipView = ({
allSeries,
data,
manualSeriesConfigs,
seriesMapping,
rowIndex,
hoveredPointIndex,
options,
range,
}: Props) => {
const style = useStyles2(getStyles);
const { onSplitOpen } = usePanelContext();
if (!allSeries || rowIndex == null) {
return null;
}
const series = allSeries[hoveredPointIndex];
const frame = series.frame(data);
const xField = series.x(frame);
const yField = series.y(frame);
const links: Array<LinkModel<Field>> = getFieldLinksForExplore({
field: yField,
splitOpenFn: onSplitOpen,
rowIndex,
range,
});
let yValue: YValue | null = null;
let extraFacets: ExtraFacets | null = null;
if (seriesMapping === SeriesMapping.Manual && manualSeriesConfigs) {
const colorFacetFieldName = manualSeriesConfigs[hoveredPointIndex].pointColor?.field ?? '';
const sizeFacetFieldName = manualSeriesConfigs[hoveredPointIndex].pointSize?.field ?? '';
const colorFacet = colorFacetFieldName ? frame.fields.find((f) => f.name === colorFacetFieldName) : undefined;
const sizeFacet = sizeFacetFieldName ? frame.fields.find((f) => f.name === sizeFacetFieldName) : undefined;
extraFacets = {
colorFacetFieldName,
sizeFacetFieldName,
colorFacetValue: colorFacet?.values.get(rowIndex),
sizeFacetValue: sizeFacet?.values.get(rowIndex),
};
}
yValue = {
name: getFieldDisplayName(yField, frame),
val: yField.values.get(rowIndex),
field: yField,
color: series.pointColor(frame) as string,
};
return (
<>
<table className={style.infoWrap}>
<tr>
<th colSpan={2} style={{ backgroundColor: yValue.color }}></th>
</tr>
<tbody>
<tr>
<th>{xField.name}</th>
<td>{fmt(frame.fields[0], xField.values.get(rowIndex))}</td>
</tr>
<tr>
<th>{yValue.name}:</th>
<td>{fmt(yValue.field, yValue.val)}</td>
</tr>
{extraFacets !== null && extraFacets.colorFacetFieldName && (
<tr>
<th>{extraFacets.colorFacetFieldName}:</th>
<td>{extraFacets.colorFacetValue}</td>
</tr>
)}
{extraFacets !== null && extraFacets.sizeFacetFieldName && (
<tr>
<th>{extraFacets.sizeFacetFieldName}:</th>
<td>{extraFacets.sizeFacetValue}</td>
</tr>
)}
{links.length > 0 && (
<tr>
<td colSpan={2}>
<VerticalGroup>
{links.map((link, i) => (
<LinkButton
key={i}
icon={'external-link-alt'}
target={link.target}
href={link.href}
onClick={link.onClick}
fill="text"
style={{ width: '100%' }}
>
{link.title}
</LinkButton>
))}
</VerticalGroup>
</td>
</tr>
)}
</tbody>
</table>
</>
);
};
function fmt(field: Field, val: number): string {
if (field.display) {
return formattedValueToString(field.display(val));
}
return `${val}`;
}
const getStyles = (theme: GrafanaTheme2) => ({
infoWrap: css`
padding: 8px;
width: 100%;
th {
font-weight: ${theme.typography.fontWeightMedium};
padding: ${theme.spacing(0.25, 2)};
}
`,
highlight: css`
background: ${theme.colors.action.hover};
`,
xVal: css`
font-weight: ${theme.typography.fontWeightBold};
`,
icon: css`
margin-right: ${theme.spacing(1)};
vertical-align: middle;
`,
});