Skip to content

Commit

Permalink
feat(CE): added new pagination format
Browse files Browse the repository at this point in the history
  • Loading branch information
github-actions[bot] authored Oct 22, 2024
1 parent c54babe commit 7084f65
Show file tree
Hide file tree
Showing 8 changed files with 303 additions and 26 deletions.
56 changes: 56 additions & 0 deletions ui/src/components/EnhancedPagination/PageChangeButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Button, Icon } from '@chakra-ui/react';
import { FiChevronLeft, FiChevronRight, FiChevronsLeft, FiChevronsRight } from 'react-icons/fi';

export enum PAGE_CHANGE_BUTTON_TYPE {
PREVIOUS = 'previous',
NEXT = 'next',
FIRST = 'first',
LAST = 'last',
}

type PageButtonProps = {
type: PAGE_CHANGE_BUTTON_TYPE;
onClick: () => void;
isEnabled?: boolean;
};

const PageChangeButton = ({ type, onClick, isEnabled = false }: PageButtonProps) => {
const iconMap = {
[PAGE_CHANGE_BUTTON_TYPE.PREVIOUS]: { icon: FiChevronLeft, value: 'previous' },
[PAGE_CHANGE_BUTTON_TYPE.FIRST]: { icon: FiChevronsLeft, value: 'first' },
[PAGE_CHANGE_BUTTON_TYPE.LAST]: { icon: FiChevronsRight, value: 'last' },
[PAGE_CHANGE_BUTTON_TYPE.NEXT]: { icon: FiChevronRight, value: 'next' },
};

const icon = iconMap[type] || iconMap[PAGE_CHANGE_BUTTON_TYPE.NEXT];

return (
<Button
height='32px'
width='32px'
borderRadius='6px'
borderStyle='solid'
borderWidth='1px'
borderColor='gray.500'
display='flex'
justifyContent='center'
alignItems='center'
color='black.200'
backgroundColor='gray.300'
minWidth='0'
padding={0}
onClick={onClick}
_hover={{ backgroundColor: 'gray.400' }}
_disabled={{
_hover: { cursor: 'not-allowed' },
backgroundColor: 'gray.400',
}}
isDisabled={!isEnabled}
data-testid={`page-change-${icon.value}`}
>
<Icon as={icon.icon} h='14px' w='14px' color='black.500' />
</Button>
);
};

export default PageChangeButton;
42 changes: 42 additions & 0 deletions ui/src/components/EnhancedPagination/PageNumberItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Box, Text } from '@chakra-ui/react';

const PageNumberItem = ({
isActive = false,
onClick,
value,
isEllipsis = false,
}: {
isActive?: boolean;
onClick?: () => void;
value?: number;
isEllipsis?: boolean;
}) => (
<Box
height='32px'
width='32px'
borderRadius='6px'
borderStyle='solid'
borderWidth={isActive ? '1px' : '0px'}
borderColor={isActive ? 'gray.500' : 'gray.400'}
display='flex'
justifyContent='center'
alignItems='center'
color='black.200'
backgroundColor={isActive ? 'gray.100' : 'gray.300'}
minWidth='0'
padding={0}
onClick={isEllipsis ? () => {} : onClick}
_hover={{ backgroundColor: 'gray.400', cursor: 'pointer' }}
_disabled={{
_hover: { cursor: 'not-allowed' },
backgroundColor: 'gray.400',
}}
data-testid={isEllipsis ? 'ellipsis' : `page-number-${value}`}
>
<Text size='xs' fontWeight='semibold'>
{isEllipsis ? '...' : value}
</Text>
</Box>
);

export default PageNumberItem;
101 changes: 101 additions & 0 deletions ui/src/components/EnhancedPagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { LinksType } from '@/services/common';
import { Stack, Box } from '@chakra-ui/react';
import PageNumberItem from './PageNumberItem';
import PageChangeButton, { PAGE_CHANGE_BUTTON_TYPE } from './PageChangeButton';

const getPage = (url: string): number => {
const parser = new URLSearchParams(new URL(url).search);
return parseInt(parser.get('page') || '0');
};

export type PaginationProps = {
links: LinksType;
currentPage: number;
handlePageChange: (pageNumber: number) => void;
};

