{confirmDialog}
{spec.enableFiltering && (
;
diff --git a/tempo/src/components/AttributeFilters.tsx b/tempo/src/components/AttributeFilters.tsx
index 3bc9f9de5..4dec3523d 100644
--- a/tempo/src/components/AttributeFilters.tsx
+++ b/tempo/src/components/AttributeFilters.tsx
@@ -17,7 +17,7 @@ import { useTimeRange } from '@perses-dev/plugin-system';
import type { UseQueryResult } from '@tanstack/react-query';
import { useQuery } from '@tanstack/react-query';
import type { ReactElement, SyntheticEvent } from 'react';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useState } from 'react';
import type { TempoClient } from '../model';
import { getUnixTimeRange } from '../plugins';
@@ -220,9 +220,11 @@ function LazyTextInput(props: LazyTextInputProps): ReactElement {
const [draftValue, setDraftValue] = useState(value);
const isValidInput = draftValue === '' || validationRegex === undefined || validationRegex.test(draftValue);
- useEffect(() => {
+ const [previousValue, setPreviousValue] = useState(value);
+ if (value !== previousValue) {
+ setPreviousValue(value);
setDraftValue(value);
- }, [value, setDraftValue]);
+ }
const handleChange = useCallback((event: React.ChangeEvent) => {
setDraftValue(event.target.value);
diff --git a/tempo/src/components/TraceQLEditor.tsx b/tempo/src/components/TraceQLEditor.tsx
index ac4193399..814aa2a21 100644
--- a/tempo/src/components/TraceQLEditor.tsx
+++ b/tempo/src/components/TraceQLEditor.tsx
@@ -20,7 +20,7 @@ import type { ReactElement } from 'react';
import { useMemo } from 'react';
import type { TempoClient } from '../model';
-import { TraceQLExtension } from './TraceQLExtension';
+import { TraceQLExtension as createTraceQLExtension } from './TraceQLExtension';
export interface TraceQLEditorProps extends Omit {
client?: TempoClient;
@@ -32,7 +32,7 @@ export function TraceQLEditor({ client, ...rest }: TraceQLEditorProps): ReactEle
const { absoluteTimeRange } = useTimeRange();
const traceQLExtension = useMemo(() => {
- return TraceQLExtension({ client, timeRange: absoluteTimeRange });
+ return createTraceQLExtension({ client, timeRange: absoluteTimeRange });
}, [client, absoluteTimeRange]);
const codemirrorTheme = useMemo(() => {
diff --git a/timeserieschart/src/QuerySettingsEditor.tsx b/timeserieschart/src/QuerySettingsEditor.tsx
index 9d24c7395..6b5a22ef5 100644
--- a/timeserieschart/src/QuerySettingsEditor.tsx
+++ b/timeserieschart/src/QuerySettingsEditor.tsx
@@ -64,7 +64,7 @@ export function QuerySettingsEditor(props: TimeSeriesChartOptionsEditorProps): R
if (!recentlyAddedInputRef.current || !focusRef.current) return;
recentlyAddedInputRef.current?.focus();
focusRef.current = false;
- }, [querySettingsList?.length]);
+ });
const handleQueryIndexChange = (e: React.ChangeEvent, i: number): void => {
if (querySettingsList !== undefined) {
diff --git a/timeserieschart/src/TimeSeriesChartBase.tsx b/timeserieschart/src/TimeSeriesChartBase.tsx
index c4ecf7af5..5b98dd097 100644
--- a/timeserieschart/src/TimeSeriesChartBase.tsx
+++ b/timeserieschart/src/TimeSeriesChartBase.tsx
@@ -64,7 +64,7 @@ import { CanvasRenderer } from 'echarts/renderers';
import isEqual from 'lodash/isEqual';
import merge from 'lodash/merge';
import type { MouseEvent } from 'react';
-import { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
+import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { AnnotationTooltip, buildAnnotationSeries } from './annotations/AnnotationTooltip';
import type { TimeSeriesAnnotation } from './utils/annotation';
@@ -210,7 +210,6 @@ export const TimeSeriesChartBase = forwardRef(fun
enableDataZoom(chartRef.current);
}
},
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
mouseover: (params: any): void => {
// Only markPoint (triangles under the X-axis) opens the annotation tooltip.
// Hovering markLine or anything else keeps the regular TimeSeries tooltip visible
@@ -224,7 +223,6 @@ export const TimeSeriesChartBase = forwardRef(fun
}
setHoveredAnnotation(null);
},
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
mouseout: (params: any): void => {
if (
annotations &&
@@ -340,22 +338,23 @@ export const TimeSeriesChartBase = forwardRef(fun
getTimezoneAwareAxisFormatter,
]);
- // Update adjacent charts so tooltip is unpinned when current chart is clicked.
- useEffect(() => {
- // Only allow pinning one tooltip at a time, subsequent tooltip click unpins previous.
- // Multiple tooltips can only be pinned if Ctrl or Cmd key is pressed while clicking.
- const multipleTooltipsPinned = tooltipPinnedCoords !== null && lastTooltipPinnedCoords !== null;
- if (multipleTooltipsPinned) {
- if (!isEqual(lastTooltipPinnedCoords, tooltipPinnedCoords)) {
- setTooltipPinnedCoords(null);
- if (tooltipPinnedCoords !== null && pinnedCrosshair !== null) {
- setPinnedCrosshair(null);
- }
- }
+ // Only changes from another chart (or new series) can clear the local pin.
+ // A local click may update its coordinates without updating the shared pin.
+ const [previousPinState, setPreviousPinState] = useState({ lastTooltipPinnedCoords, seriesMapping });
+ if (
+ previousPinState.lastTooltipPinnedCoords !== lastTooltipPinnedCoords ||
+ previousPinState.seriesMapping !== seriesMapping
+ ) {
+ setPreviousPinState({ lastTooltipPinnedCoords, seriesMapping });
+ if (
+ tooltipPinnedCoords !== null &&
+ lastTooltipPinnedCoords !== null &&
+ !isEqual(lastTooltipPinnedCoords, tooltipPinnedCoords)
+ ) {
+ setTooltipPinnedCoords(null);
+ setPinnedCrosshair(null);
}
- // tooltipPinnedCoords CANNOT be in dep array or tooltip pinning breaks in the current chart's onClick
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [lastTooltipPinnedCoords, seriesMapping]);
+ }
return (
= [];
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
const markPointData: any[] = [];
annotations.forEach((annotation, index) => {
diff --git a/timeserieschart/src/utils/data-transform.ts b/timeserieschart/src/utils/data-transform.ts
index a7244f29a..4e50701f9 100644
--- a/timeserieschart/src/utils/data-transform.ts
+++ b/timeserieschart/src/utils/data-transform.ts
@@ -200,7 +200,6 @@ function findMax(data: LegacyTimeSeries[] | TimeSeries[]): number {
if (data.length && data[0] !== undefined && (data as TimeSeries[])[0]?.values) {
(data as TimeSeries[]).forEach((series) => {
series.values.forEach((valueTuple: TimeSeriesValueTuple) => {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
const [_, value] = valueTuple;
// Use the absolute value so percent thresholds compute correctly against
// negated series (e.g. when `querySettings[].negativeY` is enabled).
diff --git a/timeseriestable/src/components/DataTable.tsx b/timeseriestable/src/components/DataTable.tsx
index 841f17f3e..5d68567fe 100644
--- a/timeseriestable/src/components/DataTable.tsx
+++ b/timeseriestable/src/components/DataTable.tsx
@@ -82,7 +82,7 @@ export const DataTable = ({ queryResults, spec }: DataTableProps): ReactElement
const itemActionsConfig = spec.actions ? (spec.actions as ActionOptions) : undefined;
const itemActionsListConfig = useMemo(
() => (itemActionsConfig?.enabled && itemActionsConfig.displayWithItem ? itemActionsConfig.actionsList : []),
- [itemActionsConfig?.enabled, itemActionsConfig?.displayWithItem, itemActionsConfig?.actionsList],
+ [itemActionsConfig],
);
const { getItemActionButtons, confirmDialog, actionButtons } = useSelectionItemActions({
diff --git a/tracetable/src/DataTable.tsx b/tracetable/src/DataTable.tsx
index 2600e7e1a..ee3121dd1 100644
--- a/tracetable/src/DataTable.tsx
+++ b/tracetable/src/DataTable.tsx
@@ -108,7 +108,7 @@ export function DataTable(props: DataTableProps): ReactElement {
}
}
return result_rows;
- }, [result, options.links?.trace, variableValues]);
+ }, [result, options.links, variableValues]);
const rowsById = useMemo(() => {
const map = new Map();
diff --git a/tracingganttchart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx b/tracingganttchart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx
index edcfbd8f4..70af9f077 100644
--- a/tracingganttchart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx
+++ b/tracingganttchart/src/TracingGanttChart/GanttTable/ResizableDivider.tsx
@@ -18,12 +18,11 @@ import { useEffect, useState } from 'react';
interface ResizableDividerProps {
parentRef: React.RefObject;
- spacing?: number;
onMove: (left: number) => void;
}
export function ResizableDivider(props: ResizableDividerProps): ReactElement {
- const { parentRef, spacing = 0, onMove } = props;
+ const { parentRef, onMove } = props;
const [isResizing, setResizing] = useState(false);
const handleMouseDown = (e: ReactMouseEvent): void => {
@@ -38,6 +37,7 @@ export function ResizableDivider(props: ResizableDividerProps): ReactElement {
if (!parentRef.current) return;
const parentRect = parentRef.current.getBoundingClientRect();
+ const spacing = parseFloat(getComputedStyle(parentRef.current).columnGap) || 0;
// The parent can be a flex row, for example: [leftPercent] [gap] [divider] [gap] [1-leftPercent].
// Without considering spacing, leftPercent would be wrong because it ignores the flex gap between the divider and the element.
diff --git a/tracingganttchart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx b/tracingganttchart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx
index 91c38cdc5..e72a4367f 100644
--- a/tracingganttchart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx
+++ b/tracingganttchart/src/TracingGanttChart/MiniGanttChart/Canvas.tsx
@@ -60,13 +60,13 @@ export function Canvas(props: CanvasProps): ReactElement {
);
useEffect(() => {
- if (!canvasRef.current || !width || !height) return;
+ if (!canvasRef.current || !width) return;
const ctx = canvasRef.current.getContext('2d');
if (!ctx) return;
- drawSpans(ctx, width, height, trace, spanColorGenerator);
- }, [width, height, trace, spanColorGenerator]);
+ drawSpans(ctx, width, CANVAS_HEIGHT, trace, spanColorGenerator);
+ }, [width, trace, spanColorGenerator]);
const translateCursorToTime = (e: ReactMouseEvent | MouseEvent): number => {
if (!canvasRef.current || !width) return 0;
diff --git a/tracingganttchart/src/TracingGanttChart/TracingGanttChart.tsx b/tracingganttchart/src/TracingGanttChart/TracingGanttChart.tsx
index f85a2b89b..a8b469bef 100644
--- a/tracingganttchart/src/TracingGanttChart/TracingGanttChart.tsx
+++ b/tracingganttchart/src/TracingGanttChart/TracingGanttChart.tsx
@@ -64,7 +64,6 @@ export function TracingGanttChart(props: TracingGanttChartProps): ReactElement {
// setTableWidth() is only called by
const [tableWidth, setTableWidth] = useState(0.82);
const gap = 2;
- const spacing = ganttChart.current ? parseFloat(getComputedStyle(ganttChart.current).columnGap) || 0 : 0;
return (
@@ -86,7 +85,7 @@ export function TracingGanttChart(props: TracingGanttChartProps): ReactElement {
{selectedSpan && (
<>
-
+
;
@@ -26,7 +26,7 @@ export function LogsQLEditor(props: LogsQLEditorProps): ReactElement {
const isDarkMode = theme.palette.mode === 'dark';
const logsqlExtension = useMemo(() => {
- return LogsQLExtension();
+ return createLogsQLExtension();
}, []);
const codemirrorTheme = useMemo(() => {
diff --git a/victorialogs/src/queries/victorialogs-log-query/VictoriaLogsLogQueryEditor.tsx b/victorialogs/src/queries/victorialogs-log-query/VictoriaLogsLogQueryEditor.tsx
index 2c2451cfd..2e90cadd8 100644
--- a/victorialogs/src/queries/victorialogs-log-query/VictoriaLogsLogQueryEditor.tsx
+++ b/victorialogs/src/queries/victorialogs-log-query/VictoriaLogsLogQueryEditor.tsx
@@ -16,7 +16,7 @@ import { createModEnterHandler } from '@perses-dev/dashboards';
import type { DatasourceSelectProps, OptionsEditorProps } from '@perses-dev/plugin-system';
import { DatasourceSelect, isVariableDatasource, useDatasourceSelectValueToSelector } from '@perses-dev/plugin-system';
import type { ReactElement } from 'react';
-import { useCallback, useState, useEffect } from 'react';
+import { useCallback, useState } from 'react';
import { LogsQLEditor } from '../../components/logsql-editor';
import type { VictoriaLogsDatasourceSelector } from '../../model';
@@ -41,10 +41,11 @@ export function VictoriaLogsLogQueryEditor(props: VictoriaLogsQueryEditorProps):
// Local state for editor value to prevent query_range calls on every keystroke
const [localQuery, setLocalQuery] = useState(value.query);
- // Update local state when prop changes
- useEffect(() => {
+ const [previousQuery, setPreviousQuery] = useState(value.query);
+ if (value.query !== previousQuery) {
+ setPreviousQuery(value.query);
setLocalQuery(value.query);
- }, [value.query]);
+ }
const handleDatasourceChange: DatasourceSelectProps['onChange'] = (newDatasourceSelection) => {
if (!isVariableDatasource(newDatasourceSelection) && newDatasourceSelection.kind === DATASOURCE_KIND) {
diff --git a/victorialogs/src/queries/victorialogs-time-series-query/VictoriaLogsTimeSeriesQueryEditor.tsx b/victorialogs/src/queries/victorialogs-time-series-query/VictoriaLogsTimeSeriesQueryEditor.tsx
index b64c4a53a..f7d3a73ee 100644
--- a/victorialogs/src/queries/victorialogs-time-series-query/VictoriaLogsTimeSeriesQueryEditor.tsx
+++ b/victorialogs/src/queries/victorialogs-time-series-query/VictoriaLogsTimeSeriesQueryEditor.tsx
@@ -16,7 +16,7 @@ import { createModEnterHandler } from '@perses-dev/dashboards';
import type { DatasourceSelectProps, OptionsEditorProps } from '@perses-dev/plugin-system';
import { DatasourceSelect, isVariableDatasource, useDatasourceSelectValueToSelector } from '@perses-dev/plugin-system';
import type { ReactElement } from 'react';
-import { useCallback, useState, useEffect } from 'react';
+import { useCallback, useState } from 'react';
import { LogsQLEditor } from '../../components/logsql-editor';
import type { VictoriaLogsDatasourceSelector } from '../../model';
@@ -38,10 +38,11 @@ export function VictoriaLogsQueryEditor(props: VictoriaLogsQueryEditorProps): Re
// Local state for editor value to prevent query_range calls on every keystroke
const [localQuery, setLocalQuery] = useState(value.query);
- // Update local state when prop changes
- useEffect(() => {
+ const [previousQuery, setPreviousQuery] = useState(value.query);
+ if (value.query !== previousQuery) {
+ setPreviousQuery(value.query);
setLocalQuery(value.query);
- }, [value.query]);
+ }
const handleDatasourceChange: DatasourceSelectProps['onChange'] = (newDatasourceSelection) => {
if (!isVariableDatasource(newDatasourceSelection) && newDatasourceSelection.kind === DATASOURCE_KIND) {
diff --git a/vitest.shared.ts b/vitest.shared.ts
index fd58ce49a..35cfcfa6b 100644
--- a/vitest.shared.ts
+++ b/vitest.shared.ts
@@ -52,7 +52,7 @@ export function definePackageVitestConfig({
packageDir,
setupFiles = [],
passWithNoTests = false,
-}: VitestPackageOptions) {
+}: VitestPackageOptions): ReturnType {
return mergeConfig(
sharedConfig,
defineConfig({