import { renderHook } from "@testing-library/react" import { describe, expect, it, vi } from "vitest" import { useOffsetPagination } from "./useOffsetPagination" describe("useOffsetPagination", () => { const defaultProps = { currentPage: 1, totalPages: 10, totalItems: 100, pageSize: 10, onPageChange: vi.fn(), onPageSizeChange: vi.fn(), } describe("basic calculations", () => { it("should calculate start and end items correctly", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, currentPage: 3, pageSize: 20, totalItems: 100, }), ) expect(result.current.startItem).toBe(41) // (3-1) * 20 + 1 expect(result.current.endItem).toBe(60) // 3 * 20 }) it("should handle zero total items", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, totalItems: 0, }), ) expect(result.current.startItem).toBe(0) expect(result.current.endItem).toBe(0) expect(result.current.hasResults).toBe(false) expect(result.current.resultsText).toBe("No results") }) it("should handle last page correctly", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, currentPage: 10, totalItems: 95, pageSize: 10, }), ) expect(result.current.startItem).toBe(91) // (10-1) * 10 + 1 expect(result.current.endItem).toBe(95) // min(10 * 10, 95) }) }) describe("page numbers generation", () => { it("should show all pages when totalPages <= 7", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, totalPages: 5, currentPage: 3, }), ) expect(result.current.pageNumbers).toEqual([1, 2, 3, 4, 5]) }) it("should show ellipsis pattern when current page is near beginning and also near the boundary", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, totalPages: 20, currentPage: 2, }), ) expect(result.current.pageNumbers).toEqual([1, 2, 3, "ellipsis", 20]) }) it("should show ellipsis pattern when current page is near end", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, totalPages: 20, currentPage: 19, }), ) expect(result.current.pageNumbers).toEqual([1, "ellipsis", 18, 19, 20]) }) it("should show ellipsis pattern when current page is in middle", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, totalPages: 20, currentPage: 10, }), ) expect(result.current.pageNumbers).toEqual([1, "ellipsis", 9, 10, 11, "ellipsis", 20]) }) }) describe("navigation handlers", () => { it("should call onPageChange when handlePageChange is called with valid page", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 3, }), ) result.current.handlePageChange(5) expect(onPageChange).toHaveBeenCalledWith(5) }) it("should not call onPageChange when handlePageChange is called with current page", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 3, }), ) result.current.handlePageChange(3) expect(onPageChange).not.toHaveBeenCalled() }) it("should not call onPageChange when handlePageChange is called with invalid page", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 3, totalPages: 10, }), ) result.current.handlePageChange(0) result.current.handlePageChange(11) expect(onPageChange).not.toHaveBeenCalled() }) it("should call onPageSizeChange when handlePageSizeChange is called", () => { const onPageSizeChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageSizeChange, }), ) result.current.handlePageSizeChange(25) expect(onPageSizeChange).toHaveBeenCalledWith(25) }) it("should not call onPageSizeChange when callback is not provided", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageSizeChange: undefined, }), ) // Should not throw expect(() => result.current.handlePageSizeChange(25)).not.toThrow() }) it("should handle previous page navigation", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 3, }), ) result.current.handlePreviousPage() expect(onPageChange).toHaveBeenCalledWith(2) }) it("should handle next page navigation", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 3, }), ) result.current.handleNextPage() expect(onPageChange).toHaveBeenCalledWith(4) }) it("should not navigate when at first page", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 1, }), ) result.current.handlePreviousPage() expect(onPageChange).not.toHaveBeenCalled() }) it("should not navigate when at last page", () => { const onPageChange = vi.fn() const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, onPageChange, currentPage: 10, totalPages: 10, }), ) result.current.handleNextPage() expect(onPageChange).not.toHaveBeenCalled() }) }) describe("state checks", () => { it("should correctly identify disabled states", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, currentPage: 1, totalPages: 10, }), ) expect(result.current.isPreviousDisabled).toBe(true) expect(result.current.isNextDisabled).toBe(false) }) it("should correctly identify disabled states at last page", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, currentPage: 10, totalPages: 10, }), ) expect(result.current.isPreviousDisabled).toBe(false) expect(result.current.isNextDisabled).toBe(true) }) it("should correctly identify disabled states in middle", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, currentPage: 5, totalPages: 10, }), ) expect(result.current.isPreviousDisabled).toBe(false) expect(result.current.isNextDisabled).toBe(false) }) }) describe("selection handling", () => { it("should show selection text when rows are selected", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, selectedRows: 5, totalRows: 100, }), ) expect(result.current.hasSelection).toBe(true) expect(result.current.selectionText).toBe("5 of 100 row(s) selected.") }) it("should not show selection text when no rows are selected", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, selectedRows: 0, totalRows: 0, }), ) expect(result.current.hasSelection).toBe(false) expect(result.current.selectionText).toBe(null) }) }) describe("results text", () => { it("should format results text correctly", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, currentPage: 3, pageSize: 20, totalItems: 100, }), ) expect(result.current.resultsText).toBe("Showing 41 to 60 of 100 results") }) it("should show 'No results' when totalItems is 0", () => { const { result } = renderHook(() => useOffsetPagination({ ...defaultProps, totalItems: 0, }), ) expect(result.current.resultsText).toBe("No results") }) }) describe("default values", () => { it("should use default pageSizeOptions when not provided", () => { const { result } = renderHook(() => useOffsetPagination({ currentPage: 1, totalPages: 10, totalItems: 100, pageSize: 10, onPageChange: vi.fn(), }), ) // The hook should work without pageSizeOptions expect(result.current.pageNumbers).toBeDefined() }) it("should use default selectedRows and totalRows when not provided", () => { const { result } = renderHook(() => useOffsetPagination({ currentPage: 1, totalPages: 10, totalItems: 100, pageSize: 10, onPageChange: vi.fn(), }), ) expect(result.current.hasSelection).toBe(false) expect(result.current.selectionText).toBe(null) }) }) })