Skip to content

Commit

Permalink
Add pagination component (#291)
Browse files Browse the repository at this point in the history
* Add Pagination component

* Add Pagination changes

* Optimize pagination code

* Fix incorrect gap for pagination
  • Loading branch information
vineethasok authored Jan 8, 2024
1 parent 9662486 commit 05cbbdd
Show file tree
Hide file tree
Showing 5 changed files with 212 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/components/Pagination/Pagination.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Pagination } from "./Pagination";

export default {
component: Pagination,
title: "Display/Pagination",
tags: ["pagination", "autodocs"],
argTypes: {
rowCount: {
control: { type: "text" },
},
},
};

export const Playground = {
args: {
currentPage: 1,
options: [250, 500],
},
};
83 changes: 83 additions & 0 deletions src/components/Pagination/Pagination.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { renderCUI } from "@/utils/test-utils";
import { Pagination, PaginationProps } from "@/components";
import { fireEvent } from "@testing-library/dom";

describe("Pagination", () => {
const onChange = jest.fn();
const renderPagination = (props: PaginationProps) => {
return renderCUI(<Pagination {...props} />);
};

it("render Pagination", () => {
const { queryByTestId, getByDisplayValue, queryByText } = renderPagination({
currentPage: 1,
onChange,
totalPages: 2,
rowCount: 200,
maxRowsPerPageList: [250, 500],
});
expect(queryByTestId("prev-btn")).not.toBeNull();
expect(queryByTestId("next-btn")).not.toBeNull();
expect(getByDisplayValue("1")).not.toBeNull();
expect(queryByText("All rows")).not.toBeNull();
expect(queryByText("200 rows")).not.toBeNull();
});

it("disable prev button on first page only", () => {
const { getByTestId } = renderPagination({
currentPage: 1,
onChange,
});
const prevBtn = getByTestId("prev-btn");
expect(prevBtn).toHaveProperty("disabled", true);
});

it("disable next button on last page only", () => {
const { getByTestId } = renderPagination({
currentPage: 2,
totalPages: 2,
onChange,
});
const nextBtn = getByTestId("next-btn");
expect(nextBtn).toHaveProperty("disabled", true);
});

it("disable next button on first page if only one page is present", () => {
const { getByTestId } = renderPagination({
currentPage: 1,
totalPages: 1,
onChange,
});
const nextBtn = getByTestId("next-btn");
expect(nextBtn).toHaveProperty("disabled", true);
});

it("should call onChange when input is changed", () => {
const { getByDisplayValue } = renderPagination({
currentPage: 1,
onChange,
});
const pageInput = getByDisplayValue("1");
fireEvent.input(pageInput, {
target: {
value: "2",
},
});
expect(onChange).toBeCalledTimes(1);
});

it("should call onPageSizeChange when pageSize option are selected", () => {
const onPageSizeChange = jest.fn();
const { getByText } = renderPagination({
currentPage: 1,
onChange,
onPageSizeChange,
maxRowsPerPageList: [250, 500],
});
const selector = getByText("All rows");
fireEvent.click(selector);
expect(getByText("250 rows")).not.toBeNull();
fireEvent.click(getByText("250 rows"));
expect(onPageSizeChange).toBeCalledTimes(1);
});
});
107 changes: 107 additions & 0 deletions src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { KeyboardEventHandler, ReactElement } from "react";
import {
Container,
ContainerProps,
IconButton,
NumberField,
Select,
Text,
} from "@/components";
import styled from "styled-components";

export interface PaginationProps extends Omit<ContainerProps, "children" | "onChange"> {
totalPages?: number;
currentPage: number;
maxRowsPerPageList?: Array<number>;
rowCount?: number | string;
onChange: (pageNumber: number) => void;
onPageSizeChange?: (pageNumber: number) => void;
pageSize?: number;
}
const CustomSelect = styled.div`
width: 150px;
`;

export const Pagination = ({
totalPages,
currentPage,
maxRowsPerPageList = [],
rowCount,
onChange: onChangeProp,
onPageSizeChange: onPageSizeChangeProp,
pageSize = -1,
...props
}: PaginationProps): ReactElement => {
const hasRowCount = ["number", "string"].includes(typeof rowCount);
const onKeyDown: KeyboardEventHandler<HTMLInputElement> = e => {
if (e.key === "ArrowUp" || e.key === "ArrowRight") {
onChangeProp(currentPage + 1);
} else if (e.key === "ArrowDown" || e.key === "ArrowLeft") {
onChangeProp(currentPage - 1);
}
};

const onChange = (value: string) => {
onChangeProp(Number(value));
};

const onPageSizeChange = (value: string) => {
if (typeof onPageSizeChangeProp === "function") {
onPageSizeChangeProp(Number(value));
}
};

return (
<Container
gap="md"
justifyContent={
rowCount || maxRowsPerPageList.length > 0 ? "space-between" : "center"
}
{...props}
>
{hasRowCount && <Text component="div">{rowCount} rows</Text>}
<Container gap="xxs">
<IconButton
icon="chevron-left"
type="ghost"
disabled={currentPage === 1}
data-testid="prev-btn"
/>
<Container maxWidth="50px">
<NumberField
onChange={onChange}
value={currentPage}
loading={false}
onKeyDown={onKeyDown}
min={1}
max={totalPages}
/>
</Container>
{!!totalPages && <Text component="div">of {totalPages}</Text>}
<IconButton
icon="chevron-right"
type="ghost"
disabled={!!totalPages && currentPage === totalPages}
data-testid="next-btn"
/>
</Container>
{maxRowsPerPageList.length > 0 && (
<CustomSelect
as={Select}
onSelect={onPageSizeChange}
value={pageSize.toString()}
>
<Select.Item value="-1">All rows</Select.Item>
{maxRowsPerPageList.map(option => (
<Select.Item
key={option}
value={option.toString()}
>
{option} rows
</Select.Item>
))}
</CustomSelect>
)}
</Container>
);
};
1 change: 1 addition & 0 deletions src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export { Logo } from "./Logos/Logo";
export { NumberField } from "./Input/NumberField";
export { PasswordField } from "./Input/PasswordField";
export { Popover } from "./Popover/Popover";
export { Pagination } from "./Pagination/Pagination";
export { Panel } from "./Panel/Panel";
export { ProgressBar } from "./ProgressBar/ProgressBar";
export { RadioGroup } from "./RadioGroup/RadioGroup";
Expand Down
3 changes: 2 additions & 1 deletion src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export type {
AutoCompleteProps,
AutoCompleteOptionListItem,
} from "./AutoComplete/AutoComplete";
export type { PaginationProps } from "./Pagination/Pagination";

export type { IconButtonProps };
export type { AlertProps };
Expand All @@ -67,7 +68,7 @@ export type { ButtonProps };
export type { CardSecondaryProps, BadgeState };
export type { CardPrimaryProps };
export type { CheckboxProps };
export type { ContainerProps};
export type { ContainerProps };
export type { ContextMenuProps };
export type { GridContainerProps };
export type { HoverCardProps };
Expand Down

1 comment on commit 05cbbdd

@vercel
Copy link

@vercel vercel bot commented on 05cbbdd Jan 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

click-ui – ./

click-ui-git-main-clickhouse.vercel.app
click-ui.vercel.app
click-ui-clickhouse.vercel.app

Please sign in to comment.