mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
XYPanel: Improvements (#54220)
* Fix panel option bugs and make tooltip options work * Support multiple series in explicit mode. Rename XY/Explicit to Auto/Manual * Fixes * Fix * Legend improvements * Rewrite Pure Component to Function Component * Add datalinks support * Legend fixes and CR modifications * Fix bugs that crash panel
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { MutableRefObject } from 'react';
|
||||
import uPlot from 'uplot';
|
||||
|
||||
import {
|
||||
@@ -41,19 +42,26 @@ export function prepScatter(
|
||||
options: XYChartOptions,
|
||||
getData: () => DataFrame[],
|
||||
theme: GrafanaTheme2,
|
||||
ttip: ScatterHoverCallback
|
||||
ttip: ScatterHoverCallback,
|
||||
onUPlotClick: null | ((evt?: Object) => void),
|
||||
isToolTipOpen: MutableRefObject<boolean>
|
||||
): ScatterPanelInfo {
|
||||
let series: ScatterSeries[];
|
||||
let builder: UPlotConfigBuilder;
|
||||
|
||||
try {
|
||||
series = prepSeries(options, getData());
|
||||
builder = prepConfig(getData, series, theme, ttip);
|
||||
builder = prepConfig(getData, series, theme, ttip, onUPlotClick, isToolTipOpen);
|
||||
} catch (e) {
|
||||
console.log('prepScatter ERROR', e);
|
||||
const errorMessage = e instanceof Error ? e.message : 'Unknown error in prepScatter';
|
||||
let errorMsg = 'Unknown error in prepScatter';
|
||||
if (typeof e === 'string') {
|
||||
errorMsg = e;
|
||||
} else if (e instanceof Error) {
|
||||
errorMsg = e.message;
|
||||
}
|
||||
|
||||
return {
|
||||
error: errorMessage,
|
||||
error: errorMsg,
|
||||
series: [],
|
||||
};
|
||||
}
|
||||
@@ -120,7 +128,7 @@ function getScatterSeries(
|
||||
// Size configs
|
||||
//----------------
|
||||
let pointSizeHints = dims.pointSizeConfig;
|
||||
let pointSizeFixed = dims.pointSizeConfig?.fixed ?? y.config.custom?.pointSizeConfig?.fixed ?? 5;
|
||||
let pointSizeFixed = dims.pointSizeConfig?.fixed ?? y.config.custom?.pointSize?.fixed ?? 5;
|
||||
let pointSize: DimensionValues<number> = () => pointSizeFixed;
|
||||
if (dims.pointSizeIndex) {
|
||||
pointSize = (frame) => {
|
||||
@@ -176,6 +184,7 @@ function getScatterSeries(
|
||||
|
||||
label: VisibilityMode.Never,
|
||||
labelValue: () => '',
|
||||
show: !frame.fields[yIndex].config.custom.hideFrom?.viz,
|
||||
|
||||
hints: {
|
||||
pointSize: pointSizeHints!,
|
||||
@@ -189,11 +198,13 @@ function getScatterSeries(
|
||||
function prepSeries(options: XYChartOptions, frames: DataFrame[]): ScatterSeries[] {
|
||||
let seriesIndex = 0;
|
||||
if (!frames.length) {
|
||||
throw 'missing data';
|
||||
throw 'Missing data';
|
||||
}
|
||||
|
||||
if (options.mode === 'explicit') {
|
||||
if (options.mode === 'manual') {
|
||||
if (options.series?.length) {
|
||||
const scatterSeries: ScatterSeries[] = [];
|
||||
|
||||
for (const series of options.series) {
|
||||
if (!series?.x) {
|
||||
throw 'Select X dimension';
|
||||
@@ -221,10 +232,12 @@ function prepSeries(options: XYChartOptions, frames: DataFrame[]): ScatterSeries
|
||||
pointSizeConfig: series.pointSize,
|
||||
pointSizeIndex: findFieldIndex(frame, series.pointSize?.field),
|
||||
};
|
||||
return [getScatterSeries(seriesIndex++, frames, frameIndex, xIndex, yIndex, dims)];
|
||||
scatterSeries.push(getScatterSeries(seriesIndex++, frames, frameIndex, xIndex, yIndex, dims));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scatterSeries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,7 +245,7 @@ function prepSeries(options: XYChartOptions, frames: DataFrame[]): ScatterSeries
|
||||
const dims = options.dims ?? {};
|
||||
const frameIndex = dims.frame ?? 0;
|
||||
const frame = frames[frameIndex];
|
||||
const numericIndicies: number[] = [];
|
||||
const numericIndices: number[] = [];
|
||||
|
||||
let xIndex = findFieldIndex(frame, dims.x);
|
||||
for (let i = 0; i < frame.fields.length; i++) {
|
||||
@@ -245,7 +258,7 @@ function prepSeries(options: XYChartOptions, frames: DataFrame[]): ScatterSeries
|
||||
continue; // skip
|
||||
}
|
||||
|
||||
numericIndicies.push(i);
|
||||
numericIndices.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,10 +266,10 @@ function prepSeries(options: XYChartOptions, frames: DataFrame[]): ScatterSeries
|
||||
throw 'Missing X dimension';
|
||||
}
|
||||
|
||||
if (!numericIndicies.length) {
|
||||
if (!numericIndices.length) {
|
||||
throw 'No Y values';
|
||||
}
|
||||
return numericIndicies.map((yIndex) => getScatterSeries(seriesIndex++, frames, frameIndex, xIndex!, yIndex, {}));
|
||||
return numericIndices.map((yIndex) => getScatterSeries(seriesIndex++, frames, frameIndex, xIndex!, yIndex, {}));
|
||||
}
|
||||
|
||||
interface DrawBubblesOpts {
|
||||
@@ -278,7 +291,9 @@ const prepConfig = (
|
||||
getData: () => DataFrame[],
|
||||
scatterSeries: ScatterSeries[],
|
||||
theme: GrafanaTheme2,
|
||||
ttip: ScatterHoverCallback
|
||||
ttip: ScatterHoverCallback,
|
||||
onUPlotClick: null | ((evt?: Object) => void),
|
||||
isToolTipOpen: MutableRefObject<boolean>
|
||||
) => {
|
||||
let qt: Quadtree;
|
||||
let hRect: Rect | null;
|
||||
@@ -489,9 +504,32 @@ const prepConfig = (
|
||||
},
|
||||
});
|
||||
|
||||
const clearPopupIfOpened = () => {
|
||||
if (isToolTipOpen.current) {
|
||||
ttip(undefined);
|
||||
if (onUPlotClick) {
|
||||
onUPlotClick();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ref_parent: HTMLElement | null = null;
|
||||
|
||||
// clip hover points/bubbles to plotting area
|
||||
builder.addHook('init', (u, r) => {
|
||||
u.over.style.overflow = 'hidden';
|
||||
ref_parent = u.root.parentElement;
|
||||
|
||||
if (onUPlotClick) {
|
||||
ref_parent?.addEventListener('click', onUPlotClick);
|
||||
}
|
||||
});
|
||||
|
||||
builder.addHook('destroy', (u) => {
|
||||
if (onUPlotClick) {
|
||||
ref_parent?.removeEventListener('click', onUPlotClick);
|
||||
clearPopupIfOpened();
|
||||
}
|
||||
});
|
||||
|
||||
let rect: DOMRect;
|
||||
@@ -502,11 +540,10 @@ const prepConfig = (
|
||||
});
|
||||
|
||||
builder.addHook('setLegend', (u) => {
|
||||
// console.log('TTIP???', u.cursor.idxs);
|
||||
if (u.cursor.idxs != null) {
|
||||
for (let i = 0; i < u.cursor.idxs.length; i++) {
|
||||
const sel = u.cursor.idxs[i];
|
||||
if (sel != null) {
|
||||
if (sel != null && !isToolTipOpen.current) {
|
||||
ttip({
|
||||
scatterIndex: i - 1,
|
||||
xIndex: sel,
|
||||
@@ -517,10 +554,15 @@ const prepConfig = (
|
||||
}
|
||||
}
|
||||
}
|
||||
ttip(undefined);
|
||||
|
||||
if (!isToolTipOpen.current) {
|
||||
ttip(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
builder.addHook('drawClear', (u) => {
|
||||
clearPopupIfOpened();
|
||||
|
||||
qt = qt || new Quadtree(0, 0, u.bbox.width, u.bbox.height);
|
||||
|
||||
qt.clear();
|
||||
@@ -600,6 +642,7 @@ const prepConfig = (
|
||||
scaleKey: '', // facets' scales used (above)
|
||||
lineColor: lineColor as string,
|
||||
fillColor: alpha(pointColor, 0.5),
|
||||
show: !field.config.custom.hideFrom?.viz,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -634,7 +677,7 @@ const prepConfig = (
|
||||
* from? is this where we would support that? -- need the previous values
|
||||
*/
|
||||
export function prepData(info: ScatterPanelInfo, data: DataFrame[], from?: number): FacetedData {
|
||||
if (info.error) {
|
||||
if (info.error || !data.length) {
|
||||
return [null];
|
||||
}
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user