XYChart: Improved new tooltip (#75818)

Co-authored-by: Ihor Yeromin <yeryomin.igor@gmail.com>
Co-authored-by: Leon Sorokin <leeoniya@gmail.com>
This commit is contained in:
Adela Almasan 2024-01-04 16:55:23 -06:00 committed by GitHub
parent a03eca29eb
commit 935ecdd809
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 979 additions and 106 deletions

View File

@ -6563,16 +6563,11 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/xychart/TooltipView.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Styles should be written using objects.", "1"],
[0, 0, 0, "Styles should be written using objects.", "2"],
[0, 0, 0, "Styles should be written using objects.", "3"],
[0, 0, 0, "Styles should be written using objects.", "4"]
[0, 0, 0, "Do not use any type assertions.", "0"]
],
"public/app/plugins/panel/xychart/XYChartPanel2.tsx:5381": [
"public/app/plugins/panel/xychart/XYChartPanel.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"],
[0, 0, 0, "Styles should be written using objects.", "2"]
[0, 0, 0, "Do not use any type assertions.", "1"]
],
"public/app/plugins/panel/xychart/dims.ts:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],

View File

@ -0,0 +1,687 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "blue",
"mode": "fixed"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 32
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 11,
"w": 7,
"x": 0,
"y": 0
},
"id": 1,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"fixed": "orange"
},
"x": "Miles_per_Gallon",
"y": "Horsepower"
}
],
"seriesMapping": "auto",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "${DS_GDEV-TESTDATA}"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Standard options: Single color",
"type": "xychart"
},
{
"datasource": {},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "green",
"mode": "shades"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 32
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 11,
"w": 7,
"x": 7,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"fixed": "orange"
},
"x": "Miles_per_Gallon",
"y": "Horsepower"
}
],
"seriesMapping": "auto",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "${DS_GDEV-TESTDATA}"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Standard options: Shades of a color",
"type": "xychart"
},
{
"datasource": {},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "yellow",
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 32
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 11,
"w": 7,
"x": 14,
"y": 0
},
"id": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"fixed": "orange"
},
"x": "Miles_per_Gallon",
"y": "Horsepower"
}
],
"seriesMapping": "auto",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "${DS_GDEV-TESTDATA}"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Standard options: Classic palette",
"type": "xychart"
},
{
"datasource": {},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "blue",
"mode": "fixed"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 32
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 12,
"w": 7,
"x": 0,
"y": 11
},
"id": 5,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"fixed": "orange"
},
"x": "x",
"y": "y"
},
{
"pointColor": {
"fixed": "green"
},
"x": "x",
"y": "z"
}
],
"seriesMapping": "manual",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "${DS_GDEV-TESTDATA}"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Standard options: Single color + Mapping: Fixed color",
"type": "xychart"
},
{
"datasource": {},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "blue",
"mode": "shades"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 32
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 12,
"w": 7,
"x": 7,
"y": 11
},
"id": 6,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"fixed": "orange"
},
"x": "x",
"y": "y"
},
{
"pointColor": {
"fixed": "green"
},
"x": "x",
"y": "z"
}
],
"seriesMapping": "manual",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "${DS_GDEV-TESTDATA}"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Standard options: Shades of color + Mapping: Fixed color",
"type": "xychart"
},
{
"datasource": {},
"fieldConfig": {
"defaults": {
"color": {
"fixedColor": "blue",
"mode": "palette-classic"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 32
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"fieldMinMax": false,
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 12,
"w": 7,
"x": 14,
"y": 11
},
"id": 7,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"fixed": "orange"
},
"x": "x",
"y": "y"
},
{
"pointColor": {
"fixed": "green"
},
"x": "x",
"y": "z"
}
],
"seriesMapping": "manual",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,z\n1,2,3\n3,4,5\n5,6,7",
"datasource": {
"type": "grafana-testdata-datasource",
"uid": "${DS_GDEV-TESTDATA}"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Standard options: Classic palette + Mapping: Fixed color",
"type": "xychart"
},
{
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "continuous-BlYlRd"
},
"custom": {
"axisBorderShow": false,
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"pointSize": {
"fixed": 50
},
"scaleDistribution": {
"type": "linear"
},
"show": "points"
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unitScale": true
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 7,
"x": 0,
"y": 23
},
"id": 8,
"options": {
"legend": {
"calcs": [],
"displayMode": "list",
"placement": "bottom",
"showLegend": true
},
"series": [
{
"pointColor": {
"field": "c",
"fixed": "dark-green"
},
"x": "x",
"y": "y"
}
],
"seriesMapping": "manual",
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"csvContent": "x,y,c\n0,0,0\n50,50,25\n100,100,50",
"datasource": {
"type": "testdata",
"uid": "PD8C576611E62080A"
},
"refId": "A",
"scenarioId": "csv_content"
}
],
"title": "Panel Title",
"type": "xychart"
}
],
"refresh": "",
"schemaVersion": 39,
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "XYChart tooltip color test",
"uid": "cb67db43-dd72-4ada-a313-53f46c20adcc",
"version": 1,
"weekStart": ""
}

