Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
130 changes: 38 additions & 92 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,109 +1,55 @@
import React from 'react';
import React, { useState } from 'react';
import './App.css';
import { getNumbers } from './utils';
import { Pagination } from './components/Pagination';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const items = getNumbers(1, 42).map(n => `Item ${n}`);
const total: number = 42;

const items = getNumbers(1, total).map(n => `Item ${n}`);

export const App: React.FC = () => {
const [perPage, setPerPage] = useState<number>(5);
const [currentPage, setCurrentPage] = useState<number>(1);

const fromIndex = (currentPage - 1) * perPage;
const fromItem = fromIndex + 1;
const toItem = Math.min(fromIndex + perPage, total);
const pageInfo = `Page ${currentPage} (items ${fromItem} - ${toItem} of ${total})`;
const selectedItems = items.slice(fromIndex, toItem);

function handlePageChange(page: number) {
if (currentPage !== page) {
setCurrentPage(page);
}
}

function handleSelectChange(count: number) {
setPerPage(count);
setCurrentPage(1);
}

return (
<div className="container">
<h1>Items with Pagination</h1>

<p className="lead" data-cy="info">
Page 1 (items 1 - 5 of 42)
{pageInfo}
</p>

<div className="form-group row">
<div className="col-3 col-sm-2 col-xl-1">
<select
data-cy="perPageSelector"
id="perPageSelector"
className="form-control">
<option value="3">3</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="20">20</option>
</select>
</div>

<label htmlFor="perPageSelector" className="col-form-label col">
items per page
</label>
</div>
<Pagination
total={total}
perPage={perPage}
currentPage={currentPage}
onPageChange={handlePageChange}
onSelectChange={handleSelectChange}
/>

{/* Move this markup to Pagination */}
<ul className="pagination">
<li className="page-item disabled">
<a
data-cy="prevLink"
className="page-link"
href="#prev"
aria-disabled="true">
«
</a>
</li>
<li className="page-item active">
<a data-cy="pageLink" className="page-link" href="#1">
1
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#2">
2
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#3">
3
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#4">
4
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#5">
5
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#6">
6
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#7">
7
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#8">
8
</a>
</li>
<li className="page-item">
<a data-cy="pageLink" className="page-link" href="#9">
9
</a>
</li>
<li className="page-item">
<a
data-cy="nextLink"
className="page-link"
href="#next"
aria-disabled="false">
»
</a>
</li>
</ul>
<ul>
<li data-cy="item">Item 1</li>
<li data-cy="item">Item 2</li>
<li data-cy="item">Item 3</li>
<li data-cy="item">Item 4</li>
<li data-cy="item">Item 5</li>
{selectedItems.map(item => (
<li key={item} data-cy="item">
{item}
</li>
))}
</ul>
</div>
);
Expand Down
117 changes: 116 additions & 1 deletion src/components/Pagination/Pagination.tsx
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

a lot of magic numbers in your code. it's better to move these numbers into well-named constants

Original file line number Diff line number Diff line change
@@ -1 +1,116 @@
export const Pagination = () => {};
import { getNumbers } from '../../utils';
import classNames from 'classnames';

interface PaginationProps {
total: number;
perPage?: number;
currentPage?: number;
onPageChange: (page: number) => void;
onSelectChange: (perPage: number) => void;
}

const SelectOptions: number[] = [3, 5, 10, 20];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

best practice to name static constants using UPPERCASE + snake_case.

e.g. SELECT_OPTIONS


export const Pagination = ({
total,
perPage = 5,
currentPage = 1,
onPageChange,
onSelectChange,
}: PaginationProps) => {
const pageCount = Math.ceil(total / perPage);
const pageList: number[] = getNumbers(1, pageCount);

function handlePageChange(page: number) {
if (page === currentPage) {
return;
}

if (page < 1 || page > pageCount) {
return;
}

onPageChange(page);
}

function handleSelectChange(event: React.ChangeEvent<HTMLSelectElement>) {
const value: string = event.currentTarget.value;

onSelectChange(Number(value));
}

return (
<>
<div className="form-group row">
<div className="col-3 col-sm-2 col-xl-1">
<select
data-cy="perPageSelector"
id="perPageSelector"
className="form-control"
value={perPage}
onChange={handleSelectChange}
>
{SelectOptions.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</div>

<label htmlFor="perPageSelector" className="col-form-label col">
items per page
</label>
</div>
<ul className="pagination">
<li
className={classNames('page-item', {
disabled: currentPage === 1,
})}
>
<a
data-cy="prevLink"
className="page-link"
href="#prev"
aria-disabled={currentPage === 1}
onClick={() => handlePageChange(currentPage - 1)}
>
«
</a>
</li>
{pageList.map(page => (
<li
key={page}
className={classNames('page-item', {
active: page === currentPage,
})}
>
<a
data-cy="pageLink"
className="page-link"
href={`#${page}`}
onClick={() => handlePageChange(page)}
>
{page}
</a>
</li>
))}
<li
className={classNames('page-item', {
disabled: currentPage === pageCount,
})}
>
<a
data-cy="nextLink"
className="page-link"
href="#next"
aria-disabled={currentPage === pageCount}
onClick={() => handlePageChange(currentPage + 1)}
>
»
</a>
</li>
</ul>
</>
);
};
Loading