Skip to content

Commit 2be7f1b

Browse files
Vixtirgermanosin
andauthored
FE: Recalculation connectors stat on client/backed filter change (#1486)
Co-authored-by: German Osin <german.osin@gmail.com>
1 parent 3e80923 commit 2be7f1b

File tree

8 files changed

+100
-15
lines changed

8 files changed

+100
-15
lines changed

frontend/src/components/Connect/List/ConnectorsTable/ConnectorsTable.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import React from 'react';
1+
import React, { useCallback } from 'react';
22
import { FullConnectorInfo } from 'generated-sources';
33
import Table from 'components/common/NewTable';
44
import { useLocalStoragePersister } from 'components/common/NewTable/ColumnResizer/lib';
55
import { useQueryPersister } from 'components/common/NewTable/ColumnFilter';
66
import { VisibilityState } from '@tanstack/react-table';
7+
import { useFilteredConnectorsDispatch } from 'components/Connect/model/FilteredConnectorsProvider';
78

89
import { connectorsColumns } from './connectorsColumns/columns';
910

@@ -21,11 +22,16 @@ export const ConnectorsTable = ({
2122
columnSizingPersistKey = 'KafkaConnect',
2223
columnVisibility,
2324
}: ConnectorsTableProps) => {
25+
const dispath = useFilteredConnectorsDispatch();
2426
const filterPersister = useQueryPersister(connectorsColumns);
2527
const columnSizingPersister = useLocalStoragePersister(
2628
columnSizingPersistKey
2729
);
2830

31+
const onFilterRows = useCallback((rows: FullConnectorInfo[]) => {
32+
dispath({ type: 'updated', connectors: rows });
33+
}, []);
34+
2935
return (
3036
<Table
3137
data={connectors}
@@ -37,6 +43,7 @@ export const ConnectorsTable = ({
3743
setRowId={setRowId}
3844
filterPersister={filterPersister}
3945
columnVisibility={columnVisibility}
46+
onFilterRows={onFilterRows}
4047
/>
4148
);
4249
};

frontend/src/components/Connect/List/ListPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useSearchParams } from 'react-router-dom';
88
import useFts from 'components/common/Fts/useFts';
99
import Fts from 'components/common/Fts/Fts';
1010
import { FullConnectorInfo } from 'generated-sources';
11+
import { FilteredConnectorsProvider } from 'components/Connect/model/FilteredConnectorsProvider';
1112

1213
import * as S from './ListPage.styled';
1314
import List from './List';
@@ -26,8 +27,8 @@ const ListPage: React.FC = () => {
2627
);
2728

2829
return (
29-
<>
30-
<ConnectorsStatistics connectors={connectors} isLoading={isLoading} />
30+
<FilteredConnectorsProvider>
31+
<ConnectorsStatistics isLoading={isLoading} />
3132
<S.Search hasInput>
3233
<Search
3334
placeholder="Search by Connect Name, Status or Type"
@@ -37,7 +38,7 @@ const ListPage: React.FC = () => {
3738
<Suspense fallback={<PageLoader />}>
3839
<List connectors={connectors} />
3940
</Suspense>
40-
</>
41+
</FilteredConnectorsProvider>
4142
);
4243
};
4344

frontend/src/components/Connect/List/Statistics/Statistics.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import React, { FC, useMemo } from 'react';
22
import * as Statistics from 'components/common/Statistics';
3-
import { FullConnectorInfo } from 'generated-sources';
3+
import { useFilteredConnectors } from 'components/Connect/model/FilteredConnectorsProvider';
44

55
import { computeStatistics } from './models/computeStatistics';
66

77
interface ConnectorsStatisticsProps {
8-
connectors: FullConnectorInfo[];
98
isLoading: boolean;
109
}
11-
const ConnectorsStatistics: FC<ConnectorsStatisticsProps> = ({
12-
connectors,
13-
isLoading,
14-
}) => {
15-
const statistics = useMemo(() => computeStatistics(connectors), [connectors]);
10+
const ConnectorsStatistics: FC<ConnectorsStatisticsProps> = ({ isLoading }) => {
11+
const connectors = useFilteredConnectors();
12+
13+
const statistics = useMemo(() => {
14+
return computeStatistics(connectors);
15+
}, [connectors]);
1616

1717
return (
1818
<Statistics.Container role="group">

frontend/src/components/Connect/List/Statistics/__tests__/Statistics.spec.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import ConnectorsStatistics from 'components/Connect/List/Statistics/Statistics'
55
import { useConnectors } from 'lib/hooks/api/kafkaConnect';
66
import { connectors } from 'lib/fixtures/kafkaConnect';
77
import { FullConnectorInfo } from 'generated-sources';
8+
import { FilteredConnectorsProvider } from 'components/Connect/model/FilteredConnectorsProvider';
89

910
jest.mock('lib/hooks/api/kafkaConnect');
1011
jest.mock('lib/hooks/useAppParams', () => ({
@@ -25,7 +26,11 @@ describe('Kafka Connect Connectors Statistics', () => {
2526
function renderComponent({ data = [], isLoading }: RenderComponentProps) {
2627
// eslint-disable-next-line @typescript-eslint/no-explicit-any
2728
useConnectorsMock.mockReturnValue({ data, isLoading } as any);
28-
render(<ConnectorsStatistics connectors={data} isLoading={isLoading} />);
29+
render(
30+
<FilteredConnectorsProvider initialData={data}>
31+
<ConnectorsStatistics isLoading={isLoading} />
32+
</FilteredConnectorsProvider>
33+
);
2934
}
3035

3136
describe('when data loading', () => {

frontend/src/components/Connect/List/Statistics/models/computeStatistics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ConnectorState, FullConnectorInfo } from 'generated-sources';
22

3-
interface Statistic {
3+
export interface Statistic {
44
connectorsCount: number;
55
failedConnectorsCount: number;
66
tasksCount: number;

frontend/src/components/Connect/List/__tests__/List.spec.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
useUpdateConnectorState,
1717
} from 'lib/hooks/api/kafkaConnect';
1818
import { FullConnectorInfo } from 'generated-sources';
19+
import { FilteredConnectorsProvider } from 'components/Connect/model/FilteredConnectorsProvider';
1920

2021
const mockedUsedNavigate = jest.fn();
2122
const mockDelete = jest.fn();
@@ -42,7 +43,9 @@ const renderComponent = (
4243
render(
4344
<ClusterContext.Provider value={contextValue}>
4445
<WithRoute path={clusterConnectorsPath()}>
45-
<List connectors={data} />
46+
<FilteredConnectorsProvider>
47+
<List connectors={data} />
48+
</FilteredConnectorsProvider>
4649
</WithRoute>
4750
</ClusterContext.Provider>,
4851
{ initialEntries: [clusterConnectorsPath(clusterName)] }
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { FullConnectorInfo } from 'generated-sources';
2+
import React, {
3+
createContext,
4+
Dispatch,
5+
FC,
6+
PropsWithChildren,
7+
useContext,
8+
useReducer,
9+
} from 'react';
10+
11+
const initialConnectors: FullConnectorInfo[] = [];
12+
type Action = { type: 'updated'; connectors: FullConnectorInfo[] };
13+
14+
function reducer(connectors: FullConnectorInfo[], action: Action) {
15+
switch (action.type) {
16+
case 'updated': {
17+
return action.connectors;
18+
}
19+
default: {
20+
throw Error(`Unknown action: ${action.type}`);
21+
}
22+
}
23+
}
24+
25+
const ConnectorsContext = createContext<FullConnectorInfo[] | null>(null);
26+
const ConnectorsDispatchContext = createContext<Dispatch<Action> | null>(null);
27+
28+
export const FilteredConnectorsProvider: FC<
29+
PropsWithChildren<{ initialData?: FullConnectorInfo[] }>
30+
> = ({ children, initialData }) => {
31+
const [connectors, dispatch] = useReducer(
32+
reducer,
33+
initialData ?? initialConnectors
34+
);
35+
return (
36+
<ConnectorsContext.Provider value={connectors}>
37+
<ConnectorsDispatchContext.Provider value={dispatch}>
38+
{children}
39+
</ConnectorsDispatchContext.Provider>
40+
</ConnectorsContext.Provider>
41+
);
42+
};
43+
44+
export const useFilteredConnectors = () => {
45+
const context = useContext(ConnectorsContext);
46+
if (!context) {
47+
throw new Error('useCounter must be used within a CounterProvider');
48+
}
49+
return context;
50+
};
51+
52+
export const useFilteredConnectorsDispatch = () => {
53+
const context = useContext(ConnectorsDispatchContext);
54+
if (!context) {
55+
throw new Error('useCounter must be used within a CounterProvider');
56+
}
57+
return context;
58+
};

frontend/src/components/common/NewTable/Table.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { useEffect } from 'react';
22
import type {
33
ColumnDef,
44
ColumnFiltersState,
@@ -77,6 +77,8 @@ export interface TableProps<TData> {
7777
onMouseLeave?: () => void;
7878

7979
setRowId?: (originalRow: TData) => string;
80+
81+
onFilterRows?: (rows: TData[]) => void;
8082
}
8183

8284
type UpdaterFn<T> = (previousState: T) => T;
@@ -163,6 +165,7 @@ function Table<TData>({
163165
filterPersister,
164166
resetPaginationOnFilter = true,
165167
columnVisibility,
168+
onFilterRows,
166169
}: TableProps<TData>) {
167170
const [searchParams, setSearchParams] = useSearchParams();
168171
const location = useLocation();
@@ -273,6 +276,14 @@ function Table<TData>({
273276
return colSizes;
274277
}, [table.getState().columnSizingInfo, table.getState().columnSizing]);
275278

279+
useEffect(() => {
280+
if (onFilterRows) {
281+
const filteredRows = table.getFilteredRowModel().rows;
282+
const filteredData = filteredRows.map((row) => row.original);
283+
onFilterRows(filteredData);
284+
}
285+
}, [table.getState().columnFilters]);
286+
276287
const handleRowClick = (row: Row<TData>) => (e: React.MouseEvent) => {
277288
// If row selection is enabled do not handle row click.
278289
if (enableRowSelection) return undefined;

0 commit comments

Comments
 (0)