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
123 changes: 50 additions & 73 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,57 @@
import React from 'react';
import './App.css';
import { getNumbers } from './utils';
import { Pagination } from './components/Pagination';
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 App component uses search params to manage page and perPage, but the task requires a stateless Pagination component that accepts props: total, perPage, currentPage (optional), and onPageChange. Here Pagination's props are different (pages, page, updateParams, handlePgeChange, lastPage, currentPage). This does not match the required API from the description — update the Pagination to accept the specified props (total, perPage, currentPage, onPageChange) or adjust the App to pass exactly those props as described in the task. (See checklist Core Functional Requirements.)

import { useSearchParams } from 'react-router-dom';

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const items = getNumbers(1, 42).map(n => `Item ${n}`);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Typo in function name. If keeping a separate handler, rename to handlePageChange, but ideally just use onPageChange directly to match the required API.

export const App: React.FC = () => {
const [searchParams, setSearchParams] = useSearchParams({
Comment on lines +4 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.

Props type includes unnecessary fields. Required API: { total: number; perPage: number; currentPage?: number; onPageChange: (page: number) => void }. Remove page, handlePgeChange, lastPage, and currentPage from props.

perPage: '5',
page: '1',
});

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

const startIndex = (currentPage - 1) * Number(perPage);
const endIndex = Math.min(Number(perPage) + startIndex, items.length);

const updateParams = (key: string, value: string) => {
const params = new URLSearchParams(searchParams);

params.set(key, value);
setSearchParams(params);
};

return (
<div className="container">
<h1>Items with Pagination</h1>
Comment on lines 31 to 32
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In Pagination mapping, each page

  • uses value={page} (line 47) and the component prop page is a string. The value attribute on li isn't required and it's incorrect to assign the whole current page string to each list item. Remove value={page} or replace with a meaningful attribute (not required). This is unnecessary and may confuse logic.


  • <p className="lead" data-cy="info">
    Page 1 (items 1 - 5 of 42)
    Page {page} (items {startIndex + 1} - {endIndex} of {items.length})
    Comment on lines 34 to +35
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    In App, items variable is defined and used; that's fine. However, the info paragraph prints Page {page} where page is a string from searchParams. The required format is Page 1 (items 1 - 5 of 42) — using a number would be more consistent. Convert to number for display or ensure string is acceptable. Also ensure items range is accurate when perPage changes (you already reset page to 1 on perPage change — good).

    </p>
    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 value={page} attribute on <li> is unnecessary. Rely on currentPage === pageNumber for the active state comparison on line 53.


    <div className="form-group row">
    <div className="col-3 col-sm-2 col-xl-1">
    {/* важливо! оновленя двох параметрів */}
    <select
    data-cy="perPageSelector"
    id="perPageSelector"
    Comment on lines 38 to 43
    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 currently updates the URL via updateParams inside page links (updateParams('page', String(pageNumber)) onClick). Per task, the Pagination should be stateless and only call onPageChange. The App should listen and save new page (possibly into URL). Move URL updates to App (i.e., call onPageChange and let App call updateParams).

    className="form-control">
    className="form-control"
    value={perPage}
    onChange={e => {
    const params = new URLSearchParams();
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Remove the value={page} attribute from the <li> element. The value prop is not valid for <li> elements in React, and the active state is already correctly handled via the className conditional on the next line.


    params.set('perPage', e.target.value);
    Comment on lines +47 to +49
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Use onPageChange(pageNumber) directly instead of a separate handlePgeChange function. Only call if pageNumber !== currentPage to avoid unnecessary callbacks.

    params.set('page', '1');

    setSearchParams(params);
    }}
    Comment on lines +46 to +53
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    move to separate fn

    >
    <option value="3">3</option>
    <option value="5">5</option>
    <option value="10">10</option>
    Expand All @@ -32,78 +64,23 @@ 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={items.length}
    perPage={Number(perPage)}
    onPageChange={newPage => {
    updateParams('page', String(newPage));
    }}
    currentPage={currentPage}
    Comment on lines +67 to +73
    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 should only receive the 4 required props: total, perPage, currentPage, and onPageChange. Remove page, handlePgeChange, lastPage, and currentPage from the JSX.

    />
    Comment on lines +67 to +74
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    App's Pagination usage (lines 83-90) passes pages and page string etc. The task requires using pattern. You should refactor App to compute total and pass perPage (number) and currentPage (number) and handle onPageChange callback that updates the URL params. Right now App uses search params directly and exposes updateParams to child — invert that responsibility so Pagination remains stateless and unaware of URL logic.


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

    type Props = {
    total: number;
    perPage: number;
    onPageChange: (page: number) => void;
    currentPage: number;
    Comment on lines +4 to +8
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Props type still contains incorrect props not in the required API: page, handlePgeChange, lastPage. The required interface is only { total: number; perPage: number; currentPage?: number; onPageChange: (page: number) => void }. Remove all non-standard props.

    };

    export const Pagination: React.FC<Props> = ({
    Comment on lines +4 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.

    The Pagination component's props and behavior don't match the task description. The description requires a stateless Pagination that accepts props: total, perPage, currentPage (optional default 1), and onPageChange (callback). In this file the component expects: pages, page (string), updateParams, handlePgeChange, lastPage, and currentPage. This diverges from the required API and shifts responsibility for computing pages and updating URL into Pagination rather than App. Please rework the component signature to match the required props: total: number, perPage: number, currentPage?: number, onPageChange: (page:number) => void and remove URL/updateParams logic from Pagination.

    total,
    perPage,
    onPageChange,
    currentPage,
    Comment on lines +11 to +15
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Destructuring still includes non-standard props. Only destructure total, perPage, currentPage, and onPageChange. The component should be stateless and only call onPageChange when the page changes.

    }) => {
    Comment on lines +11 to +16
    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 component currently receives page as a string and currentPage as a number in props. This duplication is confusing and error prone. With the required API use a single numeric currentPage prop (default 1) and remove page string prop.

    const totalPage = Math.ceil(total / perPage);
    const pages = Array.from({ length: totalPage }, (__dirname, i) => i + 1);
    const lastPage = currentPage === totalPage;

    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={e => {
    e.preventDefault();
    if (currentPage > 1) {
    onPageChange(currentPage - 1);
    }
    }}
    Comment on lines +37 to +43
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    move to separate fn

    >
    «
    </a>
    Comment on lines +32 to +46
    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 prev and next links currently call handlePgeChange (note typo) for navigation but App already provides handlePgeChange name. However the overall design still couples URL updates into Pagination via updateParams earlier. After aligning props to the required API, ensure prev/next call onPageChange(currentPage - 1) and onPageChange(currentPage + 1) only when changes occur, and ensure li.disabled and a[aria-disabled="true"] are applied when first/last page respectively.

    </li>

    {pages.map(pageNumber => {
    return (
    <li
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Remove this value={page} attribute - <li> elements don't need a value prop. Use currentPage === pageNumber to determine active state instead.

    Comment on lines +50 to +51
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Missing data-cy="item" attribute on page number <li> elements. The tests expect data-cy="item" on the <li> tags that represent page numbers, not just on the <a> tags.

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Remove value={page} from <li> element. The value attribute is unnecessary here since active state is determined by Number(page) === pageNumber in className. This may cause unexpected behavior.

    className={classNames('page-item', {
    active: currentPage === pageNumber,
    })}
    key={pageNumber}
    Comment on lines +51 to +55
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    li elements for individual pages set value={page} where page is a string prop for the current page. That is incorrect: the value attribute on each page li should not be the current page string. If you need to mark the list item you should not use value, and ensure li.active is set based on the currentPage prop (number). Also the comparison uses Number(page) === pageNumber which relies on a string prop; with the corrected API it should compare currentPage === pageNumber.

    >
    <a
    data-cy="pageLink"
    className="page-link"
    href={`#${pageNumber}`}
    onClick={e => {
    e.preventDefault();
    onPageChange(pageNumber);
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Use only onPageChange(pageNumber) here. No other callback prop is needed.

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    move to separate fn

    }}
    Comment on lines +57 to +65
    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 component uses updateParams('page', String(pageNumber)) inside page link clicks and aria-disabled with string 'true'/'false'. The Pagination should be stateless and not manipulate URL params directly; instead it must call onPageChange(pageNumber) only when the page actually changes. Move URL/searchParams handling into App and ensure onPageChange is invoked only when page differs from current value.

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

    if (!lastPage) {
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Fix the callback parameter name typo newPAge to newPage for consistency.

    onPageChange(currentPage + 1);
    }
    }}
    Comment on lines +82 to +88
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    move to separate fn

    >
    »
    </a>
    Comment on lines +82 to +91
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Pass only the required props: total, perPage, currentPage, and onPageChange. Remove page, handlePgeChange, and lastPage.

    </li>
    </ul>
    );
    };

    export default Pagination;
    8 changes: 7 additions & 1 deletion src/index.tsx
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,11 @@
    import { createRoot } from 'react-dom/client';
    import React from 'react';

    import { App } from './App';
    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 App component uses prop names and prop types for Pagination that do not match the task description. The task required a stateless API. Here the Pagination is used with props pages, page, updateParams, handlePgeChange, lastPage, currentPage. This means the implementation does not follow the required component API from the description and checklist. Please adapt to accept total, perPage, optional currentPage (default 1), and onPageChange callback instead of the current custom props.

    import { HashRouter } from 'react-router-dom';

    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Remove this prop - it's not part of the required API. The component should use currentPage prop instead.

    createRoot(document.getElementById('root') as HTMLElement).render(<App />);
    createRoot(document.getElementById('root') as HTMLElement).render(
    <HashRouter>
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Remove this prop - it's not part of the required API. Use onPageChange instead.

    <App />
    Copy link
    Copy Markdown

    Choose a reason for hiding this comment

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

    Remove this prop - it's not part of the required API. Compute disabled state internally as currentPage === totalPage.

    </HashRouter>,
    );
    Comment on lines 4 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.

    Props type includes wrong props. Task requires only: total, perPage, currentPage (optional), and onPageChange. Remove page, handlePgeChange, lastPage, currentPage.

    Loading