Explorar o código

FE: Redirect the user to the wizard page if no clusters (#3481)

* Redirect the user to the wizard page if no clusters

* added test case

* changed test case for redirect to new cluster config page

* replaced mockedUsedNavigate with mockedNavigate

* added ability to show dashboard if clicking on it

---------

Co-authored-by: Oleg Shur <workshur@gmail.com>
David Bejanyan %!s(int64=2) %!d(string=hai) anos
pai
achega
e3ee4c7fa7

+ 9 - 2
kafka-ui-react-app/src/components/Dashboard/Dashboard.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useEffect } from 'react';
 import PageHeading from 'components/common/PageHeading/PageHeading';
 import * as Metrics from 'components/common/Metrics';
 import { Tag } from 'components/common/Tag/Tag.styled';
@@ -11,6 +11,7 @@ import useBoolean from 'lib/hooks/useBoolean';
 import { Button } from 'components/common/Button/Button';
 import { clusterNewConfigPath } from 'lib/paths';
 import { GlobalSettingsContext } from 'components/contexts/GlobalSettingsContext';
+import { useNavigate } from 'react-router-dom';
 
 import * as S from './Dashboard.styled';
 import ClusterName from './ClusterName';
@@ -20,7 +21,7 @@ const Dashboard: React.FC = () => {
   const clusters = useClusters();
   const { value: showOfflineOnly, toggle } = useBoolean(false);
   const appInfo = React.useContext(GlobalSettingsContext);
-
+  const navigate = useNavigate();
   const config = React.useMemo(() => {
     const clusterList = clusters.data || [];
     const offlineClusters = clusterList.filter(
@@ -55,6 +56,12 @@ const Dashboard: React.FC = () => {
     return initialColumns;
   }, []);
 
+  useEffect(() => {
+    if (appInfo.hasDynamicConfig && !clusters.data) {
+      navigate(clusterNewConfigPath);
+    }
+  }, [clusters, appInfo.hasDynamicConfig]);
+
   return (
     <>
       <PageHeading text="Dashboard" />

+ 45 - 0
kafka-ui-react-app/src/components/Dashboard/__test__/Dashboard.spec.tsx

@@ -0,0 +1,45 @@
+import React from 'react';
+import { useClusters } from 'lib/hooks/api/clusters';
+import Dashboard from 'components/Dashboard/Dashboard';
+import { Cluster, ServerStatus } from 'generated-sources';
+import { render } from 'lib/testHelpers';
+
+interface DataType {
+  data: Cluster[] | undefined;
+}
+jest.mock('lib/hooks/api/clusters');
+const mockedNavigate = jest.fn();
+jest.mock('react-router-dom', () => ({
+  ...jest.requireActual('react-router-dom'),
+  useNavigate: () => mockedNavigate,
+}));
+describe('Dashboard component', () => {
+  const renderComponent = (hasDynamicConfig: boolean, data: DataType) => {
+    const useClustersMock = useClusters as jest.Mock;
+    useClustersMock.mockReturnValue(data);
+    render(<Dashboard />, {
+      globalSettings: { hasDynamicConfig },
+    });
+  };
+  it('redirects to new cluster configuration page if there are no clusters and dynamic config is enabled', async () => {
+    await renderComponent(true, { data: undefined });
+
+    expect(mockedNavigate).toHaveBeenCalled();
+  });
+
+  it('should not navigate to new cluster config page when there are clusters', async () => {
+    await renderComponent(true, {
+      data: [{ name: 'Cluster 1', status: ServerStatus.ONLINE }],
+    });
+
+    expect(mockedNavigate).not.toHaveBeenCalled();
+  });
+
+  it('should not navigate to new cluster config page when there are no clusters and hasDynamicConfig is false', async () => {
+    await renderComponent(false, {
+      data: [],
+    });
+
+    expect(mockedNavigate).not.toHaveBeenCalled();
+  });
+});

+ 1 - 1
kafka-ui-react-app/src/components/contexts/GlobalSettingsContext.tsx

@@ -2,7 +2,7 @@ import { useAppInfo } from 'lib/hooks/api/appConfig';
 import React from 'react';
 import { ApplicationInfoEnabledFeaturesEnum } from 'generated-sources';
 
-interface GlobalSettingsContextProps {
+export interface GlobalSettingsContextProps {
   hasDynamicConfig: boolean;
 }
 

+ 7 - 2
kafka-ui-react-app/src/lib/testHelpers.tsx

@@ -26,7 +26,10 @@ import {
 } from '@tanstack/react-query';
 import { ConfirmContextProvider } from 'components/contexts/ConfirmContext';
 import ConfirmationModal from 'components/common/ConfirmationModal/ConfirmationModal';
-import { GlobalSettingsContext } from 'components/contexts/GlobalSettingsContext';
+import {
+  GlobalSettingsContext,
+  GlobalSettingsContextProps,
+} from 'components/contexts/GlobalSettingsContext';
 import { UserInfoRolesAccessContext } from 'components/contexts/UserInfoRolesAccessContext';
 
 import { RolesType, modifyRolesData } from './permissions';
@@ -35,6 +38,7 @@ interface CustomRenderOptions extends Omit<RenderOptions, 'wrapper'> {
   preloadedState?: Partial<RootState>;
   store?: Store<Partial<RootState>, AnyAction>;
   initialEntries?: MemoryRouterProps['initialEntries'];
+  globalSettings?: GlobalSettingsContextProps;
   userInfo?: {
     roles?: RolesType;
     rbacFlag: boolean;
@@ -110,6 +114,7 @@ const customRender = (
       preloadedState,
     }),
     initialEntries,
+    globalSettings = { hasDynamicConfig: false },
     userInfo,
     ...renderOptions
   }: CustomRenderOptions = {}
@@ -119,7 +124,7 @@ const customRender = (
     children,
   }) => (
     <TestQueryClientProvider>
-      <GlobalSettingsContext.Provider value={{ hasDynamicConfig: false }}>
+      <GlobalSettingsContext.Provider value={globalSettings}>
         <ThemeProvider theme={theme}>
           <TestUserInfoProvider data={userInfo}>
             <ConfirmContextProvider>