From 6e03d57987b889fed29182034f416dbe5aa11467 Mon Sep 17 00:00:00 2001 From: shellyear Date: Wed, 24 Apr 2024 01:14:27 +0500 Subject: [PATCH 1/4] [fix] define and use custom parseLinkHeader function in pagination --- src/utils/Utils.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/utils/Utils.js b/src/utils/Utils.js index f8eb9d0..f3b7149 100644 --- a/src/utils/Utils.js +++ b/src/utils/Utils.js @@ -407,10 +407,44 @@ export function extractLastPageNumber(response) { if (!linkHeader) { return undefined; } - const links = parseLinkHeader(linkHeader); + const links = customParseLinkHeader(linkHeader); return links.last ? Number(links.last.page) : undefined; } +function customParseLinkHeader(header) { + const linkPairs = header.split(","); + const parsedLinks = {}; + + if (typeof header !== "string") { + throw new Error("Invalid input: header must be a string"); + } + + linkPairs.forEach((linkPair) => { + try { + const match = linkPair.match(/<([^>]+)>;\s*rel="([^"]+)"/); + if (!match) { + throw new Error("Invalid format: linkPair does not contain a valid URL or rel"); + } + + const url = match[1]; + const rel = match[2]; + const urlObj = new URL(url); + + const pageParam = urlObj.searchParams.get("page"); + const page = pageParam ? parseInt(pageParam, 10) : null; + + parsedLinks[rel] = { + url: url, + page: isNaN(page) ? null : page, + }; + } catch (error) { + console.error(`Error parsing link: ${error.message}`); + } + }); + + return parsedLinks; +} + export function sortToParams(sort) { return Object.keys(sort) .filter((k) => sort[k] !== undefined) From ac1f69a01fb177dc97e0024ba61b947adeca1e82 Mon Sep 17 00:00:00 2001 From: shellyear Date: Wed, 24 Apr 2024 02:31:29 +0500 Subject: [PATCH 2/4] [feature] show lastPage number --- src/components/misc/Pagination.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/misc/Pagination.jsx b/src/components/misc/Pagination.jsx index 15b293d..faaeed2 100644 --- a/src/components/misc/Pagination.jsx +++ b/src/components/misc/Pagination.jsx @@ -38,8 +38,8 @@ const Pagination = ({ pageNumber, handlePagination, itemCount, pageCount, allowS handlePagination(pageNumber - 1)} /> {pageNumber + 1} handlePagination(pageNumber + 1)} /> - {pageCount !== undefined && ( - handlePagination(pageCount)} /> + {pageCount !== undefined && pageNumber < pageCount && ( + handlePagination(pageCount)}>{pageCount + 1} )} From f6664005b85f50dbd1004915226f9f6fe83535b5 Mon Sep 17 00:00:00 2001 From: shellyear Date: Thu, 9 May 2024 00:35:01 +0200 Subject: [PATCH 3/4] [feature] implement new pagination --- src/components/misc/Pagination.jsx | 50 +++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/components/misc/Pagination.jsx b/src/components/misc/Pagination.jsx index faaeed2..afaf413 100644 --- a/src/components/misc/Pagination.jsx +++ b/src/components/misc/Pagination.jsx @@ -1,5 +1,5 @@ import React from "react"; -import PropTypes from "prop-types"; +import PropTypes, { array } from "prop-types"; import { Pagination as BSPagination } from "react-bootstrap"; import { DEFAULT_PAGE_SIZE, STORAGE_TABLE_PAGE_SIZE_KEY } from "../../constants/DefaultConstants"; import Input from "../Input"; @@ -10,6 +10,7 @@ import BrowserStorage from "../../utils/BrowserStorage"; * The first page. */ export const INITIAL_PAGE = 0; +export const MIN_PAGINATION_NUMBER = 5; const PAGE_SIZES = [10, 20, 30, 50]; const INFINITE_PAGE_SIZE = Math.pow(2, 31) - 1; @@ -30,17 +31,56 @@ const Pagination = ({ pageNumber, handlePagination, itemCount, pageCount, allowS handlePagination(INITIAL_PAGE); }; + const generatePageNumbers = () => { + let range = Array.from({ length: pageCount }, (v, i) => i); + + if (pageCount < MIN_PAGINATION_NUMBER) { + // show all pages, without ellipsis + return range; + } + + const pagesToShow = 4; + const pageNumbers = []; + let pageNumbersStart = pageNumber >= pageCount - pagesToShow ? pageCount - pagesToShow : pageNumber; + let pageNumbersEnd = Math.min(pageNumber + pagesToShow, pageCount); + + pageNumbers.push(...range.slice(pageNumbersStart, pageNumbersEnd)); + + const isLeftEllipsis = pageNumber > INITIAL_PAGE; + const isRightEllipsis = pageNumber < pageCount / 2; + + if (isLeftEllipsis) { + pageNumbers.splice(0, 0, "..."); + } + if (isRightEllipsis) { + pageNumbers.splice(pageNumbers.length - 1, 1, "..."); + } + + return pageNumbers; + }; + return ( <>
handlePagination(INITIAL_PAGE)} /> handlePagination(pageNumber - 1)} /> - {pageNumber + 1} - handlePagination(pageNumber + 1)} /> - {pageCount !== undefined && pageNumber < pageCount && ( - handlePagination(pageCount)}>{pageCount + 1} + {generatePageNumbers().map((pageNum, i) => { + if (pageNum === "...") { + return ; + } + return ( + handlePagination(pageNum)} active={pageNum === pageNumber} key={i}> + {pageNum + 1} + + ); + })} + {pageCount !== undefined && ( + handlePagination(pageCount)}> + {pageCount + 1} + )} + = pageCount} onClick={() => handlePagination(pageNumber + 1)} />
{allowSizeChange && ( From eda8aa586b79e125bca48d33f55686130a2be9ee Mon Sep 17 00:00:00 2001 From: shellyear Date: Mon, 13 May 2024 21:06:54 +0200 Subject: [PATCH 4/4] [fix] tests for pagination using testing classnames --- src/components/misc/Pagination.jsx | 18 +++++++++++++++--- tests/__tests__/components/Pagination.spec.jsx | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/components/misc/Pagination.jsx b/src/components/misc/Pagination.jsx index afaf413..2088e5c 100644 --- a/src/components/misc/Pagination.jsx +++ b/src/components/misc/Pagination.jsx @@ -63,8 +63,16 @@ const Pagination = ({ pageNumber, handlePagination, itemCount, pageCount, allowS <>
- handlePagination(INITIAL_PAGE)} /> - handlePagination(pageNumber - 1)} /> + handlePagination(INITIAL_PAGE)} + /> + handlePagination(pageNumber - 1)} + /> {generatePageNumbers().map((pageNum, i) => { if (pageNum === "...") { return ; @@ -80,7 +88,11 @@ const Pagination = ({ pageNumber, handlePagination, itemCount, pageCount, allowS {pageCount + 1} )} - = pageCount} onClick={() => handlePagination(pageNumber + 1)} /> + = pageCount} + onClick={() => handlePagination(pageNumber + 1)} + />
{allowSizeChange && ( diff --git a/tests/__tests__/components/Pagination.spec.jsx b/tests/__tests__/components/Pagination.spec.jsx index eb9fd42..f7bb3ea 100644 --- a/tests/__tests__/components/Pagination.spec.jsx +++ b/tests/__tests__/components/Pagination.spec.jsx @@ -17,7 +17,7 @@ describe("Pagination", function () { ); const pagination = TestUtils.findRenderedDOMComponentWithClass(tree, "pagination"); expect(pagination).not.toBeNull(); - const li = TestUtils.scryRenderedDOMComponentsWithTag(tree, "li"); - expect(li.length).toEqual(4); + const li = TestUtils.scryRenderedDOMComponentsWithClass(tree, "test-pag-arrow"); + expect(li.length).toEqual(3); }); });