generated from CDCgov/template
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* fix(RV-394): Add pagination * Update TemplatesIndex.tsx
- Loading branch information
1 parent
bb3edb8
commit a932583
Showing
4 changed files
with
250 additions
and
2 deletions.
There are no files selected for viewing
16 changes: 16 additions & 0 deletions
16
frontend/src/components/TemplatesIndex/TemplatesIndex.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
.pagination-text { | ||
color: #71767A; | ||
font-size: 14px; | ||
font-style: normal; | ||
font-weight: 400; | ||
line-height: normal; | ||
margin-left: 40px; | ||
} | ||
|
||
.pagination-container { | ||
justify-content: space-between; | ||
} | ||
|
||
.pagination-button-group { | ||
margin-right: 40px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { useState, useMemo } from "react"; | ||
|
||
|
||
const usePagination = <T>(items: T[] = [], itemsPerPage = 10, initialPage = 1) => { | ||
const [currentPage, setCurrentPage] = useState(initialPage); | ||
|
||
// Calculate total number of pages | ||
const totalPages = useMemo(() => | ||
Math.ceil(items.length / itemsPerPage), | ||
[items.length, itemsPerPage] | ||
); | ||
|
||
// Ensure current page stays within bounds | ||
useMemo(() => { | ||
if (currentPage > totalPages) { | ||
setCurrentPage(totalPages || 1); | ||
} | ||
}, [currentPage, totalPages]); | ||
|
||
// Get current page items | ||
const currentItems = useMemo(() => { | ||
const startIndex = (currentPage - 1) * itemsPerPage; | ||
const endIndex = startIndex + itemsPerPage; | ||
return items.slice(startIndex, endIndex); | ||
}, [items, currentPage, itemsPerPage]); | ||
|
||
// Navigation functions | ||
const goToPage = (pageNumber: number) => { | ||
const page = Math.max(1, Math.min(pageNumber, totalPages)); | ||
setCurrentPage(page); | ||
}; | ||
|
||
const nextPage = () => { | ||
if (currentPage < totalPages) { | ||
setCurrentPage(prev => prev + 1); | ||
} | ||
}; | ||
|
||
const previousPage = () => { | ||
if (currentPage > 1) { | ||
setCurrentPage(prev => prev - 1); | ||
} | ||
}; | ||
|
||
const firstPage = () => { | ||
setCurrentPage(1); | ||
}; | ||
|
||
const lastPage = () => { | ||
setCurrentPage(totalPages); | ||
}; | ||
|
||
// Generate page numbers for pagination display | ||
const getPageNumbers = (maxVisible = 5) => { | ||
const pages = []; | ||
let startPage = Math.max(1, currentPage - Math.floor(maxVisible / 2)); | ||
const endPage = Math.min(totalPages, startPage + maxVisible - 1); | ||
|
||
// Adjust start page if end page is maxed out | ||
if (endPage - startPage + 1 < maxVisible) { | ||
startPage = Math.max(1, endPage - maxVisible + 1); | ||
} | ||
|
||
for (let i = startPage; i <= endPage; i++) { | ||
pages.push(i); | ||
} | ||
|
||
return pages; | ||
}; | ||
|
||
return { | ||
currentPage, | ||
currentItems, | ||
totalPages, | ||
itemsPerPage, | ||
goToPage, | ||
nextPage, | ||
previousPage, | ||
firstPage, | ||
lastPage, | ||
getPageNumbers, | ||
hasNextPage: currentPage < totalPages, | ||
hasPreviousPage: currentPage > 1 | ||
}; | ||
}; | ||
|
||
export default usePagination; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { renderHook, act } from '@testing-library/react'; | ||
import { describe, it, expect } from 'vitest'; | ||
import usePagination from './index'; | ||
|
||
describe('usePagination hook', () => { | ||
const items: number[] = Array.from({ length: 50 }, (_, i) => i + 1); // Sample items [1, 2, ..., 50] | ||
|
||
it('should initialize with the correct state', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 1)); | ||
|
||
expect(result.current.currentPage).toBe(1); | ||
expect(result.current.totalPages).toBe(5); | ||
expect(result.current.currentItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | ||
}); | ||
|
||
it('should navigate to the next page', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 1)); | ||
|
||
act(() => { | ||
result.current.nextPage(); | ||
}); | ||
|
||
expect(result.current.currentPage).toBe(2); | ||
expect(result.current.currentItems).toEqual([11, 12, 13, 14, 15, 16, 17, 18, 19, 20]); | ||
}); | ||
|
||
it('should navigate to the previous page', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 2)); | ||
|
||
act(() => { | ||
result.current.previousPage(); | ||
}); | ||
|
||
expect(result.current.currentPage).toBe(1); | ||
expect(result.current.currentItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | ||
}); | ||
|
||
it('should navigate to the first page', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 3)); | ||
|
||
act(() => { | ||
result.current.firstPage(); | ||
}); | ||
|
||
expect(result.current.currentPage).toBe(1); | ||
expect(result.current.currentItems).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); | ||
}); | ||
|
||
it('should navigate to the last page', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 1)); | ||
|
||
act(() => { | ||
result.current.lastPage(); | ||
}); | ||
|
||
expect(result.current.currentPage).toBe(5); | ||
expect(result.current.currentItems).toEqual([41, 42, 43, 44, 45, 46, 47, 48, 49, 50]); | ||
}); | ||
|
||
it('should navigate to a specific page', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 1)); | ||
|
||
act(() => { | ||
result.current.goToPage(3); | ||
}); | ||
|
||
expect(result.current.currentPage).toBe(3); | ||
expect(result.current.currentItems).toEqual([21, 22, 23, 24, 25, 26, 27, 28, 29, 30]); | ||
}); | ||
|
||
it('should generate correct page numbers', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 3)); | ||
|
||
const pageNumbers = result.current.getPageNumbers(5); | ||
expect(pageNumbers).toEqual([1, 2, 3, 4, 5]); | ||
}); | ||
|
||
it('should handle edge cases for page numbers', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 5)); | ||
|
||
const pageNumbers = result.current.getPageNumbers(5); | ||
expect(pageNumbers).toEqual([1, 2, 3, 4, 5]); | ||
}); | ||
|
||
it('should handle hasNextPage and hasPreviousPage correctly', () => { | ||
const { result } = renderHook(() => usePagination(items, 10, 1)); | ||
|
||
expect(result.current.hasNextPage).toBe(true); | ||
expect(result.current.hasPreviousPage).toBe(false); | ||
|
||
act(() => { | ||
result.current.goToPage(5); | ||
}); | ||
|
||
expect(result.current.hasNextPage).toBe(false); | ||
expect(result.current.hasPreviousPage).toBe(true); | ||
}); | ||
}); |