2024-06-10 17:15:30 +05:30
|
|
|
import './UplotPanelWrapper.styles.scss';
|
|
|
|
|
|
|
|
|
|
import { Alert } from 'antd';
|
2024-04-02 16:40:41 +05:30
|
|
|
import { ToggleGraphProps } from 'components/Graph/types';
|
|
|
|
|
import Uplot from 'components/Uplot';
|
|
|
|
|
import { PANEL_TYPES } from 'constants/queryBuilder';
|
|
|
|
|
import GraphManager from 'container/GridCardLayout/GridCard/FullView/GraphManager';
|
|
|
|
|
import { getLocalStorageGraphVisibilityState } from 'container/GridCardLayout/GridCard/utils';
|
2025-09-07 11:50:35 +05:30
|
|
|
import { getUplotClickData } from 'container/QueryTable/Drilldown/drilldownUtils';
|
|
|
|
|
import useGraphContextMenu from 'container/QueryTable/Drilldown/useGraphContextMenu';
|
2024-04-02 16:40:41 +05:30
|
|
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|
|
|
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
|
|
|
|
import { useResizeObserver } from 'hooks/useDimensions';
|
|
|
|
|
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
|
|
|
|
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
2024-06-10 17:15:30 +05:30
|
|
|
import { cloneDeep, isEqual, isUndefined } from 'lodash-es';
|
2024-04-02 16:40:41 +05:30
|
|
|
import _noop from 'lodash-es/noop';
|
2025-09-07 11:50:35 +05:30
|
|
|
import { ContextMenu, useCoordinates } from 'periscope/components/ContextMenu';
|
2024-04-02 16:40:41 +05:30
|
|
|
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
2024-12-16 10:27:20 +04:30
|
|
|
import { useTimezone } from 'providers/Timezone';
|
2025-09-07 11:50:35 +05:30
|
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
|
|
|
import { DataSource } from 'types/common/queryBuilder';
|
2024-12-16 10:27:20 +04:30
|
|
|
import uPlot from 'uplot';
|
2024-04-02 16:40:41 +05:30
|
|
|
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
|
|
|
|
import { getTimeRange } from 'utils/getTimeRange';
|
|
|
|
|
|
|
|
|
|
import { PanelWrapperProps } from './panelWrapper.types';
|
2025-09-07 11:50:35 +05:30
|
|
|
import { getTimeRangeFromStepInterval, isApmMetric } from './utils';
|
2024-04-02 16:40:41 +05:30
|
|
|
|
|
|
|
|
function UplotPanelWrapper({
|
|
|
|
|
queryResponse,
|
|
|
|
|
widget,
|
|
|
|
|
isFullViewMode,
|
|
|
|
|
setGraphVisibility,
|
|
|
|
|
graphVisibility,
|
|
|
|
|
onToggleModelHandler,
|
|
|
|
|
onClickHandler,
|
|
|
|
|
onDragSelect,
|
2024-04-09 13:36:19 +05:30
|
|
|
selectedGraph,
|
2024-08-29 16:36:56 +05:30
|
|
|
customTooltipElement,
|
2025-01-27 08:53:19 +05:30
|
|
|
customSeries,
|
2025-09-07 11:50:35 +05:30
|
|
|
enableDrillDown = false,
|
2024-04-02 16:40:41 +05:30
|
|
|
}: PanelWrapperProps): JSX.Element {
|
|
|
|
|
const { toScrollWidgetId, setToScrollWidgetId } = useDashboard();
|
|
|
|
|
const isDarkMode = useIsDarkMode();
|
|
|
|
|
const lineChartRef = useRef<ToggleGraphProps>();
|
|
|
|
|
const graphRef = useRef<HTMLDivElement>(null);
|
2025-09-23 17:43:13 +05:30
|
|
|
const legendScrollPositionRef = useRef<{
|
|
|
|
|
scrollTop: number;
|
|
|
|
|
scrollLeft: number;
|
|
|
|
|
}>({
|
|
|
|
|
scrollTop: 0,
|
|
|
|
|
scrollLeft: 0,
|
|
|
|
|
});
|
2024-04-02 16:40:41 +05:30
|
|
|
const [minTimeScale, setMinTimeScale] = useState<number>();
|
|
|
|
|
const [maxTimeScale, setMaxTimeScale] = useState<number>();
|
|
|
|
|
const { currentQuery } = useQueryBuilder();
|
|
|
|
|
|
2024-06-10 17:15:30 +05:30
|
|
|
const [hiddenGraph, setHiddenGraph] = useState<{ [key: string]: boolean }>();
|
|
|
|
|
|
2024-04-02 16:40:41 +05:30
|
|
|
useEffect(() => {
|
|
|
|
|
if (toScrollWidgetId === widget.id) {
|
|
|
|
|
graphRef.current?.scrollIntoView({
|
|
|
|
|
behavior: 'smooth',
|
|
|
|
|
block: 'center',
|
|
|
|
|
});
|
|
|
|
|
graphRef.current?.focus();
|
|
|
|
|
setToScrollWidgetId('');
|
|
|
|
|
}
|
|
|
|
|
}, [toScrollWidgetId, setToScrollWidgetId, widget.id]);
|
|
|
|
|
|
|
|
|
|
useEffect((): void => {
|
|
|
|
|
const { startTime, endTime } = getTimeRange(queryResponse);
|
|
|
|
|
|
|
|
|
|
setMinTimeScale(startTime);
|
|
|
|
|
setMaxTimeScale(endTime);
|
|
|
|
|
}, [queryResponse]);
|
|
|
|
|
|
|
|
|
|
const containerDimensions = useResizeObserver(graphRef);
|
|
|
|
|
|
2025-09-07 11:50:35 +05:30
|
|
|
const {
|
|
|
|
|
coordinates,
|
|
|
|
|
popoverPosition,
|
|
|
|
|
clickedData,
|
|
|
|
|
onClose,
|
|
|
|
|
onClick,
|
|
|
|
|
subMenu,
|
|
|
|
|
setSubMenu,
|
|
|
|
|
} = useCoordinates();
|
|
|
|
|
const { menuItemsConfig } = useGraphContextMenu({
|
|
|
|
|
widgetId: widget.id || '',
|
|
|
|
|
query: widget.query,
|
|
|
|
|
graphData: clickedData,
|
|
|
|
|
onClose,
|
|
|
|
|
coordinates,
|
|
|
|
|
subMenu,
|
|
|
|
|
setSubMenu,
|
|
|
|
|
contextLinks: widget.contextLinks,
|
|
|
|
|
panelType: widget.panelTypes,
|
|
|
|
|
queryRange: queryResponse,
|
|
|
|
|
});
|
|
|
|
|
|
2024-04-02 16:40:41 +05:30
|
|
|
useEffect(() => {
|
|
|
|
|
const {
|
|
|
|
|
graphVisibilityStates: localStoredVisibilityState,
|
|
|
|
|
} = getLocalStorageGraphVisibilityState({
|
|
|
|
|
apiResponse: queryResponse.data?.payload.data.result || [],
|
|
|
|
|
name: widget.id,
|
|
|
|
|
});
|
|
|
|
|
if (setGraphVisibility) {
|
|
|
|
|
setGraphVisibility(localStoredVisibilityState);
|
|
|
|
|
}
|
2025-06-04 11:50:51 +05:30
|
|
|
}, [
|
|
|
|
|
queryResponse?.data?.payload?.data?.result,
|
|
|
|
|
setGraphVisibility,
|
|
|
|
|
widget.id,
|
|
|
|
|
]);
|
2024-04-02 16:40:41 +05:30
|
|
|
|
|
|
|
|
if (queryResponse.data && widget.panelTypes === PANEL_TYPES.BAR) {
|
|
|
|
|
const sortedSeriesData = getSortedSeriesData(
|
|
|
|
|
queryResponse.data?.payload.data.result,
|
|
|
|
|
);
|
|
|
|
|
// eslint-disable-next-line no-param-reassign
|
|
|
|
|
queryResponse.data.payload.data.result = sortedSeriesData;
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-24 22:37:31 +05:30
|
|
|
const stackedBarChart = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
(selectedGraph
|
|
|
|
|
? selectedGraph === PANEL_TYPES.BAR
|
|
|
|
|
: widget?.panelTypes === PANEL_TYPES.BAR) && widget?.stackedBarChart,
|
|
|
|
|
[selectedGraph, widget?.panelTypes, widget?.stackedBarChart],
|
|
|
|
|
);
|
|
|
|
|
|
2024-04-02 16:40:41 +05:30
|
|
|
const chartData = getUPlotChartData(
|
|
|
|
|
queryResponse?.data?.payload,
|
|
|
|
|
widget.fillSpans,
|
2025-09-24 22:37:31 +05:30
|
|
|
stackedBarChart,
|
2024-06-10 17:15:30 +05:30
|
|
|
hiddenGraph,
|
2024-04-02 16:40:41 +05:30
|
|
|
);
|
|
|
|
|
|
2024-06-10 17:15:30 +05:30
|
|
|
useEffect(() => {
|
2025-09-24 22:37:31 +05:30
|
|
|
if (widget.panelTypes === PANEL_TYPES.BAR && stackedBarChart) {
|
2024-06-10 17:15:30 +05:30
|
|
|
const graphV = cloneDeep(graphVisibility)?.slice(1);
|
|
|
|
|
const isSomeSelectedLegend = graphV?.some((v) => v === false);
|
|
|
|
|
if (isSomeSelectedLegend) {
|
|
|
|
|
const hiddenIndex = graphV?.findIndex((v) => v === true);
|
|
|
|
|
if (!isUndefined(hiddenIndex) && hiddenIndex !== -1) {
|
|
|
|
|
const updatedHiddenGraph = { [hiddenIndex]: true };
|
|
|
|
|
if (!isEqual(hiddenGraph, updatedHiddenGraph)) {
|
|
|
|
|
setHiddenGraph(updatedHiddenGraph);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-09-24 22:37:31 +05:30
|
|
|
}, [graphVisibility, hiddenGraph, widget.panelTypes, stackedBarChart]);
|
2024-06-10 17:15:30 +05:30
|
|
|
|
2024-12-16 10:27:20 +04:30
|
|
|
const { timezone } = useTimezone();
|
|
|
|
|
|
2025-09-07 11:50:35 +05:30
|
|
|
const clickHandlerWithContextMenu = useCallback(
|
|
|
|
|
(...args: any[]) => {
|
|
|
|
|
const [
|
|
|
|
|
xValue,
|
|
|
|
|
,
|
|
|
|
|
,
|
|
|
|
|
,
|
|
|
|
|
metric,
|
|
|
|
|
queryData,
|
|
|
|
|
absoluteMouseX,
|
|
|
|
|
absoluteMouseY,
|
|
|
|
|
axesData,
|
|
|
|
|
focusedSeries,
|
|
|
|
|
] = args;
|
|
|
|
|
const data = getUplotClickData({
|
|
|
|
|
metric,
|
|
|
|
|
queryData,
|
|
|
|
|
absoluteMouseX,
|
|
|
|
|
absoluteMouseY,
|
|
|
|
|
focusedSeries,
|
|
|
|
|
});
|
|
|
|
|
// Compute time range if needed and if axes data is available
|
|
|
|
|
let timeRange;
|
|
|
|
|
if (axesData && queryData?.queryName) {
|
|
|
|
|
// Get the compositeQuery from the response params
|
|
|
|
|
const compositeQuery = (queryResponse?.data?.params as any)?.compositeQuery;
|
|
|
|
|
|
|
|
|
|
if (compositeQuery?.queries) {
|
|
|
|
|
// Find the specific query by name from the queries array
|
|
|
|
|
const specificQuery = compositeQuery.queries.find(
|
|
|
|
|
(query: any) => query.spec?.name === queryData.queryName,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Use the stepInterval from the specific query, fallback to default
|
|
|
|
|
const stepInterval = specificQuery?.spec?.stepInterval || 60;
|
|
|
|
|
timeRange = getTimeRangeFromStepInterval(
|
|
|
|
|
stepInterval,
|
|
|
|
|
metric?.clickedTimestamp || xValue, // Use the clicked timestamp if available, otherwise use the click position timestamp
|
|
|
|
|
specificQuery?.spec?.signal === DataSource.METRICS &&
|
|
|
|
|
isApmMetric(specificQuery?.spec?.aggregations[0]?.metricName),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data && data?.record?.queryName) {
|
|
|
|
|
onClick(data.coord, { ...data.record, label: data.label, timeRange });
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[onClick, queryResponse],
|
|
|
|
|
);
|
|
|
|
|
|
2024-04-02 16:40:41 +05:30
|
|
|
const options = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
getUPlotChartOptions({
|
|
|
|
|
id: widget?.id,
|
|
|
|
|
apiResponse: queryResponse.data?.payload,
|
|
|
|
|
dimensions: containerDimensions,
|
|
|
|
|
isDarkMode,
|
|
|
|
|
onDragSelect,
|
|
|
|
|
yAxisUnit: widget?.yAxisUnit,
|
2025-09-07 11:50:35 +05:30
|
|
|
onClickHandler: enableDrillDown
|
|
|
|
|
? clickHandlerWithContextMenu
|
|
|
|
|
: onClickHandler ?? _noop,
|
2024-04-02 16:40:41 +05:30
|
|
|
thresholds: widget.thresholds,
|
|
|
|
|
minTimeScale,
|
|
|
|
|
maxTimeScale,
|
|
|
|
|
softMax: widget.softMax === undefined ? null : widget.softMax,
|
|
|
|
|
softMin: widget.softMin === undefined ? null : widget.softMin,
|
|
|
|
|
graphsVisibilityStates: graphVisibility,
|
|
|
|
|
setGraphsVisibilityStates: setGraphVisibility,
|
2024-04-09 13:36:19 +05:30
|
|
|
panelType: selectedGraph || widget.panelTypes,
|
2024-04-02 16:40:41 +05:30
|
|
|
currentQuery,
|
2025-09-24 22:37:31 +05:30
|
|
|
stackBarChart: stackedBarChart,
|
2024-06-10 17:15:30 +05:30
|
|
|
hiddenGraph,
|
|
|
|
|
setHiddenGraph,
|
2024-08-29 16:36:56 +05:30
|
|
|
customTooltipElement,
|
2024-12-16 10:27:20 +04:30
|
|
|
tzDate: (timestamp: number) =>
|
|
|
|
|
uPlot.tzDate(new Date(timestamp * 1e3), timezone.value),
|
|
|
|
|
timezone: timezone.value,
|
2025-01-27 08:53:19 +05:30
|
|
|
customSeries,
|
2025-03-24 18:42:04 +05:30
|
|
|
isLogScale: widget?.isLogScale,
|
2025-05-27 14:49:35 +05:30
|
|
|
colorMapping: widget?.customLegendColors,
|
2025-05-27 13:50:40 +05:30
|
|
|
enhancedLegend: true, // Enable enhanced legend
|
|
|
|
|
legendPosition: widget?.legendPosition,
|
2025-07-31 12:16:55 +05:30
|
|
|
query: widget?.query || currentQuery,
|
2025-09-23 17:43:13 +05:30
|
|
|
legendScrollPosition: legendScrollPositionRef.current,
|
|
|
|
|
setLegendScrollPosition: (position: {
|
|
|
|
|
scrollTop: number;
|
|
|
|
|
scrollLeft: number;
|
|
|
|
|
}) => {
|
|
|
|
|
legendScrollPositionRef.current = position;
|
|
|
|
|
},
|
2024-04-02 16:40:41 +05:30
|
|
|
}),
|
|
|
|
|
[
|
|
|
|
|
queryResponse.data?.payload,
|
|
|
|
|
containerDimensions,
|
|
|
|
|
isDarkMode,
|
|
|
|
|
onDragSelect,
|
2025-09-07 11:50:35 +05:30
|
|
|
clickHandlerWithContextMenu,
|
2024-04-02 16:40:41 +05:30
|
|
|
minTimeScale,
|
|
|
|
|
maxTimeScale,
|
|
|
|
|
graphVisibility,
|
|
|
|
|
setGraphVisibility,
|
2024-04-09 13:36:19 +05:30
|
|
|
selectedGraph,
|
2024-04-02 16:40:41 +05:30
|
|
|
currentQuery,
|
2024-06-10 17:15:30 +05:30
|
|
|
hiddenGraph,
|
2024-08-29 16:36:56 +05:30
|
|
|
customTooltipElement,
|
2024-12-16 10:27:20 +04:30
|
|
|
timezone.value,
|
2025-01-27 08:53:19 +05:30
|
|
|
customSeries,
|
2025-09-07 11:50:35 +05:30
|
|
|
enableDrillDown,
|
|
|
|
|
onClickHandler,
|
2025-07-31 12:16:55 +05:30
|
|
|
widget,
|
2025-09-24 22:37:31 +05:30
|
|
|
stackedBarChart,
|
2024-04-02 16:40:41 +05:30
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div style={{ height: '100%', width: '100%' }} ref={graphRef}>
|
|
|
|
|
<Uplot options={options} data={chartData} ref={lineChartRef} />
|
2025-09-07 11:50:35 +05:30
|
|
|
<ContextMenu
|
|
|
|
|
coordinates={coordinates}
|
|
|
|
|
popoverPosition={popoverPosition}
|
|
|
|
|
title={menuItemsConfig.header as string}
|
|
|
|
|
items={menuItemsConfig.items}
|
|
|
|
|
onClose={onClose}
|
|
|
|
|
/>
|
2025-09-24 22:37:31 +05:30
|
|
|
{stackedBarChart && isFullViewMode && (
|
2024-06-10 17:15:30 +05:30
|
|
|
<Alert
|
|
|
|
|
message="Selecting multiple legends is currently not supported in case of stacked bar charts"
|
|
|
|
|
type="info"
|
|
|
|
|
className="info-text"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
2025-09-24 22:37:31 +05:30
|
|
|
{isFullViewMode && setGraphVisibility && !stackedBarChart && (
|
2024-04-02 16:40:41 +05:30
|
|
|
<GraphManager
|
2024-06-10 17:15:30 +05:30
|
|
|
data={getUPlotChartData(queryResponse?.data?.payload, widget.fillSpans)}
|
2024-04-02 16:40:41 +05:30
|
|
|
name={widget.id}
|
|
|
|
|
options={options}
|
|
|
|
|
yAxisUnit={widget.yAxisUnit}
|
|
|
|
|
onToggleModelHandler={onToggleModelHandler}
|
|
|
|
|
setGraphsVisibilityStates={setGraphVisibility}
|
|
|
|
|
graphsVisibilityStates={graphVisibility}
|
|
|
|
|
lineChartRef={lineChartRef}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default UplotPanelWrapper;
|