From d13e8a23c12e95e6fd5eac39b685f1fdcd837e01 Mon Sep 17 00:00:00 2001 From: Huw Wilkins Date: Mon, 26 Aug 2024 15:52:34 +1000 Subject: [PATCH] refactor: update TablePaginationControls to be used outside of TablePagination --- .../TablePagination/TablePagination.scss | 4 +- .../TablePagination.stories.tsx | 26 ++++ .../TablePagination/TablePagination.tsx | 4 +- .../TablePaginationControls.test.tsx | 141 ++++++++++++++++-- .../TablePaginationControls.tsx | 101 +++++++++---- .../TablePagination.test.tsx.snap | 3 +- src/components/TablePagination/utils.tsx | 6 +- src/index.ts | 1 + 8 files changed, 238 insertions(+), 48 deletions(-) diff --git a/src/components/TablePagination/TablePagination.scss b/src/components/TablePagination/TablePagination.scss index cc7dd0dea..67cc0627d 100644 --- a/src/components/TablePagination/TablePagination.scss +++ b/src/components/TablePagination/TablePagination.scss @@ -18,7 +18,7 @@ } .back { - margin: 0 $spv--large; + margin: 0 0 0 $spv--large; .p-icon--chevron-down { rotate: 90deg; @@ -34,7 +34,7 @@ } .pagination-input { - margin-right: $spv--small; + margin: 0 $spv--small 0 $spv--large; min-width: 0; width: 3rem; } diff --git a/src/components/TablePagination/TablePagination.stories.tsx b/src/components/TablePagination/TablePagination.stories.tsx index 433dccd10..cc3371cdf 100644 --- a/src/components/TablePagination/TablePagination.stories.tsx +++ b/src/components/TablePagination/TablePagination.stories.tsx @@ -3,6 +3,7 @@ import { Meta, StoryObj } from "@storybook/react"; import TablePagination from "./TablePagination"; import MainTable from "../MainTable"; +import TablePaginationControls from "./TablePaginationControls"; const meta: Meta = { component: TablePagination, @@ -393,3 +394,28 @@ export const RenderBelow: Story = { name: "RenderBelow", }; + +/** The table pagination controls can be used without wrapping MainTable by + * using the `TablePaginationControls` component. + */ +export const ControlsOnly: Story = { + render: () => { + return ( + + ); + }, +}; diff --git a/src/components/TablePagination/TablePagination.tsx b/src/components/TablePagination/TablePagination.tsx index 9200e4910..435006bca 100644 --- a/src/components/TablePagination/TablePagination.tsx +++ b/src/components/TablePagination/TablePagination.tsx @@ -17,7 +17,7 @@ export type BasePaginationProps = { /** * list of data elements to be paginated. This component is un-opinionated about * the structure of the data but it should be identical to the data structure - * reuiqred by the child table component + * required by the child table component */ data: unknown[]; /** @@ -192,7 +192,7 @@ const TablePagination = (props: Props) => { const controls = ( ", () => { // snapshot tests it("renders table pagination controls and matches the snapshot", () => { render( ", () => { expect(screen.getAllByRole("button")).toMatchSnapshot(); expect(screen.getByRole("spinbutton")).toMatchSnapshot(); }); + + it("can go to the next page", async () => { + const onPageChange = jest.fn(); + const onNextPage = jest.fn(); + render( + + ); + await userEvent.click( + screen.getByRole("button", { name: Label.NEXT_PAGE }) + ); + expect(onPageChange).toHaveBeenCalledWith(3); + expect(onNextPage).toHaveBeenCalledWith(3); + }); + + it("can go to the previous page", async () => { + const onPageChange = jest.fn(); + const onPreviousPage = jest.fn(); + render( + + ); + await userEvent.click( + screen.getByRole("button", { name: Label.PREVIOUS_PAGE }) + ); + expect(onPageChange).toHaveBeenCalledWith(1); + expect(onPreviousPage).toHaveBeenCalledWith(1); + }); + + it("can set the page using the input", async () => { + const onInputPageChange = jest.fn(); + render( + + ); + await userEvent.type( + screen.getByRole("spinbutton", { name: Label.PAGE_NUMBER }), + "1" + ); + expect(onInputPageChange).toHaveBeenCalledWith(21); + }); + + it("can hide the page input", async () => { + render( + + ); + expect( + screen.queryByRole("spinbutton", { name: Label.PAGE_NUMBER }) + ).not.toBeInTheDocument(); + }); + + it("can display the description", async () => { + render( + + ); + expect(document.querySelector(".description")?.textContent).toBe( + "Showing 5 out of 100 items" + ); + }); + + it("can hide the description", async () => { + render( + + ); + expect( + document.querySelector(".description")?.textContent + ).not.toBeUndefined(); + }); }); diff --git a/src/components/TablePagination/TablePaginationControls/TablePaginationControls.tsx b/src/components/TablePagination/TablePaginationControls/TablePaginationControls.tsx index 5b129dfaf..3e75ed144 100644 --- a/src/components/TablePagination/TablePaginationControls/TablePaginationControls.tsx +++ b/src/components/TablePagination/TablePaginationControls/TablePaginationControls.tsx @@ -1,4 +1,4 @@ -import Button from "components/Button"; +import Button, { ButtonProps } from "components/Button"; import Icon from "components/Icon"; import Input from "components/Input"; import Select from "components/Select"; @@ -15,35 +15,65 @@ import { InternalControlProps, } from "../TablePagination"; +export enum Label { + NEXT_PAGE = "Next page", + PREVIOUS_PAGE = "Previous page", + PAGE_NUMBER = "Page number", +} + export type AllProps = BasePaginationProps & InternalControlProps & ExternalControlProps; export type Props = Omit< AllProps, - "externallyControlled" | "dataForwardProp" | "position" -> & - HTMLAttributes; + | "currentPage" + | "data" + | "dataForwardProp" + | "externallyControlled" + | "onPageChange" + | "position" + | "totalItems" +> & { + currentPage?: AllProps["currentPage"]; + displayDescription?: boolean; + onInputPageChange?: (page: number) => void; + nextButtonProps?: Partial; + onNextPage?: (page: number) => void; + onPageChange?: AllProps["onPageChange"]; + onPreviousPage?: (page: number) => void; + previousButtonProps?: Partial; + totalItems?: AllProps["totalItems"]; + visibleCount?: number; + showPageInput?: boolean; +} & HTMLAttributes; const TablePaginationControls = ({ - data, className, - itemName, - description, - pageLimits, - totalItems, currentPage, - pageSize, + description, + displayDescription = true, + onInputPageChange, + itemName, + nextButtonProps, + onNextPage, onPageChange, onPageSizeChange, + onPreviousPage, + pageLimits, + pageSize, + previousButtonProps, + showPageInput = true, + totalItems, + visibleCount, ...divProps }: Props): JSX.Element => { const isSmallScreen = useFigureSmallScreen(); - const totalPages = Math.ceil(totalItems / pageSize); + const totalPages = totalItems ? Math.ceil(totalItems / pageSize) : null; const descriptionDisplay = getDescription({ description, - data, + visibleCount, isSmallScreen, totalItems, itemName, @@ -51,19 +81,22 @@ const TablePaginationControls = ({ const handleDecrementPage = (currentPage: number) => { if (currentPage > 1) { - onPageChange(currentPage - 1); + onPageChange?.(currentPage - 1); } + onPreviousPage?.(typeof currentPage === "number" ? currentPage - 1 : null); }; const handleIncrementPage = (currentPage: number, totalPages: number) => { if (currentPage < totalPages) { - onPageChange(currentPage + 1); + onPageChange?.(currentPage + 1); } + onNextPage?.(typeof currentPage === "number" ? currentPage + 1 : null); }; const handleInputPageChange = (e: ChangeEvent) => { const newPage = Math.min(totalPages, Math.max(1, parseInt(e.target.value))); - onPageChange(newPage); + onPageChange?.(newPage); + onInputPageChange?.(Number(e.target.value)); }; const handlePageSizeChange = (e: ChangeEvent) => { @@ -77,35 +110,45 @@ const TablePaginationControls = ({ role="navigation" >
- {descriptionDisplay} + {displayDescription ? descriptionDisplay : null}
- {" "} - of {totalPages} + {showPageInput ? ( + <> + {" "} + + ) : null} + {typeof totalPages === "number" ? `of ${totalPages}` : null} diff --git a/src/components/TablePagination/__snapshots__/TablePagination.test.tsx.snap b/src/components/TablePagination/__snapshots__/TablePagination.test.tsx.snap index 0fc40b997..c3f9e9320 100644 --- a/src/components/TablePagination/__snapshots__/TablePagination.test.tsx.snap +++ b/src/components/TablePagination/__snapshots__/TablePagination.test.tsx.snap @@ -42,8 +42,7 @@ exports[` renders table pagination and matches the snapshot 1 - of  - 1 + of 1