Fix and improvement on the pagination for tracked table in the new UI

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9890
GitOrigin-RevId: ff1bb64321721718378de6447be9c9cc98e34c32
This commit is contained in:
Nicolas Inchauspe 2023-07-25 12:39:48 +02:00 committed by hasura-bot
parent 78323ed1a1
commit 82011af6ed
3 changed files with 185 additions and 6 deletions

View File

@ -1,7 +1,12 @@
import React from 'react';
import { Button } from '../../../../new-components/Button';
import { DEFAULT_PAGE_SIZES } from '../constants';
import { FaAngleLeft, FaAngleRight } from 'react-icons/fa';
import {
FaAngleDoubleLeft,
FaAngleDoubleRight,
FaAngleLeft,
FaAngleRight,
} from 'react-icons/fa';
import { PaginatedSearchableListProps } from '../hooks/usePaginatedSearchableList';
export const PageSizeDropdown = ({
@ -9,6 +14,8 @@ export const PageSizeDropdown = ({
pageSize,
decrementPage,
incrementPage,
goToFirstPage,
goToLastPage,
setPageSize,
dataSize,
totalPages,
@ -17,6 +24,11 @@ export const PageSizeDropdown = ({
<span className="whitespace-nowrap mr-2">
Page {pageNumber} of {totalPages}
</span>
<Button
icon={<FaAngleDoubleLeft />}
onClick={goToFirstPage}
disabled={pageNumber === 1}
/>
<Button
icon={<FaAngleLeft />}
onClick={decrementPage}
@ -40,5 +52,10 @@ export const PageSizeDropdown = ({
onClick={incrementPage}
disabled={pageNumber >= dataSize / pageSize}
/>
<Button
icon={<FaAngleDoubleRight />}
onClick={goToLastPage}
disabled={pageNumber >= dataSize / pageSize}
/>
</div>
);

View File

@ -0,0 +1,152 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { usePaginatedSearchableList } from './usePaginatedSearchableList';
const mockData = [
{ id: '1', name: 'John' },
{ id: '2', name: 'Jane' },
{ id: '3', name: 'Bob' },
{ id: '4', name: 'Alice' },
{ id: '5', name: 'Eve' },
{ id: '6', name: 'Carol' },
{ id: '7', name: 'Dave' },
{ id: '8', name: 'Frank' },
{ id: '9', name: 'Grace' },
{ id: '10', name: 'Heidi' },
{ id: '11', name: 'Ivan' },
{ id: '12', name: 'Mallory' },
{ id: '13', name: 'Oscar' },
{ id: '14', name: 'Peggy' },
{ id: '15', name: 'Trent' },
];
describe('usePaginatedSearchableList', () => {
it('should return the correct initial state', () => {
const { result } = renderHook(() =>
usePaginatedSearchableList({
data: mockData,
filterFn: (searchText, item) => item.name.includes(searchText),
})
);
expect(result.current.pageNumber).toBe(1);
expect(result.current.pageSize).toBe(10);
expect(result.current.searchIsActive).toBe(false);
expect(result.current.filteredData).toEqual(mockData);
expect(result.current.paginatedData).toEqual(mockData.slice(0, 10));
expect(result.current.totalPages).toBe(2);
expect(result.current.checkData.checkedIds).toEqual([]);
expect(result.current.getCheckedItems()).toEqual([]);
expect(result.current.dataSize).toBe(mockData.length);
});
it('should go to page 2 and display page 2 data when navigating page', () => {
const { result } = renderHook(() =>
usePaginatedSearchableList({
data: mockData,
filterFn: (searchText, item) => item.name.includes(searchText),
})
);
act(() => {
result.current.incrementPage();
});
expect(result.current.pageNumber).toBe(2);
expect(result.current.filteredData).toEqual(mockData);
expect(result.current.paginatedData).toEqual(mockData.slice(10, 15));
expect(result.current.totalPages).toBe(2);
expect(result.current.dataSize).toBe(mockData.length);
});
it('should update the filtered data when search text changes', () => {
const { result } = renderHook(() =>
usePaginatedSearchableList({
data: mockData,
filterFn: (searchText, item) => item.name.includes(searchText),
})
);
act(() => {
result.current.incrementPage();
result.current.handleSearch('o');
});
expect(result.current.searchIsActive).toBe(true);
expect(result.current.pageNumber).toBe(1);
expect(result.current.totalPages).toBe(1);
expect(result.current.dataSize).toBe(4);
});
it('should go to page 1 when data are filtered on page 2 and filtered data length < page size', () => {
const { result } = renderHook(() =>
usePaginatedSearchableList({
data: mockData,
filterFn: (searchText, item) => item.name.includes(searchText),
})
);
act(() => {
result.current.handleSearch('o');
});
expect(result.current.searchIsActive).toBe(true);
expect(result.current.filteredData).toEqual([
{ id: '1', name: 'John' },
{ id: '3', name: 'Bob' },
{ id: '6', name: 'Carol' },
{ id: '12', name: 'Mallory' },
]);
expect(result.current.paginatedData).toEqual([
{ id: '1', name: 'John' },
{ id: '3', name: 'Bob' },
{ id: '6', name: 'Carol' },
{ id: '12', name: 'Mallory' },
]);
expect(result.current.pageNumber).toBe(1);
expect(result.current.totalPages).toBe(1);
expect(result.current.dataSize).toBe(4);
});
it('should update the paginated data when page number changes', () => {
const { result } = renderHook(() =>
usePaginatedSearchableList({
data: mockData,
filterFn: (searchText, item) => item.name.includes(searchText),
})
);
act(() => {
result.current.setPageNumber(2);
});
expect(result.current.pageNumber).toBe(2);
expect(result.current.paginatedData).toEqual([
{ id: '11', name: 'Ivan' },
{ id: '12', name: 'Mallory' },
{ id: '13', name: 'Oscar' },
{ id: '14', name: 'Peggy' },
{ id: '15', name: 'Trent' },
]);
expect(result.current.totalPages).toBe(2);
expect(result.current.dataSize).toBe(mockData.length);
});
it('should update the checked items when rows are checked', () => {
const { result } = renderHook(() =>
usePaginatedSearchableList({
data: mockData,
filterFn: (searchText, item) => item.name.includes(searchText),
})
);
act(() => {
result.current.checkData.onCheck('1');
});
expect(result.current.checkData.checkedIds).toEqual(['1']);
expect(result.current.getCheckedItems()).toEqual([
{ id: '1', name: 'John' },
]);
expect(result.current.dataSize).toBe(mockData.length);
});
});

View File

@ -37,10 +37,10 @@ export function usePaginatedSearchableList<TData extends { id: string }>({
[checkData.checkedIds, data]
);
const handleSearch = React.useCallback(
(searchQuery: string) => setSearchText(searchQuery),
[]
);
const handleSearch = React.useCallback((searchQuery: string) => {
setPageNumber(DEFAULT_PAGE_NUMBER);
setSearchText(searchQuery);
}, []);
const incrementPage = React.useCallback(() => {
setPageNumber(currentPage =>
@ -54,6 +54,14 @@ export function usePaginatedSearchableList<TData extends { id: string }>({
);
}, []);
const goToFirstPage = React.useCallback(() => {
setPageNumber(() => 1);
}, []);
const goToLastPage = React.useCallback(() => {
setPageNumber(() => totalPages);
}, [totalPages]);
return {
pageNumber,
setPageNumber,
@ -61,6 +69,8 @@ export function usePaginatedSearchableList<TData extends { id: string }>({
setPageSize,
incrementPage,
decrementPage,
goToFirstPage,
goToLastPage,
totalPages,
searchIsActive,
handleSearch,
@ -68,7 +78,7 @@ export function usePaginatedSearchableList<TData extends { id: string }>({
filteredData,
paginatedData,
getCheckedItems,
dataSize: data.length,
dataSize: filteredData.length,
};
}