Skip to content

Conversation

@pryxnsu
Copy link
Contributor

@pryxnsu pryxnsu commented Jan 21, 2025

Description
Add pagination feature in Blog page

Related issue(s)
Fixes #3570

update.mp4

Related issue(s)

Summary by CodeRabbit

  • New Features
    • Added paginated blog listing with page numbers, prev/next controls, accessible labels, and URL-updating page navigation; includes a reusable pagination hook and page-item component plus next/previous icons.
  • Bug Fixes
    • Filtering now ignores pagination state to avoid unintended interactions.
  • Style
    • Adjusted icon spacing within buttons for consistent alignment.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2025

Walkthrough

Adds a pagination hook and UI components, integrates pagination into the blog index with router query syncing, adds next/previous SVG icons, updates filter to ignore the page query, and tweaks icon spacing in the shared Button component.

Changes

Cohort / File(s) Summary
Pagination hook
components/helpers/usePagination.ts
New generic React hook usePagination<T>(items, itemsPerPage) managing currentPage, maxPage, and memoized currentItems slice.
Pagination UI
components/pagination/Pagination.tsx, components/pagination/PaginationItem.tsx
New Pagination and PaginationItem components rendering prev/next buttons, numeric pages, ellipses, accessibility attrs, and onPageChange handling.
Blog integration
pages/blog/index.tsx
Integrates usePagination (postsPerPage = 9), renders paginated currentItems, syncs page via router query, and displays Pagination when maxPage > 1.
Icons
components/icons/Next.tsx, components/icons/Previous.tsx
New IconNext and IconPrevious SVG components used by pagination controls.
Filter behavior update
components/navigation/Filter.tsx
useEffect now strips page from routeQuery before calling onFilterApply, excluding pagination from filters.
Button spacing tweak
components/buttons/Button.tsx
Removed left/right margin utility classes around icon wrappers; purely spacing/style change.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Blog as BlogIndexPage
  participant Hook as usePagination
  participant Router as Next.js Router
  participant UI as Pagination

  User->>Blog: open /blog?page=n
  Blog->>Hook: init(items, itemsPerPage=9)
  Hook-->>Blog: { currentPage, currentItems, maxPage }
  Blog->>UI: render(currentPage, totalPages=maxPage)

  alt User clicks page/prev/next
    User->>UI: click
    UI-->>Blog: onPageChange(page)
    Blog->>Hook: setCurrentPage(page)
    Blog->>Router: replace({ query: {..., page} })
    Hook-->>Blog: updated currentItems
    Blog-->>User: re-render with new items
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Implement pagination on Blog page with configurable items per page (e.g., 10) [#3570] postsPerPage is hardcoded to 9 in pages/blog/index.tsx; no configuration surface added.
Provide Next and Previous buttons for navigation [#3570]
Allow users to jump to a specific page number [#3570]
Ensure state persists via proper routing/back navigation [#3570]

Out-of-scope changes

Code Change Explanation
Removed margin utility classes around icon wrappers (components/buttons/Button.tsx) Styling tweak in shared Button component; unrelated to pagination objectives and not required by linked issue.

Possibly related PRs

  • fix: remove hydration errors #3262 — Modifies pages/blog/index.tsx (hydration/client rendering); touches same page file and may conflict or overlap with pagination integration.

Suggested reviewers

  • derberg
  • sambhavgupta0705
  • anshgoyalevil
  • devilkiller-ag

Poem

I hop from burrow to bright page light,
Ellipses blink and carrots sit in sight.
Prev and Next, I nibble through,
Nine crumbs shown — I count anew.
Click, I hop; the meadow's right. 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

Welcome to AsyncAPI. Thanks a lot for creating your first pull request. Please check out our contributors guide useful for opening a pull request.
Keep in mind there are also other channels you can use to interact with AsyncAPI community. For more details check out this issue.

@netlify
Copy link

netlify bot commented Jan 21, 2025

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit f9bd78e
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/694a52b2cb939c00086e111a
😎 Deploy Preview https://deploy-preview-3595--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@asyncapi-bot
Copy link
Contributor

Hello, @coderabbitai[bot]! 👋🏼

    I'm 🧞🧞🧞 Genie 🧞🧞🧞 from the magic lamp. Looks like somebody needs a hand!

    At the moment the following comments are supported in pull requests:

    - `/please-take-a-look` or `/ptal` - This comment will add a comment to the PR asking for attention from the reviewrs who have not reviewed the PR yet.
    - `/ready-to-merge` or `/rtm` - This comment will trigger automerge of PR in case all required checks are green, approvals in place and do-not-merge label is not added
    - `/do-not-merge` or `/dnm` - This comment will block automerging even if all conditions are met and ready-to-merge label is added
    - `/autoupdate` or `/au` - This comment will add `autoupdate` label to the PR and keeps your PR up-to-date to the target branch's future changes. Unless there is a merge conflict or it is a draft PR. (Currently only works for upstream branches.)
    - `/update` or `/u` - This comment will update the PR with the latest changes from the target branch. Unless there is a merge conflict or it is a draft PR. NOTE: this only updates the PR once, so if you need to update again, you need to call the command again.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 21, 2025

@asyncapi-bot i'll start reviewing the changes for the pagination feature implementation.

🔗 analysis chain

🏁 scripts executed

the following scripts were executed for the analysis:

script:

#!/bin/bash
# get the pr diff to understand the changes
git diff --name-status head^..head

# get the actual diff
git diff head^..head

length of output: 9907

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov
Copy link

codecov bot commented Jan 21, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (65c10bb) to head (f9bd78e).

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #3595   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           22        22           
  Lines          798       798           
  Branches       146       146           
=========================================
  Hits           798       798           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Jan 21, 2025

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 38
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-3595--asyncapi-website.netlify.app/

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (6)
components/helpers/applyFilter.ts (1)

140-142: Consider filtering out pagination params before processing.

While the current implementation works correctly by skipping the 'page' parameter, consider a more maintainable approach by filtering out pagination-related parameters before processing.

Here's a suggested improvement:

export const onFilterApply = (
  inputData: DataObject[],
  onFilter: (result: DataObject[], query: Filter) => void,
  query: Filter
): void => {
  let result = inputData;

  if (query && Object.keys(query).length >= 1) {
-   Object.keys(query).forEach((property) => {
-     if (property === 'page') {
-       return;
-     }
+   // Filter out pagination params
+   const filterParams = Object.keys(query).filter(key => key !== 'page');
+   filterParams.forEach((property) => {
      const res = result.filter((e) => {
        // ... rest of the filtering logic
      });

      result = res;
    });
  }
  onFilter(result, query);
};

This approach:

  1. Makes the intention clearer by explicitly filtering out pagination params
  2. Avoids using early returns within forEach
  3. Is more extensible if more pagination-related params need to be excluded in the future
components/pagination/PaginationItem.tsx (1)

4-5: Remove unnecessary eslint-disable comment.

The empty eslint-disable comment can be removed as it serves no purpose.

-  // eslint-disable-next-line prettier/prettier
components/helpers/usePagination.ts (1)

14-30: Add input validation for edge cases.

While the implementation is solid, consider adding validation for:

  • Negative or zero itemsPerPage
  • Empty items array
 export function usePagination<T>(items: T[], itemsPerPage: number) {
+  if (itemsPerPage <= 0) {
+    throw new Error('itemsPerPage must be greater than 0');
+  }
+
   const [currentPage, setCurrentPage] = useState(1);
-  const maxPage = Math.ceil(items.length / itemsPerPage);
+  const maxPage = items.length ? Math.ceil(items.length / itemsPerPage) : 1;
components/pagination/Pagination.tsx (2)

6-7: Remove unnecessary eslint-disable comment.

The empty eslint-disable comment can be removed as it serves no purpose.

-  // eslint-disable-next-line prettier/prettier

55-75: Enhance accessibility and reduce CSS duplication.

  1. Add aria-labels to SVG elements for better accessibility
  2. Extract duplicate button styles to a common class
+const commonButtonStyles = `
+  font-normal flex h-[34px] items-center justify-center gap-2 rounded bg-white px-4
+  py-[7px] text-sm leading-[17px] tracking-[-0.01em]
+`;

 {/* Previous button */}
 <button
   onClick={() => handlePageChange(currentPage - 1)}
   disabled={currentPage === 1}
-  className={`
-    font-normal flex h-[34px] items-center justify-center gap-2 rounded bg-white px-4
-    py-[7px] text-sm leading-[17px] tracking-[-0.01em]
+  className={`${commonButtonStyles}
     ${currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'}
   `}
+  aria-label="Previous page"
 >
   <svg
     width='20'
     height='20'
     viewBox='0 0 24 24'
     fill='none'
     xmlns='http://www.w3.org/2000/svg'
     className='stroke-current'
+    aria-hidden="true"
   >

 {/* Next button */}
 <button
   onClick={() => handlePageChange(currentPage + 1)}
   disabled={currentPage === totalPages}
-  className={`
-    font-normal flex h-[34px] items-center justify-center gap-2 rounded bg-white px-4
-    py-[7px] text-sm leading-[17px] tracking-[-0.01em]
+  className={`${commonButtonStyles}
     ${currentPage === totalPages ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'}
   `}
+  aria-label="Next page"
 >

Also applies to: 99-119

pages/blog/index.tsx (1)

43-56: Add scroll to top behavior on page change.

When navigating between pages, consider scrolling to the top for better user experience.

 const handlePageChange = (page: number) => {
   setCurrentPage(page);

   const currentFilters = { ...router.query, page: page.toString() };

   router.push(
     {
       pathname: router.pathname,
       query: currentFilters
     },
     undefined,
     { shallow: true }
-  );
+  ).then(() => {
+    window.scrollTo({ top: 0, behavior: 'smooth' });
+  });
 };
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cf3ca64 and af0e6a1.

📒 Files selected for processing (5)
  • components/helpers/applyFilter.ts (1 hunks)
  • components/helpers/usePagination.ts (1 hunks)
  • components/pagination/Pagination.tsx (1 hunks)
  • components/pagination/PaginationItem.tsx (1 hunks)
  • pages/blog/index.tsx (3 hunks)
🔇 Additional comments (3)
components/pagination/PaginationItem.tsx (1)

19-31: Great implementation with accessibility in mind!

The component is well-structured with:

  • Clear prop types and documentation
  • Proper accessibility attributes (aria-current)
  • Semantic button element with appropriate styling
components/pagination/Pagination.tsx (1)

21-122: Well-implemented pagination logic!

The component demonstrates:

  • Smart handling of different page number scenarios
  • Clean separation of concerns
  • Proper validation in handlePageChange
pages/blog/index.tsx (1)

154-168: Well-integrated pagination system!

The pagination integration demonstrates:

  • Clean integration with the existing blog page
  • Proper state management
  • Good user experience with loading states

Comment on lines 58 to 64
useEffect(() => {
const pageFromQuery = parseInt(router.query.page as string, 10);

if (!Number.isNaN(pageFromQuery) && pageFromQuery >= 1 && pageFromQuery !== currentPage) {
setCurrentPage(pageFromQuery);
}
}, [router.query.page]);
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 21, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for invalid page numbers.

When an invalid page number is provided in the URL, the code should handle it gracefully.

 useEffect(() => {
   const pageFromQuery = parseInt(router.query.page as string, 10);

-  if (!Number.isNaN(pageFromQuery) && pageFromQuery >= 1 && pageFromQuery !== currentPage) {
+  if (!Number.isNaN(pageFromQuery)) {
+    if (pageFromQuery >= 1 && pageFromQuery <= maxPage && pageFromQuery !== currentPage) {
       setCurrentPage(pageFromQuery);
+    } else {
+      // Reset to page 1 for invalid page numbers
+      handlePageChange(1);
+    }
   }
-  }, [router.query.page]);
+  }, [router.query.page, maxPage]);

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the suggestion to add error handling for invalid page numbers. I implemented the proposed code, but I am encountering an issue where navigating to any page (e.g., ?page=2) redirects me back to ?page=1.

It seems like the handlePageChange(1) function is being triggered unnecessarily, even when the pageFromQuery is valid.

Copy link
Contributor

Choose a reason for hiding this comment

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

@priyanshuxkumar I understand the issue. The problem occurs because we're validating against maxPage before it's properly initialized. Let's modify the code to ensure we only validate when we have a valid maxPage:

useEffect(() => {
  const pageFromQuery = parseInt(router.query.page as string, 10);

  if (!Number.isNaN(pageFromQuery) && maxPage > 0) {
    if (pageFromQuery >= 1 && pageFromQuery <= maxPage && pageFromQuery !== currentPage) {
      setCurrentPage(pageFromQuery);
    } else if (pageFromQuery < 1 || pageFromQuery > maxPage) {
      // Only reset to page 1 if the page number is actually invalid
      handlePageChange(1);
    }
  }
}, [router.query.page, maxPage, currentPage]);

This should fix the issue by:

  1. Only performing validation when maxPage is properly initialized (maxPage > 0)
  2. Only resetting to page 1 when the page number is actually out of bounds

Let me know if this resolves the issue!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes its resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

@priyanshuxkumar Great to hear that the pagination is working correctly now! 🎉


If you found this review helpful, would you consider giving us a shout-out on X?
Thank you for using CodeRabbit!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
pages/blog/index.tsx (3)

40-41: Consider moving postsPerPage to configuration.

The hardcoded value of 9 posts per page would be better managed as a configuration constant, making it easier to adjust across different viewports or requirements.

+// Add to src/config/blog.ts or similar
+export const POSTS_PER_PAGE = 9;
+
 // In BlogIndexPage
-  const postsPerPage = 9;
+  const postsPerPage = POSTS_PER_PAGE;

164-168: Improve loader condition logic.

The current loader condition might show unnecessarily when currentItems is empty but posts exist. Consider checking both arrays.

-            {Object.keys(currentItems).length > 0 && !isClient && (
+            {Object.keys(posts).length > 0 && !isClient && (

169-173: Enhance pagination container accessibility.

Add appropriate ARIA attributes to improve accessibility for screen readers.

-              <div className='mt-8 w-full'>
+              <div className='mt-8 w-full' role="navigation" aria-label="Blog posts pagination">
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between af0e6a1 and 1873828.

📒 Files selected for processing (1)
  • pages/blog/index.tsx (3 hunks)
⏰ Context from checks skipped due to timeout of 180000ms (1)
  • GitHub Check: Test NodeJS PR - macos-13
🔇 Additional comments (2)
pages/blog/index.tsx (2)

4-4: LGTM! Import statements are well-organized.

The new imports for pagination components follow the existing import structure and use proper path aliases.

Also applies to: 10-10


43-69: Well-implemented page change handling and URL synchronization!

The code effectively:

  • Updates page state
  • Synchronizes URL query parameters
  • Handles invalid page numbers gracefully
  • Uses shallow routing for better performance

Comment on lines 140 to 142
if (property === 'page') {
return;
}
Copy link
Member

Choose a reason for hiding this comment

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

Can't we handle filters in a better approach?

Think in an approach where we look for some specific variables from query params here, not all.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

` const nonFilterableKeys = ['page'];

if (query && Object.keys(query).length >= 1) {
Object.keys(query).forEach((property) => {
if (nonFilterableKeys.includes(property)) {
return; // Skip non-filterable keys like 'page'
}
`

Dynamically handles filters while explicitly excluding non-filterable keys like 'page'

* @returns {T[]} currentItems - Items for the current page
* @returns {number} maxPage - Total number of pages
*/
export function usePagination<T>(items: T[], itemsPerPage: number) {
Copy link
Member

@akshatnema akshatnema Jan 25, 2025

Choose a reason for hiding this comment

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

What is T here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It takes a prop of items(all items) and returns the only item that has to be displayed on the current page

Copy link
Member

Choose a reason for hiding this comment

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

@priyanshuxkumar Update the name of the type. Type name as T is way too generic.

Comment on lines 27 to 50
const getPageNumbers = () => {
const pages = [];

if (totalPages <= 7) {
for (let i = 1; i <= totalPages; i++) pages.push(i);
} else {
pages.push(1);
if (currentPage > 3) {
pages.push('ellipsis1');
}

for (let i = Math.max(2, currentPage - 1); i <= Math.min(totalPages - 1, currentPage + 1); i++) {
pages.push(i);
}

if (currentPage < totalPages - 2) {
pages.push('ellipsis2');
}

pages.push(totalPages);
}

return pages;
};
Copy link
Member

Choose a reason for hiding this comment

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

Can you please explain the else part of this logic? It seems to be very weird.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i have updated this . basically it return the number of pages for display on pagination -
Screenshot from 2025-01-26 10-54-33

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (4)
components/icons/Previous.tsx (1)

9-18: Enhance SVG accessibility

Add appropriate ARIA attributes and descriptive elements for screen readers.

 <svg
   width='20'
   height='20'
   viewBox='0 0 24 24'
   fill='none'
   xmlns='http://www.w3.org/2000/svg'
   className='stroke-current'
+  role="img"
+  aria-label="Previous page"
 >
+  <title>Previous page</title>
   <path d='M15 18L9 12L15 6' strokeWidth='2' strokeLinecap='round' strokeLinejoin='round' />
 </svg>
components/pagination/Pagination.tsx (3)

11-12: Remove unnecessary empty eslint-disable comment

This empty prettier/eslint disable comment can be removed as it serves no purpose.


26-31: Add input validation for props

Add validation to ensure totalPages is positive and currentPage is within valid range.

 export default function Pagination({ totalPages, currentPage, onPageChange }: PaginationProps) {
+  if (totalPages < 1) {
+    throw new Error('totalPages must be positive');
+  }
+  if (currentPage < 1 || currentPage > totalPages) {
+    throw new Error('currentPage must be between 1 and totalPages');
+  }
+
   const handlePageChange = (page: number) => {

36-61: Extract magic numbers into named constants

The pagination logic uses magic numbers (7, 3, 2) which should be extracted into named constants for better maintainability.

+const MAX_VISIBLE_PAGES = 7;
+const BOUNDARY_PAGES = 3;
+const SIBLING_PAGES = 1;
+
 const getPageNumbers = (): (number | string)[] => {
-  if (totalPages <= 7) {
+  if (totalPages <= MAX_VISIBLE_PAGES) {
     return Array.from({ length: totalPages }, (_, i) => i + 1);
   }

   const pages: (number | string)[] = [1];

-  if (currentPage > 3) {
+  if (currentPage > BOUNDARY_PAGES) {
     pages.push('ellipsis1');
   }

-  const start = Math.max(2, currentPage - 1);
-  const end = Math.min(totalPages - 1, currentPage + 1);
+  const start = Math.max(2, currentPage - SIBLING_PAGES);
+  const end = Math.min(totalPages - 1, currentPage + SIBLING_PAGES);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1873828 and 458ca3e.

📒 Files selected for processing (4)
  • components/helpers/applyFilter.ts (1 hunks)
  • components/icons/Next.tsx (1 hunks)
  • components/icons/Previous.tsx (1 hunks)
  • components/pagination/Pagination.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • components/icons/Next.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/helpers/applyFilter.ts

/**
* @description Icons for Previous button in pagination
*/
export default function IconPrevios() {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in component name: IconPreviosIconPrevious

The component name has a typo that should be corrected for clarity and consistency.

-export default function IconPrevios() {
+export default function IconPrevious() {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function IconPrevios() {
export default function IconPrevious() {

currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Previous'
icon={<IconPrevios />}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Update import to use corrected icon name

Once the IconPrevios component is renamed to IconPrevious, update this import reference.

-icon={<IconPrevios />}
+icon={<IconPrevious />}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
icon={<IconPrevios />}
icon={<IconPrevious />}

Comment on lines 63 to 107
return (
<div className='font-inter flex items-center justify-center gap-8'>
{/* Previous button */}
<Button
onClick={() => handlePageChange(currentPage - 1)}
className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Previous'
icon={<IconPrevios />}
iconPosition={ButtonIconPosition.LEFT}
/>

{/* Page numbers */}
<div className='flex gap-2'>
{getPageNumbers().map((page) =>
typeof page === 'number' ? (
<PaginationItem
key={page}
pageNumber={page}
isActive={page === currentPage}
onPageChange={handlePageChange}
/>
) : (
<span
key={page}
className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]'
>
...
</span>
)
)}
</div>

{/* Next button */}
<Button
onClick={() => handlePageChange(currentPage + 1)}
className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
currentPage === totalPages ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Next'
icon={<IconNext />}
/>
</div>
);
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 26, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance accessibility and extract styles

The pagination component needs accessibility improvements and style cleanup:

  1. Add ARIA labels for navigation
  2. Extract complex conditional styles into utility classes or CSS modules
  3. Add keyboard navigation support
 return (
-  <div className='font-inter flex items-center justify-center gap-8'>
+  <nav
+    role="navigation"
+    aria-label="Pagination"
+    className='font-inter flex items-center justify-center gap-8'
+  >
     {/* Previous button */}
     <Button
       onClick={() => handlePageChange(currentPage - 1)}
+      disabled={currentPage === 1}
       className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
         currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
       }`}
       text='Previous'
       icon={<IconPrevios />}
       iconPosition={ButtonIconPosition.LEFT}
+      aria-label="Go to previous page"
     />

     {/* Page numbers */}
-    <div className='flex gap-2'>
+    <div className='flex gap-2' role="list">
       {getPageNumbers().map((page) =>
         typeof page === 'number' ? (
           <PaginationItem
             key={page}
             pageNumber={page}
             isActive={page === currentPage}
             onPageChange={handlePageChange}
+            aria-label={`Go to page ${page}`}
+            aria-current={page === currentPage ? 'page' : undefined}
           />
         ) : (
           <span
             key={page}
             className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]'
+            aria-hidden="true"
           >
             ...
           </span>
         )
       )}
     </div>

     {/* Next button */}
     <Button
       onClick={() => handlePageChange(currentPage + 1)}
+      disabled={currentPage === totalPages}
       className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
         currentPage === totalPages ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
       }`}
       text='Next'
       icon={<IconNext />}
+      aria-label="Go to next page"
     />
-  </div>
+  </nav>
 );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<div className='font-inter flex items-center justify-center gap-8'>
{/* Previous button */}
<Button
onClick={() => handlePageChange(currentPage - 1)}
className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Previous'
icon={<IconPrevios />}
iconPosition={ButtonIconPosition.LEFT}
/>
{/* Page numbers */}
<div className='flex gap-2'>
{getPageNumbers().map((page) =>
typeof page === 'number' ? (
<PaginationItem
key={page}
pageNumber={page}
isActive={page === currentPage}
onPageChange={handlePageChange}
/>
) : (
<span
key={page}
className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]'
>
...
</span>
)
)}
</div>
{/* Next button */}
<Button
onClick={() => handlePageChange(currentPage + 1)}
className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
currentPage === totalPages ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Next'
icon={<IconNext />}
/>
</div>
);
return (
<nav
role="navigation"
aria-label="Pagination"
className='font-inter flex items-center justify-center gap-8'
>
{/* Previous button */}
<Button
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1}
className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
currentPage === 1 ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Previous'
icon={<IconPrevios />}
iconPosition={ButtonIconPosition.LEFT}
aria-label="Go to previous page"
/>
{/* Page numbers */}
<div className='flex gap-2' role="list">
{getPageNumbers().map((page) =>
typeof page === 'number' ? (
<PaginationItem
key={page}
pageNumber={page}
isActive={page === currentPage}
onPageChange={handlePageChange}
aria-label={`Go to page ${page}`}
aria-current={page === currentPage ? 'page' : undefined}
/>
) : (
<span
key={page}
className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]'
aria-hidden="true"
>
...
</span>
)
)}
</div>
{/* Next button */}
<Button
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage === totalPages}
className={`font-normal flex h-[34px] items-center justify-center rounded bg-white px-3 py-[7px] text-sm leading-[17px] tracking-[-0.01em] ${
currentPage === totalPages ? 'cursor-not-allowed text-gray-300' : 'text-[#141717] hover:bg-gray-50'
}`}
text='Next'
icon={<IconNext />}
aria-label="Go to next page"
/>
</nav>
);

Copy link
Member

Choose a reason for hiding this comment

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

@priyanshuxkumar Please add these aria-label for good accessibility in the pages.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@pryxnsu pryxnsu requested a review from akshatnema January 26, 2025 06:16
@pryxnsu
Copy link
Contributor Author

pryxnsu commented Mar 9, 2025

@akshatnema could you please review this and let me know if any further changes are required?

Copy link
Collaborator

@TRohit20 TRohit20 left a comment

Choose a reason for hiding this comment

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

Hi @priyanshuxkumar Kindly resolve the conversations with relevant fixes and suggestions made by CodeRabbit if relevant :)


/** The text to be displayed on the button. */
text: string;
text: string | React.ReactNode;
Copy link
Member

Choose a reason for hiding this comment

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

This prop field is for text string only. Why do you need to add React component here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i fixed it .


if (query && Object.keys(query).length >= 1) {
Object.keys(query).forEach((property) => {
if (nonFilterableKeys.includes(property)) {
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand what is the need of nonFilterable Key field here. If you don't want to add it in filtering, simply don't pass it in criteria or input data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok , i have updated it
// Filter.tsx
useEffect(() => {
const filterableQuery = Object.fromEntries(Object.entries(routeQuery).filter(([key]) => key !== 'page'));

onFilterApply(data, onFilter, filterableQuery);

}, [routeQuery]);

/**
* @description Icons for Next button
*/
export default function IconNext() {
Copy link
Member

Choose a reason for hiding this comment

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

Why there is a need of having a separate component for these icons? Can't we use these images in SVG format?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can, but components with SVG images already exist. I did the same

@pryxnsu
Copy link
Contributor Author

pryxnsu commented Aug 26, 2025

Hi @sambhavgupta0705 @akshatnema, I’ve fixed the issues and updated the approach. Would appreciate your review. Thanks.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (8)
components/buttons/Button.tsx (1)

78-88: Avoid spacing regression between icon and text by adding conditional margins

Removing the mr-2/ml-2 created zero gap between the icon and label for all existing button usages that render both. To keep pagination’s icon-only buttons tight while preserving spacing elsewhere, add margins only when text is non-empty.

Apply this diff:

-        {icon && iconPosition === ButtonIconPosition.LEFT && (
-          <span className='inline-block' data-testid='Button-icon-left'>
+        {icon && iconPosition === ButtonIconPosition.LEFT && (
+          <span
+            className={text?.trim() ? 'mr-2 inline-block' : 'inline-block'}
+            data-testid='Button-icon-left'
+          >
             {icon}
           </span>
         )}
         <span className='inline-block'>{text}</span>
-        {icon && iconPosition === ButtonIconPosition.RIGHT && (
-          <span className='inline-block' data-testid='Button-icon-right'>
+        {icon && iconPosition === ButtonIconPosition.RIGHT && (
+          <span
+            className={text?.trim() ? 'ml-2 inline-block' : 'inline-block'}
+            data-testid='Button-icon-right'
+          >
             {icon}
           </span>
         )}
@@
-      {icon && iconPosition === ButtonIconPosition.LEFT && <span className='inline-block'>{icon}</span>}
+      {icon && iconPosition === ButtonIconPosition.LEFT && (
+        <span className={text?.trim() ? 'mr-2 inline-block' : 'inline-block'}>{icon}</span>
+      )}
       <span className='inline-block'>{text}</span>
-      {icon && iconPosition === ButtonIconPosition.RIGHT && <span className='inline-block'>{icon}</span>}
+      {icon && iconPosition === ButtonIconPosition.RIGHT && (
+        <span className={text?.trim() ? 'ml-2 inline-block' : 'inline-block'}>{icon}</span>
+      )}

Follow-up:

  • Please sanity-check a few existing buttons in the preview (primary/secondary, with left/right icons) to ensure spacing matches design expectations.

Also applies to: 101-104

components/navigation/Filter.tsx (2)

63-76: Reset pagination when filters change to avoid empty pages

If a user is on page N and applies a filter that reduces the result set, N may become out of range, producing an empty list until they manually go back. Clear the page param when any filter changes so pagination resets to page 1.

Apply this diff:

         onChange={(e) => {
           const { query } = route;
           const newQuery = {
             ...query
           };

           if (e) {
             newQuery[check.name] = e;
           } else {
             // Remove a specific filter upon clicking Select Placeholder option
             delete newQuery[check.name];
           }
+          // Reset pagination whenever filters change to avoid out-of-range pages
+          delete newQuery.page;

           if (newQuery) {
             const queryParams = new URLSearchParams(newQuery as { [key: string]: string }).toString();

Optional: also consider clearing page in bulk filter changes (if any) elsewhere in the app for consistency.


32-35: Tighten the dependency and destructuring for readability

The effect depends on route.query content, not the router instance. Depending on the router object may re-run more often than needed. Also, simple destructuring reads better.

-useEffect(() => {
-  setQuery(route.query);
-  applyFilterList(checks, data, setFilters);
-}, [route]);
+useEffect(() => {
+  setQuery(route.query);
+  applyFilterList(checks, data, setFilters);
+}, [route.query]);

And if you prefer, the filterableQuery can be written as:

-const filterableQuery = Object.fromEntries(Object.entries(routeQuery).filter(([key]) => key !== 'page'));
+const { page: _omit, ...filterableQuery } = routeQuery;
components/pagination/Pagination.tsx (5)

27-31: Avoid no-op state updates by ignoring clicks on the active page

Guard against redundant onPageChange calls that can trigger unnecessary rerenders and router pushes.

-  const handlePageChange = (page: number) => {
-    if (page >= 1 && page <= totalPages) {
-      onPageChange(page);
-    }
-  };
+  const handlePageChange = (page: number) => {
+    if (page !== currentPage && page >= 1 && page <= totalPages) {
+      onPageChange(page);
+    }
+  };

94-115: Role mismatch: container uses role="list" but children are buttons, not listitems

Either use proper list semantics (ul/li) with role="listitem" or drop role="list" here. Given we already have a nav landmark, simplest is to remove the list role.

-      <div className='flex gap-2' role='list'>
+      <div className='flex gap-2'>

102-103: Redundant aria-current prop; PaginationItem sets it internally

PaginationItem already sets aria-current based on isActive. Passing it again is unnecessary and can drift if implementations diverge.

-              aria-current={page === currentPage ? 'page' : undefined}

80-86: Verify custom Tailwind class 'hover:bg-gray-white' or replace with a safe default

'hover:bg-gray-white' is not a standard Tailwind color. If not defined in your Tailwind config, the class will be ignored. Consider using disabled: variants and a concrete color.

Option A (verify exists): keep as-is if configured.

Option B (safer, Tailwind-native):

-            currentPage === 1
-              ? 'hover:bg-gray-white cursor-not-allowed text-gray-300'
-              : 'text-[#141717] hover:bg-gray-50'
+            'disabled:cursor-not-allowed disabled:text-gray-300 text-[#141717] hover:bg-gray-50'

and rely on the disabled prop already set on the Button.

Also applies to: 121-127


70-76: Consider short-circuiting render when totalPages <= 1

This component may be reused elsewhere beyond Blog, where the caller might not guard. Returning null when there’s nothing to paginate avoids extra DOM.

Example:

if (totalPages <= 1) return null;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e1d505a and f79b659.

📒 Files selected for processing (3)
  • components/buttons/Button.tsx (2 hunks)
  • components/navigation/Filter.tsx (1 hunks)
  • components/pagination/Pagination.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
PR: asyncapi/website#3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/pagination/Pagination.tsx
🧬 Code graph analysis (2)
components/navigation/Filter.tsx (1)
components/helpers/applyFilter.ts (1)
  • onFilterApply (131-159)
components/pagination/Pagination.tsx (4)
components/buttons/Button.tsx (1)
  • Button (53-106)
components/icons/Previous.tsx (1)
  • IconPrevious (7-20)
components/pagination/PaginationItem.tsx (1)
  • PaginationItem (19-32)
components/icons/Next.tsx (1)
  • IconNext (7-20)
🔇 Additional comments (1)
components/navigation/Filter.tsx (1)

38-41: Good call excluding the page query from filtering

Filtering with routeQuery minus page prevents page changes from retriggering or narrowing results unintentionally. This aligns well with pagination UX.

@sambhavgupta0705
Copy link
Member

@priyanshuxkumar please look into codeRabbitAI suggestions

@pryxnsu
Copy link
Contributor Author

pryxnsu commented Sep 7, 2025

@priyanshuxkumar please look into codeRabbitAI suggestions

i have made the changes. please have a look

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
components/pagination/Pagination.tsx (5)

78-100: Use semantic list markup instead of role='list'.

Prefer <ul><li> for pagination lists. This improves a11y and reduces ARIA misuse.

-      {/* Page numbers */}
-      <div className='flex gap-2' role='list'>
+      {/* Page numbers */}
+      <ul className='flex gap-2'>
         {getPageNumber().map((page) =>
           typeof page === 'number' ? (
-            <PaginationItem
-              key={page}
-              pageNumber={page}
-              isActive={page === currentPage}
-              onPageChange={handlePageChange}
-              aria-label={`Go to page ${page}`}
-              aria-current={page === currentPage ? 'page' : undefined}
-            />
+            <li key={page} className='list-none'>
+              <PaginationItem
+                pageNumber={page}
+                isActive={page === currentPage}
+                onPageChange={handlePageChange}
+              />
+            </li>
           ) : (
-            <span
-              key={page}
-              className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]'
-              aria-hidden='true'
-            >
-              ...
-            </span>
+            <li key={page} className='list-none' aria-hidden='true'>
+              <span className='font-inter flex size-10 items-center justify-center text-sm font-semibold text-[#6B6B6B]'>
+                ...
+              </span>
+            </li>
           )
         )}
-      </div>
+      </ul>

65-71: Remove non-existent Tailwind class 'hover:bg-gray-white'.

Tailwind doesn’t ship bg-gray-white. Drop it to avoid dead classes.

-              ? 'hover:bg-gray-white cursor-not-allowed text-gray-300'
+              ? 'cursor-not-allowed text-gray-300'

Apply in both Prev and Next button conditionals.

Also applies to: 106-112


56-59: Redundant role on

.

<nav> already implies the navigation role. Keep only aria-label.

-    <nav
-      role='navigation'
-      aria-label='Pagination'
-      className='font-inter flex min-w-[326px] items-center justify-center md:gap-8'
-    >
+    <nav aria-label='Pagination' className='font-inter flex min-w-[326px] items-center justify-center md:gap-8'>

11-12: Remove stray prettier disable comment.

This no-op directive adds noise.

-  // eslint-disable-next-line prettier/prettier
-

73-75: Mark inline SVG icons as decorative.

Since buttons have aria-label, hide SVGs from AT to prevent duplicate announcements.

Update components/icons/Previous.tsx and components/icons/Next.tsx:

-    <svg
+    <svg
+      aria-hidden='true'
+      focusable='false'
       width='20'
       height='20'
       viewBox='0 0 24 24'
       fill='none'
       xmlns='http://www.w3.org/2000/svg'
       className='stroke-current'

Also applies to: 114-115

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f79b659 and 90b093b.

📒 Files selected for processing (1)
  • components/pagination/Pagination.tsx (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2024-10-11T10:46:58.882Z
Learnt from: akshatnema
PR: asyncapi/website#3262
File: components/Avatar.tsx:35-44
Timestamp: 2024-10-11T10:46:58.882Z
Learning: In Next.js, when avoiding nested `<a>` tags due to hydration issues, use accessible non-link elements like `<button>` or `<span>` with appropriate roles, `tabIndex`, and event handlers to maintain accessibility and SEO.

Applied to files:

  • components/pagination/Pagination.tsx
🧬 Code graph analysis (1)
components/pagination/Pagination.tsx (4)
components/buttons/Button.tsx (1)
  • Button (53-106)
components/icons/Previous.tsx (1)
  • IconPrevious (7-20)
components/pagination/PaginationItem.tsx (1)
  • PaginationItem (19-32)
components/icons/Next.tsx (1)
  • IconNext (7-20)
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test NodeJS PR - macos-13
  • GitHub Check: Test NodeJS PR - windows-latest

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
components/pagination/PaginationItem.tsx (3)

26-36: Improve a11y: explicit label and visible focus styles.

Add an aria-label for screen readers and a clear focus ring for keyboard users.

-    <button
+    <button
       {...rest}
       type="button"
       onClick={handleClick}
-      className={`font-inter font-normal relative flex size-10 items-center justify-center rounded-full text-sm leading-[26px] ${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'} ${className ?? ''}`}
+      className={`font-inter font-normal relative flex size-10 items-center justify-center rounded-full text-sm leading-[26px] ${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'} focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#6200EE] focus-visible:ring-offset-2 ${className ?? ''}`}
       aria-current={isActive ? 'page' : undefined}
+      aria-label={isActive ? \`Current page, \${pageNumber}\` : \`Go to page \${pageNumber}\`}
     >

4-5: Remove stray Prettier disable comment.

This suppression doesn’t guard any specific line and should be dropped.

-  // eslint-disable-next-line prettier/prettier
-

28-31: Avoid hard-coded hex colors; prefer theme tokens.

Using Tailwind palette (e.g., bg-primary-600, text-foreground, text-gray-900 dark:text-gray-100) or CSS vars keeps dark mode and theming consistent across the site.

If the project exposes a primary color token, swap bg-[#6200EE]/text-[#141717] for those tokens to match design system theming.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90b093b and 1b470f2.

📒 Files selected for processing (2)
  • components/pagination/Pagination.tsx (1 hunks)
  • components/pagination/PaginationItem.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/pagination/Pagination.tsx
⏰ Context from checks skipped due to timeout of 180000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test NodeJS PR - windows-latest
  • GitHub Check: Test NodeJS PR - macos-13

Comment on lines +19 to +36
export default function PaginationItem({
pageNumber,
isActive,
onPageChange,
...buttonProps
}: PaginationItemProps & React.ButtonHTMLAttributes<HTMLButtonElement>) {
return (
<button
onClick={() => onPageChange(pageNumber)}
className={`font-inter font-normal relative flex size-10 items-center
justify-center rounded-full text-sm leading-[26px]
${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'}
`}
aria-current={isActive ? 'page' : undefined}
{...buttonProps}
>
{pageNumber}
</button>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Prevent prop overrides: merge onClick, lock aria-current, and add type="button".

As written, spreading buttonProps after your props allows consumers to override onClick (breaking pagination) and aria-current. Also, without type="button", this can submit a surrounding form. Merge handlers, place the spread first, and set type="button".

-export default function PaginationItem({
-  pageNumber,
-  isActive,
-  onPageChange,
-  ...buttonProps
-}: PaginationItemProps & React.ButtonHTMLAttributes<HTMLButtonElement>) {
-  return (
-    <button
-      onClick={() => onPageChange(pageNumber)}
-      className={`font-inter font-normal relative flex size-10 items-center
-        justify-center rounded-full text-sm leading-[26px]
-        ${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'}
-      `}
-      aria-current={isActive ? 'page' : undefined}
-      {...buttonProps}
-    >
-      {pageNumber}
-    </button>
-  );
+export default function PaginationItem(
+  {
+    pageNumber,
+    isActive,
+    onPageChange,
+    onClick,
+    className,
+    ...rest
+  }: PaginationItemProps & React.ButtonHTMLAttributes<HTMLButtonElement>
+) {
+  const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
+    onClick?.(e);
+    if (!e.defaultPrevented) onPageChange(pageNumber);
+  };
+  return (
+    <button
+      {...rest}
+      type="button"
+      onClick={handleClick}
+      className={`font-inter font-normal relative flex size-10 items-center justify-center rounded-full text-sm leading-[26px] ${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'} ${className ?? ''}`}
+      aria-current={isActive ? 'page' : undefined}
+    >
+      {pageNumber}
+    </button>
+  );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export default function PaginationItem({
pageNumber,
isActive,
onPageChange,
...buttonProps
}: PaginationItemProps & React.ButtonHTMLAttributes<HTMLButtonElement>) {
return (
<button
onClick={() => onPageChange(pageNumber)}
className={`font-inter font-normal relative flex size-10 items-center
justify-center rounded-full text-sm leading-[26px]
${isActive ? 'bg-[#6200EE] text-white' : 'bg-transparent text-[#141717] hover:bg-gray-50'}
`}
aria-current={isActive ? 'page' : undefined}
{...buttonProps}
>
{pageNumber}
</button>
export default function PaginationItem(
{
pageNumber,
isActive,
onPageChange,
onClick,
className,
...rest
}: PaginationItemProps & React.ButtonHTMLAttributes<HTMLButtonElement>
) {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(e);
if (!e.defaultPrevented) onPageChange(pageNumber);
};
return (
<button
{...rest}
type="button"
onClick={handleClick}
className={`font-inter font-normal relative flex size-10 items-center justify-center rounded-full text-sm leading-[26px] ${
isActive
? 'bg-[#6200EE] text-white'
: 'bg-transparent text-[#141717] hover:bg-gray-50'
} ${className ?? ''}`}
aria-current={isActive ? 'page' : undefined}
>
{pageNumber}
</button>
);
}
🤖 Prompt for AI Agents
components/pagination/PaginationItem.tsx lines 19-36: spread of buttonProps
currently comes after component props allowing consumers to override onClick and
aria-current, and missing type attribute can submit parent forms; fix by
spreading buttonProps first, add type="button", and create a merged onClick
handler that calls the component's onPageChange(pageNumber) then calls
buttonProps.onClick if present (preserve event propagation and prevent default
handling only if callers rely on it), and set aria-current explicitly based on
isActive after the spread so it cannot be overridden.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: To Be Triaged

Development

Successfully merging this pull request may close these issues.

[FEATURE] Introduce Pagination on Blog Page

6 participants