From 90e3cb677564d0ac514b8ae1b8f868efeb080e6d Mon Sep 17 00:00:00 2001 From: Abhi kumar Date: Wed, 18 Mar 2026 19:06:23 +0530 Subject: [PATCH] feat: replaced external apis barchart with the new bar chart (#10460) * feat: replaced external apis barchart with the new bar chart * fix: tests * chore: fixed tsc --- .../components/StatusCodeBarCharts.tsx | 103 ++++++++---------- .../Domains/DomainDetails/components/utils.ts | 83 ++++++++++++++ .../__tests__/StatusCodeBarCharts.test.tsx | 33 +++++- 3 files changed, 154 insertions(+), 65 deletions(-) create mode 100644 frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/utils.ts diff --git a/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/StatusCodeBarCharts.tsx b/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/StatusCodeBarCharts.tsx index 2fbb62cdac..350d931123 100644 --- a/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/StatusCodeBarCharts.tsx +++ b/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/StatusCodeBarCharts.tsx @@ -3,16 +3,14 @@ import { UseQueryResult } from 'react-query'; import { Color } from '@signozhq/design-tokens'; import { Button, Card, Skeleton, Typography } from 'antd'; import cx from 'classnames'; -import { useGetGraphCustomSeries } from 'components/CeleryTask/useGetGraphCustomSeries'; import { useNavigateToExplorer } from 'components/CeleryTask/useNavigateToExplorer'; -import Uplot from 'components/Uplot'; -import { PANEL_TYPES } from 'constants/queryBuilder'; import { getCustomFiltersForBarChart, getFormattedEndPointStatusCodeChartData, getStatusCodeBarChartWidgetData, statusCodeWidgetInfo, } from 'container/ApiMonitoring/utils'; +import BarChart from 'container/DashboardContainer/visualization/charts/BarChart/BarChart'; import { handleGraphClick } from 'container/GridCardLayout/GridCard/utils'; import { useGraphClickToShowButton } from 'container/GridCardLayout/useGraphClickToShowButton'; import useNavigateToExplorerPages from 'container/GridCardLayout/useNavigateToExplorerPages'; @@ -20,15 +18,16 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; import { useNotifications } from 'hooks/useNotifications'; -import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; +import { LegendPosition } from 'lib/uPlotV2/components/types'; import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils'; +import { useTimezone } from 'providers/Timezone'; import { SuccessResponse } from 'types/api'; import { Widgets } from 'types/api/dashboard/getAll'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; -import { Options } from 'uplot'; import ErrorState from './ErrorState'; +import { prepareStatusCodeBarChartsConfig } from './utils'; function StatusCodeBarCharts({ endPointStatusCodeBarChartsDataQuery, @@ -67,13 +66,6 @@ function StatusCodeBarCharts({ } = endPointStatusCodeLatencyBarChartsDataQuery; const { startTime: minTime, endTime: maxTime } = timeRange; - const legendScrollPositionRef = useRef<{ - scrollTop: number; - scrollLeft: number; - }>({ - scrollTop: 0, - scrollLeft: 0, - }); const graphRef = useRef(null); const dimensions = useResizeObserver(graphRef); @@ -119,6 +111,7 @@ function StatusCodeBarCharts({ const navigateToExplorer = useNavigateToExplorer(); const { currentQuery } = useQueryBuilder(); + const { timezone } = useTimezone(); const navigateToExplorerPages = useNavigateToExplorerPages(); const { notifications } = useNotifications(); @@ -134,12 +127,6 @@ function StatusCodeBarCharts({ [], ); - const { getCustomSeries } = useGetGraphCustomSeries({ - isDarkMode, - drawStyle: 'bars', - colorMapping, - }); - const widget = useMemo( () => getStatusCodeBarChartWidgetData(domainName, { @@ -193,49 +180,36 @@ function StatusCodeBarCharts({ ], ); - const options = useMemo( - () => - getUPlotChartOptions({ - apiResponse: - currentWidgetInfoIndex === 0 - ? formattedEndPointStatusCodeBarChartsDataPayload - : formattedEndPointStatusCodeLatencyBarChartsDataPayload, - isDarkMode, - dimensions, - yAxisUnit: statusCodeWidgetInfo[currentWidgetInfoIndex].yAxisUnit, - softMax: null, - softMin: null, - minTimeScale: minTime, - maxTimeScale: maxTime, - panelType: PANEL_TYPES.BAR, - onClickHandler: graphClickHandler, - customSeries: getCustomSeries, - onDragSelect, - colorMapping, - query: currentQuery, - legendScrollPosition: legendScrollPositionRef.current, - setLegendScrollPosition: (position: { - scrollTop: number; - scrollLeft: number; - }) => { - legendScrollPositionRef.current = position; - }, - }), - [ - minTime, - maxTime, - currentWidgetInfoIndex, - dimensions, - formattedEndPointStatusCodeBarChartsDataPayload, - formattedEndPointStatusCodeLatencyBarChartsDataPayload, + const config = useMemo(() => { + const apiResponse = + currentWidgetInfoIndex === 0 + ? formattedEndPointStatusCodeBarChartsDataPayload + : formattedEndPointStatusCodeLatencyBarChartsDataPayload; + return prepareStatusCodeBarChartsConfig({ + timezone, isDarkMode, - graphClickHandler, - getCustomSeries, + query: currentQuery, onDragSelect, + onClick: graphClickHandler, + apiResponse, + minTimeScale: minTime, + maxTimeScale: maxTime, + yAxisUnit: statusCodeWidgetInfo[currentWidgetInfoIndex].yAxisUnit, colorMapping, - currentQuery, - ], - ); + }); + }, [ + currentQuery, + isDarkMode, + minTime, + maxTime, + graphClickHandler, + onDragSelect, + formattedEndPointStatusCodeBarChartsDataPayload, + formattedEndPointStatusCodeLatencyBarChartsDataPayload, + timezone, + currentWidgetInfoIndex, + colorMapping, + ]); const renderCardContent = useCallback( (query: UseQueryResult, unknown>): JSX.Element => { @@ -253,11 +227,20 @@ function StatusCodeBarCharts({ !query.isLoading && !query?.data?.payload?.data?.result?.length, })} > - + ); }, - [options, chartData], + [config, chartData, dimensions, timezone], ); return ( diff --git a/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/utils.ts b/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/utils.ts new file mode 100644 index 0000000000..42469e9932 --- /dev/null +++ b/frontend/src/container/ApiMonitoring/Explorer/Domains/DomainDetails/components/utils.ts @@ -0,0 +1,83 @@ +import { ExecStats } from 'api/v5/v5'; +import { Timezone } from 'components/CustomTimePicker/timezoneUtils'; +import { PANEL_TYPES } from 'constants/queryBuilder'; +import { buildBaseConfig } from 'container/DashboardContainer/visualization/panels/utils/baseConfigBuilder'; +import { getLegend } from 'lib/dashboard/getQueryResults'; +import getLabelName from 'lib/getLabelName'; +import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; +import { DrawStyle } from 'lib/uPlotV2/config/types'; +import { UPlotConfigBuilder } from 'lib/uPlotV2/config/UPlotConfigBuilder'; +import { get } from 'lodash-es'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; +import { Query } from 'types/api/queryBuilder/queryBuilderData'; +import { QueryData } from 'types/api/widgets/getQuery'; +import { v4 } from 'uuid'; + +export const prepareStatusCodeBarChartsConfig = ({ + timezone, + isDarkMode, + query, + onDragSelect, + onClick, + apiResponse, + minTimeScale, + maxTimeScale, + yAxisUnit, + colorMapping, +}: { + timezone: Timezone; + isDarkMode: boolean; + query: Query; + onDragSelect: (startTime: number, endTime: number) => void; + onClick?: OnClickPluginOpts['onClick']; + minTimeScale?: number; + maxTimeScale?: number; + apiResponse: MetricRangePayloadProps; + yAxisUnit?: string; + colorMapping?: Record; +}): UPlotConfigBuilder => { + const stepIntervals: ExecStats['stepIntervals'] = get( + apiResponse, + 'data.newResult.meta.stepIntervals', + {}, + ); + const minStepInterval = Math.min(...Object.values(stepIntervals)); + + const config = buildBaseConfig({ + id: v4(), + yAxisUnit: yAxisUnit, + apiResponse, + isDarkMode, + onDragSelect, + timezone, + onClick, + minTimeScale, + maxTimeScale, + stepInterval: minStepInterval, + panelType: PANEL_TYPES.BAR, + }); + + const seriesList: QueryData[] = apiResponse?.data?.result || []; + seriesList.forEach((series) => { + const baseLabelName = getLabelName( + series.metric, + series.queryName || '', // query + series.legend || '', + ); + + const label = query ? getLegend(series, query, baseLabelName) : baseLabelName; + + const currentStepInterval = get(stepIntervals, series.queryName, undefined); + + config.addSeries({ + scaleKey: 'y', + drawStyle: DrawStyle.Bar, + label: label, + colorMapping: colorMapping ?? {}, + isDarkMode, + stepInterval: currentStepInterval, + }); + }); + + return config; +}; diff --git a/frontend/src/container/ApiMonitoring/__tests__/StatusCodeBarCharts.test.tsx b/frontend/src/container/ApiMonitoring/__tests__/StatusCodeBarCharts.test.tsx index 4d563a2b57..238d549d85 100644 --- a/frontend/src/container/ApiMonitoring/__tests__/StatusCodeBarCharts.test.tsx +++ b/frontend/src/container/ApiMonitoring/__tests__/StatusCodeBarCharts.test.tsx @@ -21,10 +21,15 @@ interface MockQueryResult { } // Mocks -jest.mock('components/Uplot', () => ({ - __esModule: true, - default: jest.fn().mockImplementation(() =>
), -})); +jest.mock( + 'container/DashboardContainer/visualization/charts/BarChart/BarChart', + () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
), + }), +); jest.mock('components/CeleryTask/useGetGraphCustomSeries', () => ({ useGetGraphCustomSeries: (): { getCustomSeries: jest.Mock } => ({ @@ -70,6 +75,24 @@ jest.mock('hooks/useNotifications', () => ({ useNotifications: (): { notifications: [] } => ({ notifications: [] }), })); +jest.mock('providers/Timezone', () => ({ + useTimezone: (): { + timezone: { + name: string; + value: string; + offset: string; + searchIndex: string; + }; + } => ({ + timezone: { + name: 'UTC', + value: 'UTC', + offset: '+00:00', + searchIndex: 'UTC', + }, + }), +})); + jest.mock('lib/uPlotLib/getUplotChartOptions', () => ({ getUPlotChartOptions: jest.fn().mockReturnValue({}), })); @@ -319,7 +342,7 @@ describe('StatusCodeBarCharts', () => { mockData.payload, 'sum', ); - expect(screen.getByTestId('uplot-mock')).toBeInTheDocument(); + expect(screen.getByTestId('bar-chart-mock')).toBeInTheDocument(); expect(screen.getByText('Number of calls')).toBeInTheDocument(); expect(screen.getByText('Latency')).toBeInTheDocument(); });