From 344d576dffc4bc9c3e24c97fe7652440bfaa3324 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 19 Apr 2026 18:22:07 +0300 Subject: [PATCH 1/3] Solution --- README.md | 4 +- package-lock.json | 9 +- package.json | 2 +- src/App.tsx | 131 +++++------------- src/components/ItemList/ItemList.tsx | 15 ++ src/components/PageInfo.tsx | 20 +++ src/components/Pagination/Pagination.tsx | 77 +++++++++- .../PerPageSelector/PerPageSelector.tsx | 32 +++++ 8 files changed, 188 insertions(+), 102 deletions(-) create mode 100644 src/components/ItemList/ItemList.tsx create mode 100644 src/components/PageInfo.tsx create mode 100644 src/components/PerPageSelector/PerPageSelector.tsx diff --git a/README.md b/README.md index be8a8c6b6..9e54e8b9a 100644 --- a/README.md +++ b/README.md @@ -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: @@ -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 `` with your Github username in the [DEMO LINK](https://.github.io/react_pagination/) and add it to the PR description. +- Replace `` with your Github username in the [DEMO LINK](https://Anvrey.github.io/react_pagination/) and add it to the PR description. diff --git a/package-lock.json b/package-lock.json index bc04a7602..7fed12e13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ }, "devDependencies": { "@cypress/react18": "^2.0.1", - "@mate-academy/scripts": "^1.9.12", + "@mate-academy/scripts": "^2.1.3", "@mate-academy/students-ts-config": "*", "@mate-academy/stylelint-config": "*", "@types/node": "^20.14.10", @@ -1183,10 +1183,11 @@ } }, "node_modules/@mate-academy/scripts": { - "version": "1.9.12", - "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-1.9.12.tgz", - "integrity": "sha512-/OcmxMa34lYLFlGx7Ig926W1U1qjrnXbjFJ2TzUcDaLmED+A5se652NcWwGOidXRuMAOYLPU2jNYBEkKyXrFJA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@mate-academy/scripts/-/scripts-2.1.3.tgz", + "integrity": "sha512-a07wHTj/1QUK2Aac5zHad+sGw4rIvcNl5lJmJpAD7OxeSbnCdyI6RXUHwXhjF5MaVo9YHrJ0xVahyERS2IIyBQ==", "dev": true, + "license": "MIT", "dependencies": { "@octokit/rest": "^17.11.2", "@types/get-port": "^4.2.0", diff --git a/package.json b/package.json index f412fefe6..617386d19 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ }, "devDependencies": { "@cypress/react18": "^2.0.1", - "@mate-academy/scripts": "^1.9.12", + "@mate-academy/scripts": "^2.1.3", "@mate-academy/students-ts-config": "*", "@mate-academy/stylelint-config": "*", "@types/node": "^20.14.10", diff --git a/src/App.tsx b/src/App.tsx index 189a990c8..9cad64ea8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,110 +1,53 @@ -import React from 'react'; +import React, { useState } from 'react'; import './App.css'; import { getNumbers } from './utils'; +import { Pagination } from './components/Pagination'; +import { ItemList } from './components/ItemList/ItemList'; +import { PageInfo } from './components/PageInfo'; +import { PerPageSelector } from './components/PerPageSelector/PerPageSelector'; // 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(1); + const [perPage, setPerPage] = useState(5); + const startIndex = (currentPage - 1) * perPage; + const lastIndex = Math.min(startIndex + perPage, items.length); + const visibleItems = items.slice(startIndex, lastIndex); + + const handlePageChange = (page: number) => { + setCurrentPage(page); + }; + + const handlePerPageChange = (pages: number) => { + setPerPage(pages); + setCurrentPage(1); + }; + return (

Items with Pagination

-

- Page 1 (items 1 - 5 of 42) -

- -
-
- -
+ - -
+ - {/* Move this markup to Pagination */} - -
    -
  • Item 1
  • -
  • Item 2
  • -
  • Item 3
  • -
  • Item 4
  • -
  • Item 5
  • -
+ +
); }; diff --git a/src/components/ItemList/ItemList.tsx b/src/components/ItemList/ItemList.tsx new file mode 100644 index 000000000..1509f3695 --- /dev/null +++ b/src/components/ItemList/ItemList.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +interface VisiblevisibleItems { + visibleItems: string[]; +} +export const ItemList: React.FC = ({ visibleItems }) => { + return ( +
    + {visibleItems.map(item => ( +
  • + {item} +
  • + ))} +
+ ); +}; diff --git a/src/components/PageInfo.tsx b/src/components/PageInfo.tsx new file mode 100644 index 000000000..1f51effd5 --- /dev/null +++ b/src/components/PageInfo.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +interface PageInfoProps { + currentPage: number; + startIndex: number; + lastIndex: number; + total: number; +} +export const PageInfo: React.FC = ({ + currentPage, + startIndex, + lastIndex, + total, +}) => { + return ( +

+ Page {currentPage} (items {startIndex + 1} - {lastIndex} of {total}) +

+ ); +}; diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index e417a09fc..a8b0ad425 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -1 +1,76 @@ -export const Pagination = () => {}; +import React from 'react'; +import cn from 'classnames'; +interface PaginationProps { + total: number; + perPage: number; + currentPage?: number; + onPageChange: (page: number) => void; +} +export const Pagination: React.FC = ({ + total, + perPage, + currentPage = 1, + onPageChange, +}) => { + const totalPages = Math.ceil(total / perPage); + const pages = []; + + for (let i = 1; i <= totalPages; i++) { + pages.push(i); + } + + return ( + + ); +}; diff --git a/src/components/PerPageSelector/PerPageSelector.tsx b/src/components/PerPageSelector/PerPageSelector.tsx new file mode 100644 index 000000000..0c4ac85a7 --- /dev/null +++ b/src/components/PerPageSelector/PerPageSelector.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +interface PerPageSelectorProps { + perPage: number; + onPerPageChange: (perPage: number) => void; +} +export const PerPageSelector: React.FC = ({ + perPage, + onPerPageChange, +}) => { + return ( +
+
+ +
+ + +
+ ); +}; From 2f58c5439703e16534b2ecd871cd52223a15c7b4 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 19 Apr 2026 19:00:59 +0300 Subject: [PATCH 2/3] Solution1 --- src/components/Pagination/Pagination.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index a8b0ad425..a3d2bcb14 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -48,7 +48,9 @@ export const Pagination: React.FC = ({ href={`#${page}`} onClick={e => { e.preventDefault(); - onPageChange(page); + if (page !== currentPage) { + onPageChange(page); + } }} > {page} From 866f7191ebe158b22ad4c368588c5226b614adf6 Mon Sep 17 00:00:00 2001 From: Andrey Date: Sun, 19 Apr 2026 19:18:40 +0300 Subject: [PATCH 3/3] Solution2 --- src/components/Pagination/Pagination.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index a3d2bcb14..1661d1e7d 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -20,7 +20,7 @@ export const Pagination: React.FC = ({ } return ( -
    +