Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fix

- paging when displayed inside a form.

### Added

- possibility to customize back/next first/last page navigation components.

### Removed

- remove cypress declarations
Expand Down
55 changes: 55 additions & 0 deletions cypress/cypress/component/Paging/Paging.cy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,59 @@ describe("Paging.cy.tsx", () => {
.replace("{total}", (pages * itemsPerPage).toString()),
);
});

it("custom navigationComponents work correctly", () => {
const itemsPerPage = 10;
const pages = 5;
const currentPage = 3;
const translations = { showedItemsText: "Item {from} to {to} from {total}", itemsPerPageDropdown: "Items per page" };
const customNavigationComponents = {
backPageComponent: "←",
nextPageComponent: "→",
firstPageComponent: "⇤",
lastPageComponent: "⇥",
};

mount(
<Paging
currentItemsPerPage={itemsPerPage}
currentPage={currentPage}
currentRecordCount={itemsPerPage}
setCurrentPage={cy.spy().as("setCurrentPage")}
totalRecords={pages * itemsPerPage}
setItemsPerPage={cy.spy().as("setItemsPerPage")}
translations={translations}
navigationComponents={customNavigationComponents}
/>,
);

// Check that custom navigation components are rendered
cy.get("[data-cy-root] > .container-fluid > .row > .col-6:nth-of-type(2) > .btn-group > button.btn").then(
(items: JQuery<HTMLElement>) => {
// Verify custom symbols are present
cy.wrap(items.filter((_, item) => item.textContent === "⇤"))
.should("exist")
.and("be.visible");
cy.wrap(items.filter((_, item) => item.textContent === "←"))
.should("exist")
.and("be.visible");
cy.wrap(items.filter((_, item) => item.textContent === "→"))
.should("exist")
.and("be.visible");
cy.wrap(items.filter((_, item) => item.textContent === "⇥"))
.should("exist")
.and("be.visible");

// Test functionality with custom components
cy.wrap(items.filter((_, item) => item.textContent === "⇤")).click();
cy.get("@setCurrentPage").should("be.calledWith", 1);
cy.wrap(items.filter((_, item) => item.textContent === "←")).click();
cy.get("@setCurrentPage").should("be.calledWith", currentPage - 1);
cy.wrap(items.filter((_, item) => item.textContent === "→")).click();
cy.get("@setCurrentPage").should("be.calledWith", currentPage + 1);
cy.wrap(items.filter((_, item) => item.textContent === "⇥")).click();
cy.get("@setCurrentPage").should("be.calledWith", pages);
},
);
});
});
29 changes: 23 additions & 6 deletions src/lib/Paging/Paging.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Button, ButtonGroup, Col, DropdownItem, DropdownMenu, DropdownToggle, Row, UncontrolledButtonDropdown } from "reactstrap";
import { useMemo } from "react";
import { ReactNode, useMemo } from "react";

interface PagingProps {
currentItemsPerPage: number;
Expand All @@ -8,6 +8,7 @@ interface PagingProps {
currentRecordCount: number;
pagingPossible?: boolean;
translations: PagingTranslations;
navigationComponents?: PagingNavigationComponents;
possiblePageItemCounts?: number[];
maxPagesShown?: number;
showControls?: boolean;
Expand All @@ -21,6 +22,20 @@ interface PagingTranslations {
itemsPerPageDropdown: string;
}

interface PagingNavigationComponents {
backPageComponent: ReactNode;
nextPageComponent: ReactNode;
firstPageComponent: ReactNode;
lastPageComponent: ReactNode;
}

const defaultNavigationComponents: PagingNavigationComponents = {
backPageComponent: "<",
nextPageComponent: ">",
firstPageComponent: "<<",
lastPageComponent: ">>",
};

// eslint-disable-next-line complexity
const Paging = ({
currentItemsPerPage,
Expand All @@ -29,13 +44,15 @@ const Paging = ({
currentRecordCount,
pagingPossible = true,
translations,
navigationComponents = defaultNavigationComponents,
possiblePageItemCounts,
maxPagesShown = 7,
showControls = true,
changePageSizePossible = true,
setItemsPerPage,
setCurrentPage,
}: PagingProps) => {
const { backPageComponent, nextPageComponent, firstPageComponent, lastPageComponent } = navigationComponents;
const maxPage = Math.ceil(totalRecords / currentItemsPerPage);
const firstPageShown = Math.max(0, Math.min(currentPage - Math.ceil(maxPagesShown / 2), maxPage - maxPagesShown));

Expand Down Expand Up @@ -79,12 +96,12 @@ const Paging = ({
<ButtonGroup size="sm">
{showControls && (
<Button color="secondary" outline disabled={currentPage === 1} onClick={() => setCurrentPage(1)}>
{"<<"}
{firstPageComponent}
</Button>
)}
{showControls && (
<Button color="secondary" outline disabled={currentPage === 1} onClick={() => setCurrentPage(currentPage - 1)}>
{"<"}
{backPageComponent}
</Button>
)}
{Array.from({ length: maxPage + 1 }, (_, i) => i)
Expand All @@ -97,12 +114,12 @@ const Paging = ({
))}
{showControls && (
<Button color="secondary" outline disabled={currentPage === maxPage} onClick={() => setCurrentPage(currentPage + 1)}>
{">"}
{nextPageComponent}
</Button>
)}
{showControls && (
<Button color="secondary" outline disabled={currentPage === maxPage} onClick={() => setCurrentPage(maxPage)}>
{">>"}
{lastPageComponent}
</Button>
)}
</ButtonGroup>
Expand All @@ -113,4 +130,4 @@ const Paging = ({
);
};

export { Paging, PagingProps, PagingTranslations };
export { Paging, PagingProps, PagingTranslations, PagingNavigationComponents };
2 changes: 1 addition & 1 deletion styles/Form/_form.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
form div[role="group"] {
form div[role="group"]:not(.paging div[role="group"]) {
display: flex;
gap: 0.25rem;

Expand Down
Loading