From 135055cb4515ffb7c05ee51134136e817a924fbd Mon Sep 17 00:00:00 2001 From: Renat Kalimulin <103274228+Nilumilak@users.noreply.github.com> Date: Thu, 7 Mar 2024 12:41:16 +0300 Subject: [PATCH] FE: Create connector button throws 404 if KC cluster is inaccessible (#136) Co-authored-by: Roman Zabaluev --- .../src/components/Connect/List/ListPage.tsx | 40 ++++++++++++------- .../Connect/List/__tests__/ListPage.spec.tsx | 27 ++++++++++++- .../src/components/common/Tooltip/Tooltip.tsx | 10 ++++- 3 files changed, 58 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/Connect/List/ListPage.tsx b/frontend/src/components/Connect/List/ListPage.tsx index 94ec8354c..5d61ff3c4 100644 --- a/frontend/src/components/Connect/List/ListPage.tsx +++ b/frontend/src/components/Connect/List/ListPage.tsx @@ -1,21 +1,23 @@ import React, { Suspense } from 'react'; import useAppParams from 'lib/hooks/useAppParams'; -import { clusterConnectorNewRelativePath, ClusterNameRoute } from 'lib/paths'; +import { ClusterNameRoute, clusterConnectorNewRelativePath } from 'lib/paths'; import ClusterContext from 'components/contexts/ClusterContext'; import Search from 'components/common/Search/Search'; import * as Metrics from 'components/common/Metrics'; import PageHeading from 'components/common/PageHeading/PageHeading'; -import { ActionButton } from 'components/common/ActionComponent'; +import Tooltip from 'components/common/Tooltip/Tooltip'; import { ControlPanelWrapper } from 'components/common/ControlPanel/ControlPanel.styled'; import PageLoader from 'components/common/PageLoader/PageLoader'; -import { Action, ConnectorState, ResourceType } from 'generated-sources'; -import { useConnectors } from 'lib/hooks/api/kafkaConnect'; +import { ConnectorState, Action, ResourceType } from 'generated-sources'; +import { useConnectors, useConnects } from 'lib/hooks/api/kafkaConnect'; +import { ActionButton } from 'components/common/ActionComponent'; import List from './List'; const ListPage: React.FC = () => { const { isReadOnly } = React.useContext(ClusterContext); const { clusterName } = useAppParams(); + const { data: connects = [] } = useConnects(clusterName); // Fetches all connectors from the API, without search criteria. Used to display general metrics. const { data: connectorsMetrics, isLoading } = useConnectors(clusterName); @@ -33,17 +35,25 @@ const ListPage: React.FC = () => { <> {!isReadOnly && ( - - Create Connector - + + Create Connector + + } + showTooltip={!connects.length} + content="No Connects available" + placement="left" + /> )} diff --git a/frontend/src/components/Connect/List/__tests__/ListPage.spec.tsx b/frontend/src/components/Connect/List/__tests__/ListPage.spec.tsx index 7da8e47e8..0cbe4dd78 100644 --- a/frontend/src/components/Connect/List/__tests__/ListPage.spec.tsx +++ b/frontend/src/components/Connect/List/__tests__/ListPage.spec.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { connectors } from 'lib/fixtures/kafkaConnect'; +import { connectors, connects } from 'lib/fixtures/kafkaConnect'; import ClusterContext, { ContextProps, initialValue, @@ -8,7 +8,7 @@ import ListPage from 'components/Connect/List/ListPage'; import { screen, within } from '@testing-library/react'; import { render, WithRoute } from 'lib/testHelpers'; import { clusterConnectorsPath } from 'lib/paths'; -import { useConnectors } from 'lib/hooks/api/kafkaConnect'; +import { useConnectors, useConnects } from 'lib/hooks/api/kafkaConnect'; jest.mock('components/Connect/List/List', () => () => (
Connectors List
@@ -16,6 +16,7 @@ jest.mock('components/Connect/List/List', () => () => ( jest.mock('lib/hooks/api/kafkaConnect', () => ({ useConnectors: jest.fn(), + useConnects: jest.fn(), })); jest.mock('components/common/Icons/SpinnerIcon', () => () => 'progressbar'); @@ -28,6 +29,10 @@ describe('Connectors List Page', () => { isLoading: false, data: [], })); + + (useConnects as jest.Mock).mockImplementation(() => ({ + data: connects, + })); }); const renderComponent = async (contextValue: ContextProps = initialValue) => @@ -178,4 +183,22 @@ describe('Connectors List Page', () => { expect(failedTasksIndicator).toHaveTextContent('Failed Tasks 1'); }); }); + + describe('Create new connector', () => { + it('Create new connector button is enabled when connects list is not empty', async () => { + await renderComponent(); + + expect(screen.getByText('Create Connector')).toBeEnabled(); + }); + + it('Create new connector button is disabled when connects list is empty', async () => { + (useConnects as jest.Mock).mockImplementation(() => ({ + data: [], + })); + + await renderComponent(); + + expect(screen.getByText('Create Connector')).toBeDisabled(); + }); + }); }); diff --git a/frontend/src/components/common/Tooltip/Tooltip.tsx b/frontend/src/components/common/Tooltip/Tooltip.tsx index 0764320f5..4d31585c4 100644 --- a/frontend/src/components/common/Tooltip/Tooltip.tsx +++ b/frontend/src/components/common/Tooltip/Tooltip.tsx @@ -12,9 +12,15 @@ interface TooltipProps { value: React.ReactNode; content: string; placement?: Placement; + showTooltip?: boolean; } -const Tooltip: React.FC = ({ value, content, placement }) => { +const Tooltip: React.FC = ({ + value, + content, + placement, + showTooltip = true, +}) => { const [open, setOpen] = useState(false); const { x, y, refs, strategy, context } = useFloating({ open, @@ -28,7 +34,7 @@ const Tooltip: React.FC = ({ value, content, placement }) => {
{value}
- {open && ( + {showTooltip && open && (