-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Solution #1480
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Solution #1480
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,25 +1,45 @@ | ||
| 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}`); | ||
|
|
||
| export const App: React.FC = () => { | ||
| const [currentPage, setCurrentPage] = useState<number>(1); | ||
| const [perPage, setPerPage] = useState<number>(5); | ||
|
Comment on lines
+10
to
+11
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
| ); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,84 @@ | ||
| export const Pagination = () => {}; | ||
| import React from 'react'; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The task requires using React Router to persist |
||
|
|
||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initial |
||
| 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When changing |
||
| </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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When the page changes via the |
||
| </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> | ||
| </> | ||
| ); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,17 @@ | ||
| { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, |
||
| "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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Initial |
||
| "noEmit": true, | ||
| "jsx": "react-jsx", | ||
| "strict": true | ||
| }, | ||
| "include": ["src"] | ||
| } | ||
There was a problem hiding this comment.
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.