From a5721878716088dff33652b9a573890c5c7d7c45 Mon Sep 17 00:00:00 2001 From: naviailpach Date: Fri, 17 Apr 2026 16:13:11 +0300 Subject: [PATCH 1/3] add task solution --- README.md | 26 ++--- src/App.tsx | 127 ++++++++++------------- src/components/Pagination/Pagination.tsx | 94 ++++++++++++++++- src/index.tsx | 7 +- 4 files changed, 166 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index be8a8c6b6..568d448d8 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,29 @@ > 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: - ```jsx harmony - { ... }} - /> - ``` + ```jsx harmony + { ... }} + /> + ``` 1. Keep the HTML stucture `data-cy` attributes; 1. Show all the existing pages considering `total` and `perPage` 1. Current page should be highlighted with `li.active`; 1. `onPageChange` callback should be triggered only if page was changed; 1. The `App` should listen to the `onPageChange` and save a new page; 1. `«` and `»` links should open the prev and the next pages accordingly - - disable each of them if it is already the first or the last page (use `li.disabled` and `a[aria-disabled="true"]`) + - disable each of them if it is already the first or the last page (use `li.disabled` and `a[aria-disabled="true"]`) 1. Show the pagination info inside `data-cy="info"` in the next format `Page 1 (items 1 - 5 of 42)`; 1. Implement the ` + className="form-control" + value={perPage} + onChange={handleSelectChange} + > @@ -32,78 +72,19 @@ export const App: React.FC = () => { - {/* Move this markup to Pagination */} - + +
    -
  • Item 1
  • -
  • Item 2
  • -
  • Item 3
  • -
  • Item 4
  • -
  • Item 5
  • + {visibleItems.map(item => ( +
  • + {item} +
  • + ))}
); diff --git a/src/components/Pagination/Pagination.tsx b/src/components/Pagination/Pagination.tsx index e417a09fc..55af6192b 100644 --- a/src/components/Pagination/Pagination.tsx +++ b/src/components/Pagination/Pagination.tsx @@ -1 +1,93 @@ -export const Pagination = () => {}; +import cn from 'classnames'; + +import React from 'react'; + +interface Props { + total: number; + perPage: number; + currentPage?: number; + onPageChange: (page: number) => void; +} + +export const Pagination: React.FC = ({ + total, + perPage, + currentPage = 1, + onPageChange, +}) => { + const totalPages = Math.max(1, Math.ceil(total / perPage)); + + const pages = Array.from({ length: totalPages }, (_, i) => i + 1); + + return ( + + ); +}; diff --git a/src/index.tsx b/src/index.tsx index 226f3f4bd..e928d76a6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,10 @@ +import { BrowserRouter } from 'react-router-dom'; import { createRoot } from 'react-dom/client'; import { App } from './App'; -createRoot(document.getElementById('root') as HTMLElement).render(); +createRoot(document.getElementById('root') as HTMLElement).render( + + + , +); From 1c04e68cf54767dd27e5468434fb9961eb0a1101 Mon Sep 17 00:00:00 2001 From: naviailpach Date: Fri, 17 Apr 2026 16:31:05 +0300 Subject: [PATCH 2/3] add task solution --- src/App.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/App.tsx b/src/App.tsx index 06f812691..5ea572346 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -15,7 +15,7 @@ export const App: React.FC = () => { const perPage = Number(searchParams.get('perPage')) || 5; const totalPages = Math.max(1, Math.ceil(total / perPage)); - const safePage = Math.min(currentPage, totalPages); + const safePage = Math.max(1, Math.min(currentPage, totalPages)); const handlePageChange = (page: number) => { if (page !== safePage) { From af0501f0c30e00990cea99ef130fabae97f73470 Mon Sep 17 00:00:00 2001 From: naviailpach Date: Sat, 18 Apr 2026 22:40:32 +0300 Subject: [PATCH 3/3] solution --- package-lock.json | 70 +++++++++++++++++++++++++++++++++++++++++++---- package.json | 3 +- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index bc04a7602..4945a9803 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,12 @@ "classnames": "^2.5.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router-dom": "^7.14.1", "react-transition-group": "^4.4.5" }, "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 +1184,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", @@ -3256,7 +3258,8 @@ "node_modules/classnames": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" }, "node_modules/clean-stack": { "version": "2.2.0", @@ -3403,6 +3406,19 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -8734,6 +8750,44 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz", + "integrity": "sha512-5BCvFskyAAVumqhEKh/iPhLOIkfxcEUz8WqFIARCkMg8hZZzDYX9CtwxXA0e+qT8zAxmMC0x3Ckb9iMONwc5jg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.14.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.1.tgz", + "integrity": "sha512-ZkrQuwwhGibjQLqH1eCdyiZyLWglPxzxdl5tgwgKEyCSGC76vmAjleGocRe3J/MLfzMUIKwaFJWpFVJhK3d2xA==", + "license": "MIT", + "dependencies": { + "react-router": "7.14.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -9232,6 +9286,12 @@ "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "dev": true }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", diff --git a/package.json b/package.json index f412fefe6..43d2f9d9a 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,12 @@ "classnames": "^2.5.1", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-router-dom": "^7.14.1", "react-transition-group": "^4.4.5" }, "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",