Issues/921 pagination consumer groups (#1802)
* Add Pagination to the ConsumerGroups Page * Consumer Groups test suites minor code modifications * Consumer Groups test Search results code * Consumer Groups test Search with Api request * Consumer Groups Search Logic
This commit is contained in:
parent
b4df8a73c8
commit
c79905ce32
7 changed files with 160 additions and 107 deletions
|
@ -1,57 +1,29 @@
|
|||
import React from 'react';
|
||||
import { ClusterName } from 'redux/interfaces';
|
||||
import { Switch, useParams } from 'react-router-dom';
|
||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||
import { Switch } from 'react-router-dom';
|
||||
import Details from 'components/ConsumerGroups/Details/Details';
|
||||
import ListContainer from 'components/ConsumerGroups/List/ListContainer';
|
||||
import ResetOffsets from 'components/ConsumerGroups/Details/ResetOffsets/ResetOffsets';
|
||||
import { useAppDispatch, useAppSelector } from 'lib/hooks/redux';
|
||||
import {
|
||||
fetchConsumerGroupsPaged,
|
||||
getAreConsumerGroupsPagedFulfilled,
|
||||
getConsumerGroupsOrderBy,
|
||||
getConsumerGroupsSortOrder,
|
||||
} from 'redux/reducers/consumerGroups/consumerGroupsSlice';
|
||||
import { BreadcrumbRoute } from 'components/common/Breadcrumb/Breadcrumb.route';
|
||||
|
||||
const ConsumerGroups: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { clusterName } = useParams<{ clusterName: ClusterName }>();
|
||||
const isFetched = useAppSelector(getAreConsumerGroupsPagedFulfilled);
|
||||
const orderBy = useAppSelector(getConsumerGroupsOrderBy);
|
||||
const sortOrder = useAppSelector(getConsumerGroupsSortOrder);
|
||||
React.useEffect(() => {
|
||||
dispatch(
|
||||
fetchConsumerGroupsPaged({
|
||||
clusterName,
|
||||
orderBy: orderBy || undefined,
|
||||
sortOrder,
|
||||
})
|
||||
);
|
||||
}, [clusterName, orderBy, sortOrder, dispatch]);
|
||||
|
||||
if (isFetched) {
|
||||
return (
|
||||
<Switch>
|
||||
<BreadcrumbRoute
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/consumer-groups"
|
||||
component={ListContainer}
|
||||
/>
|
||||
<BreadcrumbRoute
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/consumer-groups/:consumerGroupID"
|
||||
component={Details}
|
||||
/>
|
||||
<BreadcrumbRoute
|
||||
path="/ui/clusters/:clusterName/consumer-groups/:consumerGroupID/reset-offsets"
|
||||
component={ResetOffsets}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
|
||||
return <PageLoader />;
|
||||
return (
|
||||
<Switch>
|
||||
<BreadcrumbRoute
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/consumer-groups"
|
||||
component={ListContainer}
|
||||
/>
|
||||
<BreadcrumbRoute
|
||||
exact
|
||||
path="/ui/clusters/:clusterName/consumer-groups/:consumerGroupID"
|
||||
component={Details}
|
||||
/>
|
||||
<BreadcrumbRoute
|
||||
path="/ui/clusters/:clusterName/consumer-groups/:consumerGroupID/reset-offsets"
|
||||
component={ResetOffsets}
|
||||
/>
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConsumerGroups;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import PageHeading from 'components/common/PageHeading/PageHeading';
|
||||
import Search from 'components/common/Search/Search';
|
||||
import { ControlPanelWrapper } from 'components/common/ControlPanel/ControlPanel.styled';
|
||||
|
@ -14,12 +15,19 @@ import {
|
|||
GroupIDCell,
|
||||
StatusCell,
|
||||
} from 'components/ConsumerGroups/List/ConsumerGroupsTableCells';
|
||||
import usePagination from 'lib/hooks/usePagination';
|
||||
import useSearch from 'lib/hooks/useSearch';
|
||||
import { useAppDispatch } from 'lib/hooks/redux';
|
||||
import { ClusterName } from 'redux/interfaces';
|
||||
import { fetchConsumerGroupsPaged } from 'redux/reducers/consumerGroups/consumerGroupsSlice';
|
||||
import PageLoader from 'components/common/PageLoader/PageLoader';
|
||||
|
||||
export interface Props {
|
||||
consumerGroups: ConsumerGroupDetails[];
|
||||
orderBy: ConsumerGroupOrdering | null;
|
||||
sortOrder: SortOrder;
|
||||
totalPages: number;
|
||||
isFetched: boolean;
|
||||
setConsumerGroupsSortOrderBy(orderBy: ConsumerGroupOrdering | null): void;
|
||||
}
|
||||
|
||||
|
@ -28,23 +36,33 @@ const List: React.FC<Props> = ({
|
|||
sortOrder,
|
||||
orderBy,
|
||||
totalPages,
|
||||
isFetched,
|
||||
setConsumerGroupsSortOrderBy,
|
||||
}) => {
|
||||
const [searchText, setSearchText] = React.useState<string>('');
|
||||
const { page, perPage } = usePagination();
|
||||
const [searchText, handleSearchText] = useSearch();
|
||||
const dispatch = useAppDispatch();
|
||||
const { clusterName } = useParams<{ clusterName: ClusterName }>();
|
||||
|
||||
const tableData = useMemo(() => {
|
||||
return consumerGroups.filter(
|
||||
(consumerGroup) =>
|
||||
!searchText || consumerGroup?.groupId?.indexOf(searchText) >= 0
|
||||
React.useEffect(() => {
|
||||
dispatch(
|
||||
fetchConsumerGroupsPaged({
|
||||
clusterName,
|
||||
orderBy: orderBy || undefined,
|
||||
sortOrder,
|
||||
page,
|
||||
perPage,
|
||||
search: searchText,
|
||||
})
|
||||
);
|
||||
}, [searchText, consumerGroups]);
|
||||
}, [clusterName, orderBy, searchText, sortOrder, page, perPage, dispatch]);
|
||||
|
||||
const tableState = useTableState<
|
||||
ConsumerGroupDetails,
|
||||
string,
|
||||
ConsumerGroupOrdering
|
||||
>(
|
||||
tableData,
|
||||
consumerGroups,
|
||||
{
|
||||
totalPages,
|
||||
idSelector: (consumerGroup) => consumerGroup.groupId,
|
||||
|
@ -56,9 +74,9 @@ const List: React.FC<Props> = ({
|
|||
}
|
||||
);
|
||||
|
||||
const handleInputChange = (search: string) => {
|
||||
setSearchText(search);
|
||||
};
|
||||
if (!isFetched) {
|
||||
return <PageLoader />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -67,7 +85,7 @@ const List: React.FC<Props> = ({
|
|||
<Search
|
||||
placeholder="Search by Consumer Group ID"
|
||||
value={searchText}
|
||||
handleSearch={handleInputChange}
|
||||
handleSearch={handleSearchText}
|
||||
/>
|
||||
</ControlPanelWrapper>
|
||||
<SmartTable
|
||||
|
@ -75,6 +93,7 @@ const List: React.FC<Props> = ({
|
|||
isFullwidth
|
||||
placeholder="No active consumer groups"
|
||||
hoverable
|
||||
paginated
|
||||
>
|
||||
<TableColumn
|
||||
title="Consumer Group ID"
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
getConsumerGroupsTotalPages,
|
||||
sortBy,
|
||||
selectAll,
|
||||
getAreConsumerGroupsPagedFulfilled,
|
||||
} from 'redux/reducers/consumerGroups/consumerGroupsSlice';
|
||||
import List from 'components/ConsumerGroups/List/List';
|
||||
|
||||
|
@ -14,6 +15,7 @@ const mapStateToProps = (state: RootState) => ({
|
|||
orderBy: getConsumerGroupsOrderBy(state),
|
||||
sortOrder: getConsumerGroupsSortOrder(state),
|
||||
totalPages: getConsumerGroupsTotalPages(state),
|
||||
isFetched: getAreConsumerGroupsPagedFulfilled(state),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import List, { Props } from 'components/ConsumerGroups/List/List';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { render } from 'lib/testHelpers';
|
||||
import { consumerGroups as consumerGroupMock } from 'redux/reducers/consumerGroups/__test__/fixtures';
|
||||
|
@ -23,6 +23,7 @@ describe('List', () => {
|
|||
sortOrder={sortOrder || SortOrder.ASC}
|
||||
setConsumerGroupsSortOrderBy={setConsumerGroupsSortOrderBy || jest.fn()}
|
||||
totalPages={totalPages || 1}
|
||||
isFetched={'isFetched' in props ? !!props.isFetched : true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -41,38 +42,6 @@ describe('List', () => {
|
|||
expect(screen.getByText('groupId2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('when searched', () => {
|
||||
it('renders only searched consumers', async () => {
|
||||
await waitFor(() => {
|
||||
userEvent.type(
|
||||
screen.getByPlaceholderText('Search by Consumer Group ID'),
|
||||
consumerGroupMock[0].groupId
|
||||
);
|
||||
});
|
||||
|
||||
expect(
|
||||
screen.getByText(consumerGroupMock[0].groupId)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(consumerGroupMock[1].groupId)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders will not render a list since not found in the list', async () => {
|
||||
await waitFor(() => {
|
||||
userEvent.type(
|
||||
screen.getByPlaceholderText('Search by Consumer Group ID'),
|
||||
'NotFoundedText'
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/No active consumer groups/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Testing the Ordering', () => {
|
||||
it('should test the sort order functionality', async () => {
|
||||
const thElement = screen.getByText(/consumer group id/i);
|
||||
|
|
|
@ -6,38 +6,69 @@ import {
|
|||
waitForElementToBeRemoved,
|
||||
} from '@testing-library/react';
|
||||
import ConsumerGroups from 'components/ConsumerGroups/ConsumerGroups';
|
||||
import { consumerGroups } from 'redux/reducers/consumerGroups/__test__/fixtures';
|
||||
import {
|
||||
consumerGroups,
|
||||
noConsumerGroupsResponse,
|
||||
} from 'redux/reducers/consumerGroups/__test__/fixtures';
|
||||
import { render } from 'lib/testHelpers';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import { Route } from 'react-router';
|
||||
import { Route, Router } from 'react-router';
|
||||
import { ConsumerGroupOrdering, SortOrder } from 'generated-sources';
|
||||
import { createMemoryHistory } from 'history';
|
||||
|
||||
const clusterName = 'cluster1';
|
||||
|
||||
const renderComponent = () =>
|
||||
const historyMock = createMemoryHistory({
|
||||
initialEntries: [clusterConsumerGroupsPath(clusterName)],
|
||||
});
|
||||
|
||||
const renderComponent = (history = historyMock) =>
|
||||
render(
|
||||
<Route path={clusterConsumerGroupsPath(':clusterName')}>
|
||||
<ConsumerGroups />
|
||||
</Route>,
|
||||
<Router history={history}>
|
||||
<Route path={clusterConsumerGroupsPath(':clusterName')}>
|
||||
<ConsumerGroups />
|
||||
</Route>
|
||||
</Router>,
|
||||
{
|
||||
pathname: clusterConsumerGroupsPath(clusterName),
|
||||
}
|
||||
);
|
||||
|
||||
describe('ConsumerGroup', () => {
|
||||
describe('ConsumerGroups', () => {
|
||||
it('renders with initial state', async () => {
|
||||
renderComponent();
|
||||
|
||||
expect(screen.getByRole('progressbar')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
describe('Fetching Mock', () => {
|
||||
const url = `/api/clusters/${clusterName}/consumer-groups/paged?orderBy=${ConsumerGroupOrdering.NAME}&sortOrder=${SortOrder.ASC}`;
|
||||
describe('Default Route and Fetching Consumer Groups', () => {
|
||||
const url = `/api/clusters/${clusterName}/consumer-groups/paged`;
|
||||
afterEach(() => {
|
||||
fetchMock.reset();
|
||||
});
|
||||
|
||||
it('renders empty table on no consumer group response', async () => {
|
||||
fetchMock.getOnce(url, noConsumerGroupsResponse, {
|
||||
query: {
|
||||
orderBy: ConsumerGroupOrdering.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent();
|
||||
await waitFor(() => expect(fetchMock.calls().length).toBe(1));
|
||||
|
||||
expect(screen.getByRole('table')).toBeInTheDocument();
|
||||
expect(screen.getByText('No active consumer groups')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with 404 from consumer groups', async () => {
|
||||
const consumerGroupsMock = fetchMock.getOnce(url, 404);
|
||||
const consumerGroupsMock = fetchMock.getOnce(url, 404, {
|
||||
query: {
|
||||
orderBy: ConsumerGroupOrdering.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent();
|
||||
|
||||
|
@ -48,10 +79,19 @@ describe('ConsumerGroup', () => {
|
|||
});
|
||||
|
||||
it('renders with 200 from consumer groups', async () => {
|
||||
const consumerGroupsMock = fetchMock.getOnce(url, {
|
||||
pagedCount: 1,
|
||||
consumerGroups,
|
||||
});
|
||||
const consumerGroupsMock = fetchMock.getOnce(
|
||||
url,
|
||||
{
|
||||
pagedCount: 1,
|
||||
consumerGroups,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
orderBy: ConsumerGroupOrdering.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
renderComponent();
|
||||
|
||||
|
@ -60,6 +100,43 @@ describe('ConsumerGroup', () => {
|
|||
|
||||
expect(screen.getByText('Consumers')).toBeInTheDocument();
|
||||
expect(screen.getByRole('table')).toBeInTheDocument();
|
||||
expect(screen.getByText(consumerGroups[0].groupId)).toBeInTheDocument();
|
||||
expect(screen.getByText(consumerGroups[1].groupId)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with 200 from consumer groups with Searched Query ', async () => {
|
||||
const searchResult = consumerGroups[0];
|
||||
const searchText = searchResult.groupId;
|
||||
|
||||
const consumerGroupsMock = fetchMock.getOnce(
|
||||
url,
|
||||
{
|
||||
pagedCount: 1,
|
||||
consumerGroups: [searchResult],
|
||||
},
|
||||
{
|
||||
query: {
|
||||
orderBy: ConsumerGroupOrdering.NAME,
|
||||
sortOrder: SortOrder.ASC,
|
||||
search: searchText,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
const mockedHistory = createMemoryHistory({
|
||||
initialEntries: [
|
||||
`${clusterConsumerGroupsPath(clusterName)}?q=${searchText}`,
|
||||
],
|
||||
});
|
||||
renderComponent(mockedHistory);
|
||||
|
||||
await waitForElementToBeRemoved(() => screen.getByRole('progressbar'));
|
||||
await waitFor(() => expect(consumerGroupsMock.called()).toBeTruthy());
|
||||
|
||||
expect(screen.getByText(searchText)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(consumerGroups[1].groupId)
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,6 +25,11 @@ export const consumerGroups = [
|
|||
},
|
||||
];
|
||||
|
||||
export const noConsumerGroupsResponse = {
|
||||
pageCount: 1,
|
||||
consumerGroups: [],
|
||||
};
|
||||
|
||||
export const consumerGroupsPage = {
|
||||
totalPages: 1,
|
||||
consumerGroups,
|
||||
|
|
|
@ -33,15 +33,24 @@ export const fetchConsumerGroupsPaged = createAsyncThunk<
|
|||
clusterName: ClusterName;
|
||||
orderBy?: ConsumerGroupOrdering;
|
||||
sortOrder?: SortOrder;
|
||||
page?: number;
|
||||
perPage?: number;
|
||||
search: string;
|
||||
}
|
||||
>(
|
||||
'consumerGroups/fetchConsumerGroupsPaged',
|
||||
async ({ clusterName, orderBy, sortOrder }, { rejectWithValue }) => {
|
||||
async (
|
||||
{ clusterName, orderBy, sortOrder, page, perPage, search },
|
||||
{ rejectWithValue }
|
||||
) => {
|
||||
try {
|
||||
const response = await api.getConsumerGroupsPageRaw({
|
||||
clusterName,
|
||||
orderBy,
|
||||
sortOrder,
|
||||
page,
|
||||
perPage,
|
||||
search,
|
||||
});
|
||||
return await response.value();
|
||||
} catch (error) {
|
||||
|
|
Loading…
Add table
Reference in a new issue