Skip to content

Commit

Permalink
Merge pull request #155 from akaene/fix/pagination
Browse files Browse the repository at this point in the history
define and use custom parseLinkHeader function in pagination
  • Loading branch information
blcham committed May 13, 2024
2 parents a1ddf7f + eda8aa5 commit 94098ac
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 9 deletions.
64 changes: 58 additions & 6 deletions src/components/misc/Pagination.jsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand All @@ -30,17 +31,68 @@ 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 (
<>
<div className="d-flex justify-content-center">
<BSPagination>
<BSPagination.First disabled={pageNumber === INITIAL_PAGE} onClick={() => handlePagination(INITIAL_PAGE)} />
<BSPagination.Prev disabled={pageNumber === INITIAL_PAGE} onClick={() => handlePagination(pageNumber - 1)} />
<BSPagination.Item active={true}>{pageNumber + 1}</BSPagination.Item>
<BSPagination.Next disabled={itemCount < pageSize} onClick={() => handlePagination(pageNumber + 1)} />
<BSPagination.First
className="test-pag-arrow"
disabled={pageNumber === INITIAL_PAGE}
onClick={() => handlePagination(INITIAL_PAGE)}
/>
<BSPagination.Prev
className="test-pag-arrow"
disabled={pageNumber === INITIAL_PAGE}
onClick={() => handlePagination(pageNumber - 1)}
/>
{generatePageNumbers().map((pageNum, i) => {
if (pageNum === "...") {
return <BSPagination.Ellipsis key={i} />;
}
return (
<BSPagination.Item onClick={() => handlePagination(pageNum)} active={pageNum === pageNumber} key={i}>
{pageNum + 1}
</BSPagination.Item>
);
})}
{pageCount !== undefined && (
<BSPagination.Last disabled={pageNumber === pageCount} onClick={() => handlePagination(pageCount)} />
<BSPagination.Item active={pageNumber === pageCount} onClick={() => handlePagination(pageCount)}>
{pageCount + 1}
</BSPagination.Item>
)}
<BSPagination.Next
className="test-pag-arrow"
disabled={pageNumber >= pageCount}
onClick={() => handlePagination(pageNumber + 1)}
/>
</BSPagination>
</div>
{allowSizeChange && (
Expand Down
36 changes: 35 additions & 1 deletion src/utils/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions tests/__tests__/components/Pagination.spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

0 comments on commit 94098ac

Please sign in to comment.