const Pagination = ({ links, currentPage, handlePageChange }: PaginationProps) => {
const firstPage = getPage(links.first);
const lastPage = getPage(links.last);

const renderPageNumbers = () => {
if (currentPage === firstPage) {
return (
<>
<PageNumberItem
isActive
value={currentPage}
onClick={() => handlePageChange(currentPage)}
/>
{lastPage > firstPage + 1 && <PageNumberItem isEllipsis />}
{lastPage !== firstPage && (
<PageNumberItem value={lastPage} onClick={() => handlePageChange(lastPage)} />
)}
</>
);
} else if (currentPage === lastPage) {
return (
<>
<PageNumberItem value={firstPage} onClick={() => handlePageChange(firstPage)} />
{lastPage > firstPage + 1 && <PageNumberItem isEllipsis />}
<PageNumberItem
isActive
value={currentPage}
onClick={() => handlePageChange(currentPage)}
/>
</>
);
} else {
return (
<>
<PageNumberItem value={firstPage} onClick={() => handlePageChange(firstPage)} />
{currentPage > firstPage + 1 && <PageNumberItem isEllipsis />}
<PageNumberItem
isActive
value={currentPage}
onClick={() => handlePageChange(currentPage)}
/>
{currentPage < lastPage - 1 && <PageNumberItem isEllipsis />}
<PageNumberItem value={lastPage} onClick={() => handlePageChange(lastPage)} />
</>
);
}
};

return (
<Box
width='fit-content'
backgroundColor='gray.300'
borderColor='gray.400'
borderRadius='8px'
borderWidth='1px'
padding='4px'
>
<Stack direction='row' justify='end' spacing={4} alignItems='center' gap='6px'>
<PageChangeButton
isEnabled={currentPage !== firstPage}
type={PAGE_CHANGE_BUTTON_TYPE.FIRST}
onClick={() => handlePageChange(firstPage)}
/>
<PageChangeButton
isEnabled={currentPage !== firstPage}
type={PAGE_CHANGE_BUTTON_TYPE.PREVIOUS}
onClick={() => handlePageChange(currentPage - 1)}
/>
{renderPageNumbers()}
<PageChangeButton
isEnabled={currentPage !== lastPage}
type={PAGE_CHANGE_BUTTON_TYPE.NEXT}
onClick={() => handlePageChange(currentPage + 1)}
/>
<PageChangeButton
isEnabled={currentPage !== lastPage}
type={PAGE_CHANGE_BUTTON_TYPE.LAST}
onClick={() => handlePageChange(lastPage)}
/>
</Stack>
</Box>
);
};

export default Pagination;
86 changes: 86 additions & 0 deletions ui/src/components/EnhancedPagination/__tests__/Pagination.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { fireEvent, render, screen } from '@testing-library/react';
import { expect } from '@jest/globals';
import '@testing-library/jest-dom';

import Pagination, { PaginationProps } from '../Pagination';

describe('Pagination', () => {
const mockHandlePageChange = jest.fn();
const defaultProps: PaginationProps = {
links: {
self: 'http://localhost:3000/api/v1/connectors?',
first: 'http://localhost:3000/api/v1/connectors?page=1&per_page=10',
prev: null,
next: 'http://localhost:3000/api/v1/connectors?page=2&per_page=10',
last: 'http://localhost:3000/api/v1/connectors?page=10&per_page=10',
},
currentPage: 5,
handlePageChange: mockHandlePageChange,
};

beforeEach(() => {
mockHandlePageChange.mockClear();
});

it('should render correct page numbers for middle page', () => {
render(<Pagination {...defaultProps} />);
expect(screen.getByTestId('page-number-1')).toBeTruthy();
expect(screen.getByTestId('page-number-5')).toBeTruthy();
expect(screen.getByTestId('page-number-10')).toBeTruthy();
});

it('should render correct page numbers for first page', () => {
render(<Pagination {...defaultProps} currentPage={1} />);
expect(screen.getByTestId('page-number-1')).toBeTruthy();
expect(screen.getByTestId('page-number-10')).toBeTruthy();
expect(screen.queryByTestId('page-number-5')).not.toBeTruthy();
});

it('should render correct page numbers for last page', () => {
render(<Pagination {...defaultProps} currentPage={10} />);
expect(screen.getByTestId('page-number-1')).toBeTruthy();
expect(screen.getByTestId('page-number-10')).toBeTruthy();
});

it('should call handlePageChange with correct page number when a page number is clicked', () => {
render(<Pagination {...defaultProps} />);
const pageButton = screen.getByTestId('page-number-1');
fireEvent.click(pageButton);
expect(mockHandlePageChange).toHaveBeenCalledWith(1);
});

it('should disable first and previous buttons on first page', () => {
render(<Pagination {...defaultProps} currentPage={1} />);

expect(screen.getByTestId('page-change-first')).toHaveProperty('disabled', true);
expect(screen.getByTestId('page-change-previous')).toHaveProperty('disabled', true);
});

it('should disable next and last buttons on last page', () => {
render(<Pagination {...defaultProps} currentPage={10} />);

expect(screen.getByTestId('page-change-next')).toHaveProperty('disabled', true);
expect(screen.getByTestId('page-change-last')).toHaveProperty('disabled', true);
});

it('should call handlePageChange with correct page number when navigation buttons are clicked', () => {
render(<Pagination {...defaultProps} />);
fireEvent.click(screen.getByTestId('page-change-first'));
expect(mockHandlePageChange).toHaveBeenCalledWith(1);

fireEvent.click(screen.getByTestId('page-change-previous'));
expect(mockHandlePageChange).toHaveBeenCalledWith(4);

fireEvent.click(screen.getByTestId('page-change-next'));
expect(mockHandlePageChange).toHaveBeenCalledWith(6);

fireEvent.click(screen.getByTestId('page-change-last'));
expect(mockHandlePageChange).toHaveBeenCalledWith(10);
});

it('should render ellipsis correctly', () => {
render(<Pagination {...defaultProps} />);
const ellipses = screen.getAllByText('...');
expect(ellipses).toHaveLength(2);
});
});
1 change: 1 addition & 0 deletions ui/src/components/EnhancedPagination/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Pagination';
5 changes: 3 additions & 2 deletions ui/src/components/ModelTable/ModelTable.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import GenerateTable from '@/components/Table/Table';
import { getAllModels, APIData } from '@/services/models';
import { getAllModels, GetAllModelsResponse } from '@/services/models';
import { addIconDataToArray, ConvertToTableData } from '@/utils';
import NoModels from '@/views/Models/NoModels';
import Loader from '@/components/Loader';
import useQueryWrapper from '@/hooks/useQueryWrapper';
import { useStore } from '@/stores';
import { ApiResponse } from '@/services/common';

