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 (