mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GraphNG: fix mem leaks & avoid plot re-inits (#38017)
* State timeline: fix mem leak caused by excessive plot re-init * Update PlotTooltipInterpolator type * Do not reference config object in the setCursor hook * fix excessive BarChart re-init caused by shallow diff of text config * one less error Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com> Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
||||
} from '@grafana/ui';
|
||||
import { BarChartOptions } from './types';
|
||||
import { preparePlotConfigBuilder, preparePlotFrame } from './utils';
|
||||
import { PropDiffFn } from '../../../../../packages/grafana-ui/src/components/GraphNG/GraphNG';
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
@@ -20,7 +21,14 @@ export interface BarChartProps
|
||||
extends BarChartOptions,
|
||||
Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend' | 'theme'> {}
|
||||
|
||||
const propsToDiff: string[] = ['orientation', 'barWidth', 'groupWidth', 'stacking', 'showValue', 'text'];
|
||||
const propsToDiff: Array<string | PropDiffFn> = [
|
||||
'orientation',
|
||||
'barWidth',
|
||||
'groupWidth',
|
||||
'stacking',
|
||||
'showValue',
|
||||
(prev: BarChartProps, next: BarChartProps) => next.text?.valueSize === prev.text?.valueSize,
|
||||
];
|
||||
|
||||
export const BarChart: React.FC<BarChartProps> = (props) => {
|
||||
const theme = useTheme2();
|
||||
|
||||
@@ -272,22 +272,22 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
|
||||
const interpolateTooltip: PlotTooltipInterpolator = (
|
||||
updateActiveSeriesIdx,
|
||||
updateActiveDatapointIdx,
|
||||
updateTooltipPosition
|
||||
updateTooltipPosition,
|
||||
u
|
||||
) => {
|
||||
return (u: uPlot) => {
|
||||
let found: Rect | undefined;
|
||||
let cx = u.cursor.left! * devicePixelRatio;
|
||||
let cy = u.cursor.top! * devicePixelRatio;
|
||||
let found: Rect | undefined;
|
||||
let cx = u.cursor.left! * devicePixelRatio;
|
||||
let cy = u.cursor.top! * devicePixelRatio;
|
||||
|
||||
qt.get(cx, cy, 1, 1, (o) => {
|
||||
if (pointWithin(cx, cy, o.x, o.y, o.x + o.w, o.y + o.h)) {
|
||||
found = o;
|
||||
}
|
||||
});
|
||||
qt.get(cx, cy, 1, 1, (o) => {
|
||||
if (pointWithin(cx, cy, o.x, o.y, o.x + o.w, o.y + o.h)) {
|
||||
found = o;
|
||||
}
|
||||
});
|
||||
|
||||
if (found) {
|
||||
// prettier-ignore
|
||||
if (found !== hovered) {
|
||||
if (found) {
|
||||
// prettier-ignore
|
||||
if (found !== hovered) {
|
||||
barMark.style.display = '';
|
||||
barMark.style.left = found.x / devicePixelRatio + 'px';
|
||||
barMark.style.top = found.y / devicePixelRatio + 'px';
|
||||
@@ -298,16 +298,15 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
|
||||
updateActiveDatapointIdx(hovered.didx);
|
||||
updateTooltipPosition();
|
||||
}
|
||||
} else if (hovered !== undefined) {
|
||||
updateActiveSeriesIdx(hovered!.sidx);
|
||||
updateActiveDatapointIdx(hovered!.didx);
|
||||
updateTooltipPosition();
|
||||
hovered = undefined;
|
||||
barMark.style.display = 'none';
|
||||
} else {
|
||||
updateTooltipPosition(true);
|
||||
}
|
||||
};
|
||||
} else if (hovered !== undefined) {
|
||||
updateActiveSeriesIdx(hovered!.sidx);
|
||||
updateActiveDatapointIdx(hovered!.didx);
|
||||
updateTooltipPosition();
|
||||
hovered = undefined;
|
||||
barMark.style.display = 'none';
|
||||
} else {
|
||||
updateTooltipPosition(true);
|
||||
}
|
||||
};
|
||||
|
||||
let alignedTotals: AlignedData | null = null;
|
||||
|
||||
@@ -126,7 +126,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
|
||||
updateActiveSeriesIdx,
|
||||
updateActiveDatapointIdx,
|
||||
updateTooltipPosition
|
||||
) => (u: uPlot) => {
|
||||
) => {
|
||||
if (shouldChangeHover) {
|
||||
if (hoveredSeriesIdx != null) {
|
||||
updateActiveSeriesIdx(hoveredSeriesIdx);
|
||||
@@ -271,7 +271,7 @@ export function prepareTimelineFields(
|
||||
let isTimeseries = false;
|
||||
let changed = false;
|
||||
const fields: Field[] = [];
|
||||
for (const field of frame.fields) {
|
||||
for (let field of frame.fields) {
|
||||
switch (field.type) {
|
||||
case FieldType.time:
|
||||
isTimeseries = true;
|
||||
@@ -281,8 +281,17 @@ export function prepareTimelineFields(
|
||||
case FieldType.number:
|
||||
case FieldType.boolean:
|
||||
case FieldType.string:
|
||||
// magic value for join() to leave nulls alone
|
||||
(field.config.custom = field.config.custom ?? {}).spanNulls = -1;
|
||||
field = {
|
||||
...field,
|
||||
config: {
|
||||
...field.config,
|
||||
custom: {
|
||||
...field.config.custom,
|
||||
// magic value for join() to leave nulls alone
|
||||
spanNulls: -1,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
if (mergeValues) {
|
||||
let merged = unsetSameFutureValues(field.values.toArray());
|
||||
|
||||
Reference in New Issue
Block a user