diff --git a/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx b/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx index 3b84ad1bbe..df0346e55c 100644 --- a/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx +++ b/frontend/src/container/MetricsExplorer/Explorer/Explorer.tsx @@ -12,6 +12,7 @@ import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { addEmptyWidgetInDashboardJSONWithQuery } from 'hooks/dashboard/utils'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; +import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useNotifications } from 'hooks/useNotifications'; import { useSafeNavigate } from 'hooks/useSafeNavigate'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; @@ -57,6 +58,8 @@ function Explorer(): JSX.Element { [currentQuery, updateAllQueriesOperators], ); + useShareBuilderUrl(exportDefaultQuery); + const handleExport = useCallback( (dashboard: Dashboard | null): void => { if (!dashboard) return; diff --git a/frontend/src/container/MetricsExplorer/Explorer/__tests__/Explorer.test.tsx b/frontend/src/container/MetricsExplorer/Explorer/__tests__/Explorer.test.tsx new file mode 100644 index 0000000000..8f372698d4 --- /dev/null +++ b/frontend/src/container/MetricsExplorer/Explorer/__tests__/Explorer.test.tsx @@ -0,0 +1,101 @@ +import { render } from '@testing-library/react'; +import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; +import ROUTES from 'constants/routes'; +import * as useOptionsMenuHooks from 'container/OptionsMenu'; +import * as useUpdateDashboardHooks from 'hooks/dashboard/useUpdateDashboard'; +import * as useQueryBuilderHooks from 'hooks/queryBuilder/useQueryBuilder'; +import { Provider } from 'react-redux'; +import { MemoryRouter } from 'react-router-dom'; +import store from 'store'; +import { DataSource } from 'types/common/queryBuilder'; + +import Explorer from '../Explorer'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: (): { pathname: string } => ({ + pathname: `${ROUTES.METRICS_EXPLORER_EXPLORER}`, + }), +})); +jest.mock('hooks/useSafeNavigate', () => ({ + useSafeNavigate: (): any => ({ + safeNavigate: jest.fn(), + }), +})); +jest.mock('hooks/useNotifications', () => ({ + useNotifications: (): any => ({ + notifications: { + error: jest.fn(), + }, + }), +})); +jest.mock('uplot', () => { + const paths = { + spline: jest.fn(), + bars: jest.fn(), + }; + const uplotMock = jest.fn(() => ({ + paths, + })); + return { + paths, + default: uplotMock, + }; +}); +jest.mock('react-redux', () => ({ + ...jest.requireActual('react-redux'), + useSelector: (): any => ({ + globalTime: { + selectedTime: { + startTime: 1713734400000, + endTime: 1713738000000, + }, + maxTime: 1713738000000, + minTime: 1713734400000, + }, + }), +})); + +jest.spyOn(useUpdateDashboardHooks, 'useUpdateDashboard').mockReturnValue({ + mutate: jest.fn(), + isLoading: false, +} as any); + +jest.spyOn(useOptionsMenuHooks, 'useOptionsMenu').mockReturnValue({ + selectColumns: [], +} as any); + +const mockUpdateAllQueriesOperators = jest.fn(); +const mockUseQueryBuilderData = { + handleRunQuery: jest.fn(), + stagedQuery: initialQueriesMap[DataSource.METRICS], + updateAllQueriesOperators: mockUpdateAllQueriesOperators, + currentQuery: initialQueriesMap[DataSource.METRICS], + resetQuery: jest.fn(), + redirectWithQueryBuilderData: jest.fn(), +}; +jest.spyOn(useQueryBuilderHooks, 'useQueryBuilder').mockReturnValue({ + mockUseQueryBuilderData, +} as any); + +describe('Explorer', () => { + it('should render Explorer query builder with metrics datasource selected', () => { + jest.spyOn(useQueryBuilderHooks, 'useQueryBuilder').mockReturnValue({ + ...mockUseQueryBuilderData, + // Initially have a different datasource + stagedQuery: initialQueriesMap[DataSource.TRACES], + } as any); + render( + + + + + , + ); + expect(mockUpdateAllQueriesOperators).toHaveBeenCalledWith( + initialQueriesMap[DataSource.METRICS], + PANEL_TYPES.TIME_SERIES, + DataSource.METRICS, + ); + }); +}); diff --git a/frontend/src/container/MetricsExplorer/Summary/Summary.tsx b/frontend/src/container/MetricsExplorer/Summary/Summary.tsx index a16acd734a..208248a2d7 100644 --- a/frontend/src/container/MetricsExplorer/Summary/Summary.tsx +++ b/frontend/src/container/MetricsExplorer/Summary/Summary.tsx @@ -1,16 +1,19 @@ import './Summary.styles.scss'; import * as Sentry from '@sentry/react'; +import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder'; import { usePageSize } from 'container/InfraMonitoringK8s/utils'; import { useGetMetricsList } from 'hooks/metricsExplorer/useGetMetricsList'; import { useGetMetricsTreeMap } from 'hooks/metricsExplorer/useGetMetricsTreeMap'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations'; +import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useCallback, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { TagFilter } from 'types/api/queryBuilder/queryBuilderData'; +import { DataSource } from 'types/common/queryBuilder'; import { GlobalReducer } from 'types/reducer/globalTime'; import InspectModal from '../Inspect'; @@ -25,13 +28,15 @@ import { getMetricsListQuery, } from './utils'; +const DEFAULT_ORDER_BY: OrderByPayload = { + columnName: 'samples', + order: 'desc', +}; + function Summary(): JSX.Element { const { pageSize, setPageSize } = usePageSize('metricsExplorer'); const [currentPage, setCurrentPage] = useState(1); - const [orderBy, setOrderBy] = useState({ - columnName: 'samples', - order: 'desc', - }); + const [orderBy, setOrderBy] = useState(DEFAULT_ORDER_BY); const [heatmapView, setHeatmapView] = useState( TreemapViewType.TIMESERIES, ); @@ -45,7 +50,31 @@ function Summary(): JSX.Element { (state) => state.globalTime, ); - const { currentQuery } = useQueryBuilder(); + const { currentQuery, updateAllQueriesOperators } = useQueryBuilder(); + + const defaultQuery = useMemo(() => { + const query = updateAllQueriesOperators( + initialQueriesMap.metrics, + PANEL_TYPES.LIST, + DataSource.METRICS, + ); + + return { + ...query, + builder: { + ...query.builder, + queryData: [ + { + ...query.builder.queryData[0], + orderBy: [DEFAULT_ORDER_BY], + }, + ], + }, + }; + }, [updateAllQueriesOperators]); + + useShareBuilderUrl(defaultQuery); + const queryFilters = useMemo( () => currentQuery?.builder?.queryData[0]?.filters || { diff --git a/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx b/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx index 90fe868f6f..1b416d4f64 100644 --- a/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx +++ b/frontend/src/pages/MetricsExplorer/MetricsExplorerPage.tsx @@ -2,12 +2,8 @@ import './MetricsExplorerPage.styles.scss'; import RouteTab from 'components/RouteTab'; import { TabRoutes } from 'components/RouteTab/types'; -import { initialQueriesMap } from 'constants/queryBuilder'; -import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import history from 'lib/history'; -import { useLayoutEffect, useMemo } from 'react'; import { useLocation } from 'react-use'; -import { DataSource } from 'types/common/queryBuilder'; import { Explorer, Summary } from './constants'; @@ -16,14 +12,6 @@ function MetricsExplorerPage(): JSX.Element { const routes: TabRoutes[] = [Summary, Explorer]; - const initialQuery = useMemo(() => initialQueriesMap[DataSource.METRICS], []); - const { resetQuery } = useQueryBuilder(); - - useLayoutEffect(() => { - resetQuery(initialQuery); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return (