Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Pagination",
"position": 6,
"link": {
"type": "generated-index",
"description": "Documentation for the Pagination component"
}
}
184 changes: 184 additions & 0 deletions docs/docs/reference/react/components/Pagination/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
---
title: Pagination
description: Accessible pagination component with keyboard navigation support
keywords:
- Pagination
- React component
- Navigation
- Keyboard navigation
- Accessibility
tags:
- react
- ui
- components
- navigation
---

## Overview

`Pagination` is a reusable, accessible navigation component for paginated content.
It provides intuitive page navigation with both click and keyboard controls,
smart ellipsis display for large page counts, and full accessibility support.

The component uses **0-indexed** page values internally but displays **1-indexed**
page numbers to users.

## ✨ Features

- **Keyboard Navigation**: Navigate pages using Arrow keys (← →)
- **Smart Ellipsis**: Automatically shows ellipsis for large page ranges
- **Accessible**: Full ARIA support with proper labels and current page indication
- **Input-Aware**: Keyboard navigation is disabled when user is typing in form fields
- **Auto-Hide**: Component returns `null` when only one page exists

## 🛠 Basic Usage

```jsx
import Pagination from './components/Pagination';

function Gallery() {
const [page, setPage] = useState(0);
const totalPages = 10;

return (
<div>
<ImageGrid page={page} />
<Pagination page={page} totalPages={totalPages} onChange={setPage} />
</div>
);
}
```

## Keyboard Navigation

Users can navigate pages using keyboard arrow keys:

| Key | Action |
| -------------- | ------------------- |
| `←` ArrowLeft | Go to previous page |
| `→` ArrowRight | Go to next page |

:::note
Keyboard navigation is automatically disabled when the user is focused on
an `<input>`, `<textarea>`, or any content-editable element to prevent
interfering with text editing.
:::

## Ellipsis Behavior

The component intelligently displays ellipsis (`…`) to indicate skipped pages:

```
Page 1: ‹ [1] 2 … 10 ›
Page 5: ‹ 1 … 4 [5] 6 … 10 ›
Page 10: ‹ 1 … 9 [10] ›
```

The visible range is controlled by the `delta` parameter in the internal
`getVisiblePages` function (default: 1), which determines how many pages
to show on each side of the current page.

## API Reference

| Prop | Type | Required | Description |
| ------------ | ------------------------ | -------- | --------------------------------------------------------------- |
| `page` | `number` | Yes | Current page index (0-indexed) |
| `totalPages` | `number` | Yes | Total number of pages |
| `onChange` | `(page: number) => void` | Yes | Callback when page changes, receives new page index (0-indexed) |

## Accessibility

The Pagination component follows WAI-ARIA best practices:

- Uses `<nav>` element with `aria-label="Pagination"`
- Current page button has `aria-current="page"`
- Arrow buttons have descriptive `aria-label` ("Previous page", "Next page")
- Disabled buttons use the `disabled` attribute

```jsx
// Rendered HTML structure
<nav aria-label="Pagination">
<button aria-label="Previous page" disabled>
</button>
<button aria-current="page">1</button>
<button>2</button>
<span>…</span>
<button>10</button>
<button aria-label="Next page">›</button>
</nav>
```

## Styling

Styles are defined in `Pagination.module.css` and include:

| Class | Description |
| ------------- | ---------------------------------------- |
| `.pagination` | Container with flexbox centering and gap |
| `.arrow` | Previous/Next navigation buttons |
| `.page` | Individual page number buttons |
| `.active` | Highlighted current page |
| `.ellipsis` | Ellipsis separator styling |

### CSS Custom Properties Used

- `--color-text-light` — Default text color for buttons
- `--color-glass-border` — Hover background color
- `--color-primary` — Active page background
- `--color-border` — Active page text color
- `--color-text-muted` — Ellipsis color

### Mobile Optimization

The component includes responsive styles for screens under 480px width,
reducing button padding and font sizes for better touch targets.

## Examples

### Basic Pagination

```jsx
const [page, setPage] = useState(0);

<Pagination page={page} totalPages={20} onChange={setPage} />;
```

### With Data Fetching

```jsx
function DataTable() {
const [page, setPage] = useState(0);
const { data, totalPages } = useQuery(['items', page], () => fetchItems({ page, limit: 10 }));

return (
<>
<Table data={data} />
<Pagination
page={page}
totalPages={totalPages}
onChange={(newPage) => {
setPage(newPage);
window.scrollTo({ top: 0, behavior: 'smooth' });
}}
/>
</>
);
}
```

### Conditional Rendering

The component automatically hides when there's only one page or less:

```jsx
// This renders nothing if totalPages <= 1
<Pagination page={0} totalPages={1} onChange={setPage} />
```

