Skip to content

Commit

Permalink
[PBNTR-258] NEW Pagination kit in React (#3552)
Browse files Browse the repository at this point in the history
This PR adds the Pagination kit in react

![Screenshot 2024-08-09 at 9 18 12
AM](https://github.com/user-attachments/assets/fb0295a1-dadd-4859-b78d-e41ee1da512e)


**How to test?** Steps to confirm the desired behavior:
1. Go to 'the pagination kit and test it


#### Checklist:
- [ ] **LABELS** Add a label: `enhancement`, `bug`, `improvement`, `new
kit`, `deprecated`, or `breaking`. See [Changelog &
Labels](https://github.com/powerhome/playbook/wiki/Changelog-&-Labels)
for details.
- [ ] **DEPLOY** I have added the `milano` label to show I'm ready for a
review.
- [ ] **TESTS** I have added test coverage to my code.
  • Loading branch information
nickamantia authored Aug 23, 2024
1 parent 4b051ec commit 378069b
Show file tree
Hide file tree
Showing 12 changed files with 327 additions and 14 deletions.
2 changes: 2 additions & 0 deletions playbook/app/entrypoints/playbook-doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import * as MultipleUsersStacked from 'kits/pb_multiple_users_stacked/docs'
import * as Nav from 'kits/pb_nav/docs'
import * as OnlineStatus from 'kits/pb_online_status/docs'
import * as Overlay from 'kits/pb_overlay/docs'
import * as Pagination from 'kits/pb_pagination/docs'
import * as Passphrase from 'kits/pb_passphrase/docs'
import * as PbReactPopover from 'kits/pb_popover/docs'
import * as Person from 'kits/pb_person/docs'
Expand Down Expand Up @@ -172,6 +173,7 @@ WebpackerReact.registerComponents({
...Nav,
...OnlineStatus,
...Overlay,
...Pagination,
...Passphrase,
...PbReactPopover,
...Person,
Expand Down
1 change: 1 addition & 0 deletions playbook/app/javascript/kits.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export { default as Nav } from '../pb_kits/playbook/pb_nav/_nav'
export { default as NavItem } from '../pb_kits/playbook/pb_nav/_item'
export { default as OnlineStatus } from '../pb_kits/playbook/pb_online_status/_online_status'
export { default as Overlay} from '../pb_kits/playbook/pb_overlay/_overlay'
export { default as Pagination } from '../pb_kits/playbook/pb_pagination/_pagination'
export { default as Passphrase } from '../pb_kits/playbook/pb_passphrase/_passphrase'
export { default as PbReactPopover } from '../pb_kits/playbook/pb_popover/_popover'
export { default as Person } from '../pb_kits/playbook/pb_person/_person'
Expand Down
62 changes: 49 additions & 13 deletions playbook/app/pb_kits/playbook/pb_pagination/_pagination.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,46 +3,53 @@
@import "../tokens/border_radius";
@import "../tokens/shadows";


$pagination_padding: 7px 13px 6px 13px;
$top_bottom_radius: 0px;

@mixin hover-state {
background-color: $active_light !important;
color: $primary;
border-radius: $border_rad_light;
}

.pb_pagination {
display: inline-block;
border-radius: $border_rad_light;
border: $border_rad_lightest solid $border_light;
background-color: $white;
padding: $space_xxs 0px !important;
li {
padding: $space_xs 0px !important;
li, .pagination-number {
display: inline;
> a, li > span {
> a, li > span, .pagination-number {
padding: $pagination_padding;
text-decoration: none;
}}
li:first-child > a, li:first-child > span {
li:first-child > a, li:first-child > span, .pagination-number, .pagination-left {
background-color: $white;
padding: $pagination_padding;
border-right: $border_rad_lightest solid $border_light;
z-index: 2;
border-top-right-radius: $top_bottom_radius;
border-bottom-right-radius: $top_bottom_radius;
cursor: pointer;
}
li:last-child > a, li:last-child > span {
li:last-child > a, li:last-child > span, .pagination-number, .pagination-right {
padding: $pagination_padding;
border-left: $border_rad_lightest solid $border_light;
z-index: 2;
border-top-left-radius: $top_bottom_radius;
border-bottom-left-radius: $top_bottom_radius;
}
a {
a, .pagination-number {
color: $text_lt_default;
font-size: $text_small;
font-weight: $regular;
border: none;
transition: all $transition_default ease-out;

&:hover {
background-color: $active_light;
color: $primary;
border-radius: $border_rad_light;
@include hover-state;
}

&:focus-visible {
Expand All @@ -52,8 +59,8 @@ $top_bottom_radius: 0px;
transition: none;
}
}
.active > span {
background-color: $primary;
.active > span, .pagination-number.active {
background-color: $primary !important;
border-radius: $border_rad_light;
color: #fff;
padding: $pagination_padding;
Expand All @@ -62,8 +69,37 @@ $top_bottom_radius: 0px;
font-weight: $bold;
font-size: $text_small;
}
.disabled > span {
padding: $pagination_padding;
.disabled {
pointer-events: none;
opacity: 0.5;
color: grey;

& > span {
padding: $pagination_padding;
font-size: $text_small;
}
}

.pagination-right,
.pagination-left {
background-color: $white;
border-top: none;
border-bottom: none;
cursor: pointer;
padding: 7px 11px 6px;
font-size: $text_small;

&:hover {
@include hover-state;
}
}
.pagination-left {
border-left: none;
margin-right: $space_xxs;
}

.pagination-right {
border-right: none;
margin-left: $space_xxs;
}
}
164 changes: 164 additions & 0 deletions playbook/app/pb_kits/playbook/pb_pagination/_pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import React, { useState } from "react";
import classnames from 'classnames'
import { globalProps } from '../utilities/globalProps'
import { buildAriaProps, buildCss, buildDataProps, buildHtmlProps } from '../utilities/props'
import Icon from '../pb_icon/_icon';

type PaginationProps = {
aria?: { [key: string]: string },
className?: string,
data?: { [key: string]: string },
htmlOptions?: {[key: string]: string | number | boolean | (() => void)},
id?: string,
current?: number;
onChange?: (pageNumber: number) => void;
range?: number;
total?: number;
};

const Pagination = ( props: PaginationProps) => {
const {
aria = {},
className,
data = {},
htmlOptions = {},
id,
current = 1,
onChange,
range = 5,
total = 1,
} = props
const [currentPage, setCurrentPage] = useState(current);

const handlePageChange = (pageNumber: number) => {
if (pageNumber >= 1 && pageNumber <= total) {
setCurrentPage(pageNumber);
if (onChange) {
onChange(pageNumber);
}
}
};

const renderPageButtons = (): JSX.Element[] => {
const buttons: JSX.Element[] = [];

// Calculate pagination range with let
let rangeStart = Math.max(1, currentPage - Math.floor(range / 2));
let rangeEnd = Math.min(total, rangeStart + range - 1);

// Adjust range if it's too short to fit the range
if (rangeEnd - rangeStart + 1 < range) {
if (rangeStart > 1) {
rangeStart = Math.max(1, rangeEnd - range + 1);
} else {
rangeEnd = Math.min(total, rangeStart + range - 1);
}
}

// Always display the first page button
if (rangeStart > 1) {
buttons.push(
<li
className="pagination-number"
key={1}
onClick={() => handlePageChange(1)}
>
1
</li>
);
}

// Always display the second page button
if (rangeStart > 2) {
buttons.push(
<li
className="pagination-number"
key={2}
onClick={() => handlePageChange(2)}
>
2
</li>
);
}

// Display page buttons within the calculated range
for (let i = rangeStart; i <= rangeEnd; i++) {
buttons.push(
<li
className={`pagination-number ${i === currentPage ? "active" : ""}`}
key={i}
onClick={() => handlePageChange(i)}
>
{i}
</li>
);
}

// Always display the second-to-last page button
if (rangeEnd < total - 1) {
buttons.push(
<li
className={`pagination-number ${total - 1 === currentPage ? "active" : ""}`}
key={total - 1}
onClick={() => handlePageChange(total - 1)}
>
{total - 1}
</li>
);
}

// Always display the last page button
if (rangeEnd < total) {
buttons.push(
<li
className={`pagination-number ${total === currentPage ? "active" : ""}`}
key={total}
onClick={() => handlePageChange(total)}
>
{total}
</li>
);
}


return buttons;
};


const ariaProps = buildAriaProps(aria)
const dataProps = buildDataProps(data)
const htmlProps = buildHtmlProps(htmlOptions)
const classes = classnames(
buildCss('pb_paginate'),
globalProps(props),
className
)

return (
<div
{...ariaProps}
{...dataProps}
{...htmlProps}
className={classes}
id={id}
>
<div className="pb_pagination">
<li
className={`pagination-left ${currentPage === 1 ? 'disabled' : ''}`}
onClick={() => handlePageChange(currentPage - 1)}
>
<Icon icon="chevron-left" />
</li>
{renderPageButtons()}
<li
className={`pagination-right ${currentPage === total ? 'disabled' : ''}`}
onClick={() => handlePageChange(currentPage + 1)}
>
<Icon icon="chevron-right" />
</li>
</div>
</div>
);
};

export default Pagination;
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react'

import Pagination from '../_pagination'

const PaginationDefault = (props) => {

return (
<>
<Pagination
current={1}
range={5}
total={10}
{...props}
/>
</>
)
}

export default PaginationDefault
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The `range` prop determines how many pages to display in the Pagination component. Regardless of this value, the first two and last two pages are always visible to facilitate navigation to the beginning and end of the pagination. If these always-visible pages fall within the specified range, they are included in the display. If they fall outside the range, the pagination will show additional pages up to the number defined by the `range` prop.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useState } from "react";
import { Table, Pagination } from 'playbook-ui'


import { data } from "./data";

const PaginationPageChange = (props) => {

const [activePage, setActivePage] = useState(1);
const rowsPerPage = 3;
const totalPages = Math.ceil(data.length / rowsPerPage);

const onPageChange = (pageNumber) => {
setActivePage(pageNumber);
};

const currentData = data.slice(
(activePage - 1) * rowsPerPage,
activePage * rowsPerPage
);


return (
<div className="App">
<Table
marginBottom="xs"
responsive="none"
size="sm"
{...props}
>
<Table.Head>
<Table.Row>
<Table.Header>{"Column 1"}</Table.Header>
<Table.Header>{"Column 2"}</Table.Header>
<Table.Header>{"Column 3"}</Table.Header>
<Table.Header>{"Column 4"}</Table.Header>
<Table.Header>{"Column 5"}</Table.Header>
</Table.Row>
</Table.Head>
<Table.Body>
{currentData.map((row, index) => (
<Table.Row key={index}>
{row.map((cell, cellIndex) => (
<Table.Cell key={cellIndex}>{cell}</Table.Cell>
))}
</Table.Row>
))}
</Table.Body>
</Table>

<Pagination
current={1}
onChange={onPageChange}
range={5}
total={totalPages}
{...props}
/>
</div>
)
}

export default PaginationPageChange
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
You can use the `onChange` prop to control the data of your table. This prop is callback function that will allow you control the state.
Loading

0 comments on commit 378069b

Please sign in to comment.