View File

@ -124,5 +124,6 @@
"timeseries-yaxis-ticks": (import '../dev-dashboards/panel-timeseries/timeseries-yaxis-ticks.json'),
"trend_example": (import '../dev-dashboards/panel-trend/trend_example.json'),
"xychart-example": (import '../dev-dashboards/panel-xychart/xychart-example.json'),
"xychart-tooltip-color-test": (import '../dev-dashboards/panel-xychart/xychart-tooltip-color-test.json'),
},
}

View File

@ -163,22 +163,22 @@ function fmt(field: Field, val: number): string {
}
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;
`,
infoWrap: css({
padding: '8px',
width: '100%',
th: {
fontWeight: theme.typography.fontWeightMedium,
padding: theme.spacing(0.25, 2),
},
}),
highlight: css({
background: theme.colors.action.hover,
}),
xVal: css({
fontWeight: theme.typography.fontWeightBold,
}),
icon: css({
marginRight: theme.spacing(1),
verticalAlign: 'middle',
}),
});

View File

@ -16,6 +16,7 @@ import { config } from '@grafana/runtime';
import {
Portal,
TooltipDisplayMode,
TooltipPlugin2,
UPlotChart,
UPlotConfigBuilder,
VizLayout,
@ -23,10 +24,12 @@ import {
VizLegendItem,
VizTooltipContainer,
} from '@grafana/ui';
import { TooltipHoverMode } from '@grafana/ui/src/components/uPlot/plugins/TooltipPlugin2';
import { FacetedData } from '@grafana/ui/src/components/uPlot/types';
import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
import { TooltipView } from './TooltipView';
import { XYChartTooltip } from './XYChartTooltip';
import { Options, SeriesMapping } from './panelcfg.gen';
import { prepData, prepScatter, ScatterPanelInfo } from './scatter';
import { ScatterHoverEvent, ScatterSeries } from './types';
@ -34,13 +37,14 @@ import { ScatterHoverEvent, ScatterSeries } from './types';
type Props = PanelProps<Options>;
const TOOLTIP_OFFSET = 10;
export const XYChartPanel2 = (props: Props) => {
export const XYChartPanel = (props: Props) => {
const [error, setError] = useState<string | undefined>();
const [series, setSeries] = useState<ScatterSeries[]>([]);
const [builder, setBuilder] = useState<UPlotConfigBuilder | undefined>();
const [facets, setFacets] = useState<FacetedData | undefined>();
const [hover, setHover] = useState<ScatterHoverEvent | undefined>();
const [shouldDisplayCloseButton, setShouldDisplayCloseButton] = useState<boolean>(false);
const showNewVizTooltips = Boolean(config.featureToggles.newVizTooltips);
const isToolTipOpen = useRef<boolean>(false);
const oldOptions = usePrevious(props.options);
@ -69,9 +73,9 @@ export const XYChartPanel2 = (props: Props) => {
props.options,
getData,
config.theme2,
scatterHoverCallback,
onUPlotClick,
isToolTipOpen
showNewVizTooltips ? null : scatterHoverCallback,
showNewVizTooltips ? null : onUPlotClick,
showNewVizTooltips ? null : isToolTipOpen
);
if (info.error) {
@ -82,7 +86,7 @@ export const XYChartPanel2 = (props: Props) => {
setFacets(() => prepData(info, props.data.series));
setError(undefined);
}
}, [props.data.series, props.options]);
}, [props.data.series, props.options, showNewVizTooltips]);
const initFacets = useCallback(() => {
setFacets(() => prepData({ error, series }, props.data.series));
@ -187,11 +191,11 @@ export const XYChartPanel2 = (props: Props) => {
}
const legendStyle = {
flexStart: css`
div {
justify-content: flex-start !important;
}
`,
flexStart: css({
div: {
justifyContent: 'flex-start',
},
}),
};
return (
@ -218,47 +222,69 @@ export const XYChartPanel2 = (props: Props) => {
<>
<VizLayout width={props.width} height={props.height} legend={renderLegend()}>
{(vizWidth: number, vizHeight: number) => (
<UPlotChart config={builder} data={facets} width={vizWidth} height={vizHeight} />
<UPlotChart config={builder} data={facets} width={vizWidth} height={vizHeight}>
{config.featureToggles.newVizTooltips && props.options.tooltip.mode !== TooltipDisplayMode.None && (
<TooltipPlugin2
config={builder}
hoverMode={TooltipHoverMode.xyOne}
render={(u, dataIdxs, seriesIdx, isPinned, dismiss) => {
return (
<XYChartTooltip
data={props.data.series}
dataIdxs={dataIdxs}
allSeries={series}
dismiss={dismiss}
isPinned={isPinned}
options={props.options}
seriesIdx={seriesIdx}
/>
);
}}
/>
)}
</UPlotChart>
)}
</VizLayout>
<Portal>
{hover && props.options.tooltip.mode !== TooltipDisplayMode.None && (
<VizTooltipContainer
position={{ x: hover.pageX, y: hover.pageY }}
offset={{ x: TOOLTIP_OFFSET, y: TOOLTIP_OFFSET }}
allowPointerEvents={isToolTipOpen.current}
>
{shouldDisplayCloseButton && (
<div
style={{
width: '100%',
display: 'flex',
justifyContent: 'flex-end',
}}
>
<CloseButton
onClick={onCloseToolTip}
{!config.featureToggles.newVizTooltips && (
<Portal>
{hover && props.options.tooltip.mode !== TooltipDisplayMode.None && (
<VizTooltipContainer
position={{ x: hover.pageX, y: hover.pageY }}
offset={{ x: TOOLTIP_OFFSET, y: TOOLTIP_OFFSET }}
allowPointerEvents={isToolTipOpen.current}
>
{shouldDisplayCloseButton && (
<div
style={{
position: 'relative',
top: 'auto',
right: 'auto',
marginRight: 0,
width: '100%',
display: 'flex',
justifyContent: 'flex-end',
}}
/>
</div>
)}
<TooltipView
options={props.options.tooltip}
allSeries={series}
manualSeriesConfigs={props.options.series}
seriesMapping={props.options.seriesMapping!}
rowIndex={hover.xIndex}
hoveredPointIndex={hover.scatterIndex}
data={props.data.series}
/>
</VizTooltipContainer>
)}
</Portal>
>
<CloseButton
onClick={onCloseToolTip}
style={{
position: 'relative',
top: 'auto',
right: 'auto',
marginRight: 0,
}}
/>
</div>
)}
<TooltipView
options={props.options.tooltip}
allSeries={series}
manualSeriesConfigs={props.options.series}
seriesMapping={props.options.seriesMapping!}
rowIndex={hover.xIndex}
hoveredPointIndex={hover.scatterIndex}
data={props.data.series}
/>
</VizTooltipContainer>
)}
</Portal>
)}
</>
);
};

View File

@ -0,0 +1,136 @@
import { css } from '@emotion/css';
import React from 'react';
import { DataFrame, Field, getFieldDisplayName, GrafanaTheme2, LinkModel } from '@grafana/data';
import { alpha } from '@grafana/data/src/themes/colorManipulator';
import { 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';
import { ColorIndicator, LabelValue } from '@grafana/ui/src/components/VizTooltip/types';
import { getTitleFromHref } from 'app/features/explore/utils/links';
import { Options } from './panelcfg.gen';
import { ScatterSeries, YValue } from './types';
import { fmt } from './utils';
export interface Props {
dataIdxs: Array<number | null>;
seriesIdx: number | null | undefined;
isPinned: boolean;
dismiss: () => void;
options: Options;
data: DataFrame[]; // source data
allSeries: ScatterSeries[];
}
export const XYChartTooltip = ({ dataIdxs, seriesIdx, data, allSeries, dismiss, options, isPinned }: Props) => {
const styles = useStyles2(getStyles);
const rowIndex = dataIdxs.find((idx) => idx !== null);
// @todo: remove -1 when uPlot v2 arrive
// context: first value in dataIdxs always null and represent X series
const hoveredPointIndex = seriesIdx! - 1;
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 getHeaderLabel = (): LabelValue => {
let label = series.name;
if (options.seriesMapping === 'manual') {
label = options.series?.[hoveredPointIndex]?.name ?? `Series ${hoveredPointIndex + 1}`;
}
let colorThing = series.pointColor(frame);
if (Array.isArray(colorThing)) {
colorThing = colorThing[rowIndex];
}
return {
label,
value: null,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
color: alpha(colorThing as string, 0.5),
colorIndicator: ColorIndicator.marker_md,
};
};
const getContentLabel = (): LabelValue[] => {
let colorThing = series.pointColor(frame);
if (Array.isArray(colorThing)) {
colorThing = colorThing[rowIndex];
}
const yValue: YValue = {
name: getFieldDisplayName(yField, frame),
val: yField.values[rowIndex],
field: yField,
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
color: alpha(colorThing as string, 0.5),
};
const content: LabelValue[] = [
{
label: getFieldDisplayName(xField, frame),
value: fmt(xField, xField.values[rowIndex]),
},
{
label: yValue.name,
value: fmt(yValue.field, yValue.val),
},
];
// add extra fields
const extraFields: Field[] = frame.fields.filter((f) => f !== xField && f !== yField);
if (extraFields) {
extraFields.forEach((field) => {
content.push({
label: field.name,
value: fmt(field, field.values[rowIndex]),
});
});
}
return content;
};
const getLinks = (): Array<LinkModel<Field>> => {
let links: Array<LinkModel<Field>> = [];
if (yField.getLinks) {
const v = yField.values[rowIndex];
const disp = yField.display ? yField.display(v) : { text: `${v}`, numeric: +v };
links = yField.getLinks({ calculatedValue: disp, valueRowIndex: rowIndex }).map((linkModel) => {
if (!linkModel.title) {
linkModel.title = getTitleFromHref(linkModel.href);
}
return linkModel;
});
}
return links;
};
return (
<div className={styles.wrapper}>
<VizTooltipHeader headerLabel={getHeaderLabel()} />
<VizTooltipContent contentLabelValue={getContentLabel()} />
{isPinned && <VizTooltipFooter dataLinks={getLinks()} canAnnotate={false} />}
</div>
);
};
const getStyles = (theme: GrafanaTheme2) => ({
wrapper: css({
display: 'flex',
flexDirection: 'column',
width: '280px',
}),
});

View File

@ -3,11 +3,11 @@ import { commonOptionsBuilder } from '@grafana/ui';
import { AutoEditor } from './AutoEditor';
import { ManualEditor } from './ManualEditor';
import { XYChartPanel2 } from './XYChartPanel2';
import { XYChartPanel } from './XYChartPanel';
import { getScatterFieldConfig } from './config';
import { Options, FieldConfig, defaultFieldConfig } from './panelcfg.gen';
export const plugin = new PanelPlugin<Options, FieldConfig>(XYChartPanel2)
export const plugin = new PanelPlugin<Options, FieldConfig>(XYChartPanel)
.useFieldConfig(getScatterFieldConfig(defaultFieldConfig))
.setPanelOptions((builder) => {
builder
@ -38,6 +38,6 @@ export const plugin = new PanelPlugin<Options, FieldConfig>(XYChartPanel2)
showIf: (cfg) => cfg.seriesMapping === 'manual',
});
commonOptionsBuilder.addTooltipOptions(builder);
commonOptionsBuilder.addTooltipOptions(builder, true);
commonOptionsBuilder.addLegendOptions(builder);
});

View File

@ -46,9 +46,9 @@ export function prepScatter(
options: Options,
getData: () => DataFrame[],
theme: GrafanaTheme2,
ttip: ScatterHoverCallback,
ttip: null | ScatterHoverCallback,
onUPlotClick: null | ((evt?: Object) => void),
isToolTipOpen: MutableRefObject<boolean>
isToolTipOpen: null | MutableRefObject<boolean>
): ScatterPanelInfo {
let series: ScatterSeries[];
let builder: UPlotConfigBuilder;
@ -294,14 +294,13 @@ interface DrawBubblesOpts {
};
}
//const prepConfig: UPlotConfigPrepFnXY<Options> = ({ frames, series, theme }) => {
const prepConfig = (
getData: () => DataFrame[],
scatterSeries: ScatterSeries[],
theme: GrafanaTheme2,
ttip: ScatterHoverCallback,
ttip: null | ScatterHoverCallback,
onUPlotClick: null | ((evt?: Object) => void),
isToolTipOpen: MutableRefObject<boolean>
isToolTipOpen: null | MutableRefObject<boolean>
) => {
let qt: Quadtree;
let hRect: Rect | null;
@ -522,8 +521,10 @@ const prepConfig = (
});
const clearPopupIfOpened = () => {
if (isToolTipOpen.current) {
ttip(undefined);
if (isToolTipOpen?.current) {
if (ttip) {
ttip(undefined);
}
if (onUPlotClick) {
onUPlotClick();
}
@ -534,7 +535,9 @@ const prepConfig = (
// clip hover points/bubbles to plotting area
builder.addHook('init', (u, r) => {
u.over.style.overflow = 'hidden';
if (!config.featureToggles.newVizTooltips) {
u.over.style.overflow = 'hidden';
}
ref_parent = u.root.parentElement;
if (onUPlotClick) {
@ -556,26 +559,28 @@ const prepConfig = (
rect = r;
});
builder.addHook('setLegend', (u) => {
if (u.cursor.idxs != null) {
for (let i = 0; i < u.cursor.idxs.length; i++) {
const sel = u.cursor.idxs[i];
if (sel != null && !isToolTipOpen.current) {
ttip({
scatterIndex: i - 1,
xIndex: sel,
pageX: rect.left + u.cursor.left!,
pageY: rect.top + u.cursor.top!,
});
return; // only show the first one
if (ttip) {
builder.addHook('setLegend', (u) => {
if (u.cursor.idxs != null) {
for (let i = 0; i < u.cursor.idxs.length; i++) {
const sel = u.cursor.idxs[i];
if (sel != null && !isToolTipOpen?.current) {
ttip({
scatterIndex: i - 1,
xIndex: sel,
pageX: rect.left + u.cursor.left!,
pageY: rect.top + u.cursor.top!,
});
return; // only show the first one
}
}
}
}
if (!isToolTipOpen.current) {
ttip(undefined);
}
});
if (!isToolTipOpen?.current) {
ttip(undefined);
}
});
}
builder.addHook('drawClear', (u) => {
clearPopupIfOpened();
@ -598,8 +603,8 @@ const prepConfig = (
const frames = getData();
let xField = scatterSeries[0].x(scatterSeries[0].frame(frames));
let config = xField.config;
let customConfig = config.custom;
let fieldConfig = xField.config;
let customConfig = fieldConfig.custom;
let scaleDistr = customConfig?.scaleDistribution;
builder.addScale({
@ -610,12 +615,12 @@ const prepConfig = (
distribution: scaleDistr?.type,
log: scaleDistr?.log,
linearThreshold: scaleDistr?.linearThreshold,
min: config.min,
max: config.max,
min: fieldConfig.min,
max: fieldConfig.max,
softMin: customConfig?.axisSoftMin,
softMax: customConfig?.axisSoftMax,
centeredZero: customConfig?.axisCenteredZero,
decimals: config.decimals,
decimals: fieldConfig.decimals,
});
// why does this fall back to '' instead of null or undef?

View File

@ -50,3 +50,17 @@ export interface ScatterSeries {
};
};
}
export interface YValue {
name: string;
val: number;
field: Field;
color: string;
}
export interface ExtraFacets {
colorFacetFieldName: string;
sizeFacetFieldName: string;
colorFacetValue: number;
sizeFacetValue: number;
}

View File

@ -0,0 +1,9 @@
import { Field, formattedValueToString } from '@grafana/data';
export function fmt(field: Field, val: number): string {
if (field.display) {
return formattedValueToString(field.display(val));
}
return `${val}`;
}