## Implementation Notes

- **0-indexed internally**: The `page` prop and `onChange` callback use 0-based indexing
- **1-indexed display**: Page buttons show human-readable 1-based numbers
- **Effect cleanup**: Keyboard event listeners are properly cleaned up on unmount
- **No external dependencies**: Uses only React's built-in hooks
164 changes: 164 additions & 0 deletions docs/docs/reference/react/components/Pagination/tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
---
title: Pagination Tests
description: Test coverage for the Pagination component
keywords:
- Pagination
- Testing
- Vitest
- React Testing Library
tags:
- react
- testing
- components
---

## Overview

The `Pagination` component has comprehensive test coverage using
[Vitest](https://vitest.dev/) and [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/).

Tests are located in `src/components/Pagination.test.jsx`.

## Running Tests

```bash
# Run all tests
npm test

# Run only Pagination tests
npm test Pagination

# Run tests in watch mode
npm test -- --watch
```

## Test Categories

### Rendering Tests

Tests that verify the component renders correctly under various conditions:

| Test | Description |
| ---------------------------------------- | ---------------------------------------- |
| Should not render when `totalPages` is 1 | Component returns `null` for single page |
| Should not render when `totalPages` is 0 | Component returns `null` for zero pages |
| Should render when `totalPages` > 1 | Navigation element is present |
| Should render with correct `aria-label` | Accessibility label is set |
| Should render previous and next buttons | Arrow buttons are present |

### Page Button Tests

Tests for individual page number buttons:

| Test | Description |
| -------------------------------------------------- | ------------------------------- |
| Should render page numbers correctly | 1-indexed display verification |
| Should mark current page with `aria-current` | Accessibility current indicator |
| Should apply active class to current page | Visual styling verification |
| Should not have `aria-current` on non-active pages | Correct ARIA usage |

### Ellipsis Display Tests

Tests for the smart ellipsis behavior:

| Test | Description |
| ---------------------------------------------- | ----------------------- |
| Should show leading ellipsis | When not on first pages |
| Should show trailing ellipsis | When not on last pages |
| Should show first page button | When far from start |
| Should show last page button | When far from end |
| Should not show leading ellipsis on first page | Edge case handling |
| Should not show trailing ellipsis on last page | Edge case handling |

### Navigation Button Tests

Tests for Previous/Next buttons:

| Test | Description |
| ---------------------------------------------------- | ----------------- |
| Should disable previous button on first page | Boundary handling |
| Should disable next button on last page | Boundary handling |
| Should enable previous button when not on first page | Normal state |
| Should enable next button when not on last page | Normal state |

### Click Interaction Tests

Tests for mouse/touch interactions:

| Test | Description |
| -------------------------------------------- | ----------------------- |
| Should call `onChange` with previous page | Previous button click |
| Should call `onChange` with next page | Next button click |
| Should call `onChange` with correct page | Page number click |
| Should call `onChange` with 0 for first page | First page button click |
| Should call `onChange` with last index | Last page button click |

### Keyboard Navigation Tests

Tests for keyboard accessibility:

| Test | Description |
| -------------------------------------------- | -------------------- |
| Should go to next page on ArrowRight | Right arrow key |
| Should go to previous page on ArrowLeft | Left arrow key |
| Should not go beyond last page on ArrowRight | Boundary handling |
| Should not go before first page on ArrowLeft | Boundary handling |
| Should ignore navigation in input fields | Form field awareness |
| Should ignore navigation in textarea | Form field awareness |
| Should ignore other keys | Only arrow keys work |

### Visible Pages Calculation Tests

Tests for the page windowing algorithm:

| Test | Description |
| ----------------------------------------- | ------------------ |
| Should show adjacent pages around current | Delta calculation |
| Should handle edge case at beginning | First page display |
| Should handle edge case at end | Last page display |

### Cleanup Tests

Tests for proper React lifecycle handling:

| Test | Description |
| --------------------------------------- | ---------------------- |
| Should remove event listener on unmount | Memory leak prevention |

## Test Utilities

### CSS Module Mock

```jsx
vi.mock('./Pagination.module.css', () => ({
default: {
pagination: 'mocked-pagination-class',
arrow: 'mocked-arrow-class',
page: 'mocked-page-class',
active: 'mocked-active-class',
ellipsis: 'mocked-ellipsis-class',
},
}));
```

### Common Test Setup

```jsx
const mockOnChange = vi.fn();

beforeEach(() => {
mockOnChange.mockClear();
});
```

## Coverage Areas

- Conditional rendering
- Accessibility attributes
- Click handlers
- Keyboard navigation
- Disabled states
- Ellipsis logic
- Edge cases (first/last page)
- Event listener cleanup
- Form field awareness
Loading