type ModelTableProps = {
handleOnRowClick: (args: unknown) => void;
Expand All @@ -13,7 +14,7 @@ type ModelTableProps = {
const ModelTable = ({ handleOnRowClick }: ModelTableProps): JSX.Element => {
const activeWorkspaceId = useStore((state) => state.workspaceId);

const { data } = useQueryWrapper<APIData, Error>(
const { data } = useQueryWrapper<ApiResponse<GetAllModelsResponse[]>, Error>(
['models', activeWorkspaceId],
() => getAllModels({ type: 'data' }),
{
Expand Down
6 changes: 3 additions & 3 deletions ui/src/services/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ export * from '@/services/axios';

export type APIRequestMethod = 'get' | 'post' | 'put' | 'delete';

type LinksType = {
export type LinksType = {
first: string;
last: string;
next: string;
prev: string;
next: string | null;
prev: string | null;
self: string;
};

Expand Down
32 changes: 11 additions & 21 deletions ui/src/views/Activate/Syncs/SyncRecords/SyncRecords.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { getSyncRecords } from '@/services/syncs';
import Loader from '@/components/Loader';
import ContentContainer from '@/components/ContentContainer';

import Pagination from '@/components/Pagination';
import SyncRunEmptyImage from '@/assets/images/empty-state-illustration.svg';

import { SyncRecordsTopBar } from './SyncRecordsTopBar';
Expand All @@ -21,6 +20,7 @@ import { SyncRecordResponse } from '@/views/Activate/Syncs/types';

import DataTable from '@/components/DataTable';
import { SyncRecordsColumns, useDynamicSyncColumns } from './SyncRecordsColumns';
import Pagination from '@/components/EnhancedPagination';

const SyncRecords = (): JSX.Element => {
const [searchParams, setSearchParams] = useSearchParams();
Expand Down Expand Up @@ -81,18 +81,6 @@ const SyncRecords = (): JSX.Element => {
}
}, [isFilteredSyncRecordsError, toast]);

const handleNextPage = () => {
if (filteredSyncRunRecords?.links?.next) {
setCurrentPage((prevPage) => prevPage + 1);
}
};

const handlePrevPage = () => {
if (filteredSyncRunRecords?.links?.prev) {
setCurrentPage((prevPage) => Math.max(prevPage - 1, 1));
}
};

const handleStatusTabChange = (status: SyncRecordStatus) => {
setCurrentPage(1);
setCurrentStatusTab(status);
Expand Down Expand Up @@ -143,14 +131,16 @@ const SyncRecords = (): JSX.Element => {
<Box border='1px' borderColor='gray.400' borderRadius='lg' overflowX='scroll'>
<DataTable data={data} columns={allColumns} />
</Box>
<Box display='flex' flexDirection='row-reverse' pt='10px'>
<Pagination
currentPage={currentPage}
isPrevPageEnabled={filteredSyncRunRecords?.links?.prev != null}
isNextPageEnabled={filteredSyncRunRecords?.links?.next != null}
handleNextPage={handleNextPage}
handlePrevPage={handlePrevPage}
/>
<Box display='flex' justifyContent='center' pt='20px'>
{filteredSyncRunRecords.links ? (
<Pagination
links={filteredSyncRunRecords?.links}
currentPage={currentPage}
handlePageChange={setCurrentPage}
/>
) : (
<>Pagination unavailable.</>
)}
</Box>
</Box>
)}
Expand Down

0 comments on commit 7084f65

Please sign in to comment.