Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

> Here is the [working version](https://mate-academy.github.io/react_pagination/)

You a given a list of items and markup for the `Pagination`. Implement the
You a given a list of items and markup for the `Pagination`. Implement the
`Pagination` as a stateless component to show only the items for a current page.

1. The `Pagination` should be used with the next props:
Expand All @@ -24,12 +24,12 @@ You a given a list of items and markup for the `Pagination`. Implement the
1. Show the pagination info inside `data-cy="info"` in the next format `Page 1 (items 1 - 5 of 42)`;
1. Implement the `<select data-cy="perPageSelector">` with `3`, `5`, `10`, `20` options to change the `perPage`;
- show the 1st page after changing a `perPage`;
1. (*) Use React Router to save `?page=2&perPage=7` in the URL and apply them on page load
10. (*) Use React Router to save `?page=2&perPage=7` in the URL and apply them on page load

## Instructions

- Install Prettier Extention and use this [VSCode settings](https://mate-academy.github.io/fe-program/tools/vscode/settings.json) to enable format on save.
- Implement a solution following the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline).
- Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript).
- Open one more terminal and run tests with `npm test` to ensure your solution is correct.
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://<your_account>.github.io/react_pagination/) and add it to the PR description.
- Replace `<your_account>` with your Github username in the [DEMO LINK](https://Cumacea.github.io/react_pagination/) and add it to the PR description.
113 changes: 39 additions & 74 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,49 @@
import React from 'react';
import { useSearchParams } from 'react-router-dom';
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 = 42;
const items = getNumbers(1, TOTAL).map(n => `Item ${n}`);

export const App: React.FC = () => {
const [searchParams, setSearchParams] = useSearchParams();

const currentPage = Number(searchParams.get('page') || 1);
const perPage = Number(searchParams.get('perPage') || 5);

const onPageChange = (page: number) => {
setSearchParams({ page: String(page), perPage: String(perPage) });
};

const handleSelectChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
setSearchParams({ page: '1', perPage: event.target.value });
};

const start = (currentPage - 1) * perPage;
const end = start + perPage;
const startItem = start + 1;
const lastItem = Math.min(end, TOTAL);

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

<p className="lead" data-cy="info">
Page 1 (items 1 - 5 of 42)
Page {currentPage} (items {startItem} - {lastItem} of {TOTAL})
</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">
className="form-control"
value={perPage}
onChange={handleSelectChange}
>
<option value="3">3</option>
<option value="5">5</option>
<option value="10">10</option>
Expand All @@ -32,78 +56,19 @@ export const App: React.FC = () => {
</label>
</div>

{/* 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>
<Pagination
total={TOTAL}
perPage={perPage}
currentPage={currentPage}
onPageChange={onPageChange}
/>

<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>
{items.slice(start, end).map(item => (
<li key={item} data-cy="item">
{item}
</li>
))}
</ul>
</div>
);
Expand Down
87 changes: 86 additions & 1 deletion src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,86 @@
export const Pagination = () => {};
import React from 'react';
import classNames from 'classnames';
import { getNumbers } from '../../utils';

type Props = {
total: number;
perPage: number;
currentPage?: number;
onPageChange: (page: number) => void;
};

export const Pagination: React.FC<Props> = ({
total,
perPage,
currentPage = 1,
onPageChange,
}) => {
const pagesCount = Math.ceil(total / perPage);
const pages = getNumbers(1, pagesCount);

return (
<ul className="pagination">
<li className={classNames('page-item', { disabled: currentPage === 1 })}>
<a
data-cy="prevLink"
className="page-link"
href="#prev"
aria-disabled={currentPage === 1 ? 'true' : 'false'}
onClick={event => {
event.preventDefault();

if (currentPage !== 1) {
onPageChange(currentPage - 1);
}
}}
>
«
</a>
</li>

{pages.map(page => (
<li
key={page}
className={classNames('page-item', { active: currentPage === page })}
>
<a
data-cy="pageLink"
className="page-link"
href={`#${page}`}
onClick={event => {
event.preventDefault();

if (page !== currentPage) {
onPageChange(page);
}
}}
>
{page}
</a>
</li>
))}

<li
className={classNames('page-item', {
disabled: currentPage === pagesCount,
})}
>
<a
data-cy="nextLink"
className="page-link"
href="#next"
aria-disabled={currentPage === pagesCount ? 'true' : 'false'}
onClick={event => {
event.preventDefault();

if (currentPage !== pagesCount) {
onPageChange(currentPage + 1);
}
}}
>
»
</a>
</li>
</ul>
);
};
7 changes: 6 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';

import { App } from './App';

createRoot(document.getElementById('root') as HTMLElement).render(<App />);
createRoot(document.getElementById('root') as HTMLElement).render(
<BrowserRouter>
<App />
</BrowserRouter>,
);
Loading