Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 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 Down Expand Up @@ -32,4 +32,4 @@ You a given a list of items and markup for the `Pagination`. Implement the
- 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://aholubko.github.io/react_pagination/) and add it to the PR description.
108 changes: 35 additions & 73 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
import React from 'react';
import React, { useState } from 'react';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The task requires using React Router to persist and apply query params (e.g., ?page=2&perPage=7). There are no React Router imports or usage here. Import and use React Router hooks (for example, useSearchParams or useLocation/useNavigate) so the app can read query params on load and update them when page/perPage change.

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}`);

export const App: React.FC = () => {
const [currentPage, setCurrentPage] = useState<number>(1);
const [perPage, setPerPage] = useState<number>(5);
Comment on lines +10 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

currentPage and perPage are initialized from hardcoded useState values. Per requirements, these should be initialized from URL query parameters when present (fallback to defaults 1 and 5). Parse the query params on mount and set initial state accordingly.


const total = items.length;
const startIndex = (currentPage - 1) * perPage;
const endIndex = startIndex + perPage;
const visibleItems = items.slice(startIndex, endIndex);

const visibleFrom = total === 0 ? 0 : startIndex + 1;
const visibleTo = Math.min(endIndex, 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 {visibleFrom} - {visibleTo} 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={event => {
const newPerPage = Number(event.target.value);

setPerPage(newPerPage);
setCurrentPage(1);
}}
Comment on lines +36 to +41
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When the perPage selector changes you reset to page 1 and update state, which is correct, but you also must update the URL query params so ?page=1&perPage= is reflected in the address bar. Update the search params here (via router) in addition to updating state.

>
<option value="3">3</option>
<option value="5">5</option>
<option value="10">10</option>
Expand All @@ -33,77 +53,19 @@ export const App: React.FC = () => {
</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={setCurrentPage}
Comment on lines +56 to +60
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pagination's onPageChange is wired to setCurrentPage directly. You should wrap this so that when the page changes you both update component state and persist the new ?page value in the URL (keeping perPage in sync). This ensures reload/bookmark restores the same pagination state as required.

/>

<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>
{visibleItems.map(item => (
<li key={item} data-cy="item">
{item}
</li>
))}
</ul>
</div>
);
Expand Down
85 changes: 84 additions & 1 deletion src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,84 @@
export const Pagination = () => {};
import React from 'react';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The task requires using React Router to persist ?page=...&perPage=.... React Router hooks (e.g. useSearchParams or useLocation/useNavigate) are not imported here — you should import and use them to read/write query params.


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

export const Pagination: React.FC<Props> = ({
total,
Comment on lines +10 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Initial currentPage and perPage should be derived from the URL query parameters on page load (e.g. parse ?page and ?perPage), not always defaulted to 1 and 5. Add logic to initialize state from the router query params or set them after mount.

perPage,
currentPage = 1,
onPageChange,
}) => {
const totalPages = Math.ceil(total / perPage);

const pages = Array.from({ length: totalPages }, (_, i) => i + 1);

const isFirstPage = currentPage === 1;
const isLastPage = currentPage === totalPages;

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

if (!isFirstPage) {
onPageChange(currentPage - 1);
}
}}
>
«
Comment on lines +39 to +40
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When changing perPage you must also update the URL to reflect ?page=1&perPage=... so the selection is persisted. Currently this only updates component state (setPerPage / setCurrentPage(1)) but does not touch the router search params.

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

if (page !== currentPage) {
onPageChange(page);
}
}}
>
{page}
</a>
Comment on lines +56 to +61
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

When the page changes via the Pagination component you must sync that change into the URL query params (so ?page=...&perPage=... stays current). Either wrap setCurrentPage to also update the query params here or use an effect that synchronizes currentPage/perPage to the router.

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

if (!isLastPage) {
onPageChange(currentPage + 1);
}
}}
>
»
</a>
</li>
</ul>
</>
);
};
19 changes: 15 additions & 4 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The task requires using React Router to persist query params (?page=...&perPage=...) and apply them on page load. This file currently only imports React and useState. Import and use appropriate router hooks (for example, useSearchParams, useNavigate, or useLocation plus useEffect) so you can read params on mount and update them on changes. Also ensure the app is wrapped with a Router (e.g., BrowserRouter) at the root.

"extends": "@mate-academy/students-ts-config",
"include": [
"src"
]
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "Bundler",
"allowImportingTsExtensions": false,
"resolveJsonModule": true,
"isolatedModules": true,
Comment on lines +10 to +11
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Initial currentPage and perPage are hardcoded. According to the requirements, you must read page and perPage from the URL query params (if present) on load and use them to initialize state. Consider parsing search params in an effect or deriving initial values from useSearchParams.

"noEmit": true,
"jsx": "react-jsx",
"strict": true
},
"include": ["src"]
}
Loading