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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"eslint-plugin-prettier": "^3.3.1",
"jest": "^26.6.3",
"next": "^11.0.1",
"next-router-mock": "^0.1.4",
"next-router-mock": "^0.6.1",
"prettier": "^2.2.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down
29 changes: 15 additions & 14 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ React Pagination
[![npm package](https://badge.fury.io/js/%40aboutbits%2Freact-pagination.svg)](https://badge.fury.io/js/%40aboutbits%2Freact-pagination)
[![license](https://img.shields.io/github/license/aboutbits/react-pagination)](https://github.com/aboutbits/react-pagination/blob/main/license.md)

This package includes pagination hooks for React. The hooks support saving the search and pagination values in local
This package includes pagination hooks for React. The hooks support saving the query and pagination values in local
state or in the browser URL.

## Table of content

- [Usage](#usage)
- [useSearchAndPagination](#usesearchandpagination)
- [useQueryAndPagination](#usequeryandpagination)
- [Supported Implementations](#supported-implementations)
- [In Memory Pagination](#in-memory-pagination)
- [React-Router based pagination](#react-router-based-pagination)
Expand All @@ -26,17 +26,17 @@ First, you have to install the package:
npm install @aboutbits/react-pagination
```

Second, you can make use of the `useSearchAndPagination` hook. This package implements 3 versions of this hook:
Second, you can make use of the `useQueryAndPagination` hook. This package implements 3 versions of this hook:

- [In Memory](#in-memory-pagination): Use this hook where you don't want to modify browser history. e.g. Dialogs
- [React Router](#react-router-based-pagination): Use this hook if you want to keep track of the state in the URL and
your project is using React Router.
- [NextJS Router](#nextjs-router-based-pagination): Use this hook if you want to keep track of the state in the URL and
your project is using NextJS.

### useSearchAndPagination
### useQueryAndPagination

This hook supports the combination of a search value and pagination and manages the state of the search value, and the
This hook supports the combination of query parameters and pagination and manages the state of the query parameter values and the
pagination values.

#### The hook supports following configuration parameter object:
Expand All @@ -45,39 +45,40 @@ pagination values.
|---|---|---|---|
|indexType|IndexType|IndexType.ZERO_BASED|It defines whether the pagination is zero or one based.|
|pageSize|number|15|Page size of the pagination.|
|defaultQueryParameters/Record<string, string>|{}|It defines the default value for each query parameter. This is used to remove a query parameter from the URL and also to clear the query.

#### The hook returns the following object:

|value|type|description|
|---|---|---|
|search|string|value of your search parameter|
|queryParameters|object|values of your query parameters|
|page|number|value of the current page|
|size|number|max elements in a single page|
|actions|object|object with 3 functions: search, setPage, clear|
|actions|object|object with 3 functions: updateQuery, setPage, clear|

#### Example usage with NextJS

```tsx
import { useSearchAndPagination } from '@aboutbits/react-pagination/dist/nextRouterPagination'
import { useQueryAndPagination } from '@aboutbits/react-pagination/dist/nextRouterPagination'

const users = [
'Alex', 'Simon', 'Natan', 'Nadia', 'Moritz', 'Marie'
]

function UserList() {
const { page, size, search, actions } = useSearchAndPagination({pageSize: 2})
const { page, size, queryParameters, actions } = useQueryAndPagination({pageSize: 2})

return (
<div>
<input onChange={(value) => actions.search(value)}/>
<input onChange={(value) => actions.updateQuery({search: value})}/>
<button onClick={() => actions.clear()}>Clear Input</button>
<select onSelect={(value) => actions.setPage(value)}>
<option value={0}>First Page</option>
<option value={1}>Second Page</option>
</select>

<ul>
{users.filter(user => user.startsWith(search))
{users.filter(user => user.startsWith(queryParameters.search))
.slice(page, page + size)
.map(user => <li>{user}</li>)}
</ul>
Expand All @@ -99,15 +100,15 @@ This package includes 3 different implementations of the above hook.
Use this pagination hook if you want to keep track of the pagination in memory. This is very handy for dialogs.

```tsx
import { useSearchAndPagination } from '@aboutbits/react-pagination/dist/inMemoryPagination'
import { useQueryAndPagination } from '@aboutbits/react-pagination/dist/inMemoryPagination'
```

### React-Router based pagination

These are specific hooks for applications that use [React Router](https://reactrouter.com/) for routing.

```tsx
import { useSearchAndPagination } from '@aboutbits/react-pagination/dist/reactRouterPagination'
import { useQueryAndPagination } from '@aboutbits/react-pagination/dist/reactRouterPagination'
```

### NextJS Router based pagination
Expand All @@ -116,7 +117,7 @@ These are specific hooks for applications that use [NextJS Router](https://nextj
for routing.

```tsx
import { useSearchAndPagination } from '@aboutbits/react-pagination/dist/nextRouterPagination'
import { useQueryAndPagination } from '@aboutbits/react-pagination/dist/nextRouterPagination'
```

## Build & Publish
Expand Down
66 changes: 51 additions & 15 deletions src/__test__/inMemoryPagination.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { act, renderHook } from '@testing-library/react-hooks'
import { useSearchAndPagination } from '../inMemoryPagination'
import { useQueryAndPagination } from '../inMemoryPagination'
import { IndexType } from '../types'

test('should initialize pagination', () => {
const { result } = renderHook(() => useSearchAndPagination())
const { result } = renderHook(() => useQueryAndPagination())

expect(result.current.page).toBe(0)
expect(result.current.search).toBe('')
expect(result.current.size).toBe(15)
})

test('should change page', () => {
const { result } = renderHook(() => useSearchAndPagination())
const { result } = renderHook(() => useQueryAndPagination())

act(() => {
result.current.actions.setPage(2)
Expand All @@ -21,17 +20,21 @@ test('should change page', () => {
})

test('should change search', () => {
const { result } = renderHook(() => useSearchAndPagination())
const { result } = renderHook(() =>
useQueryAndPagination({ defaultQueryParameters: { search: '' } })
)

act(() => {
result.current.actions.search('Max')
result.current.actions.updateQuery({ search: 'Max' })
})

expect(result.current.search).toBe('Max')
expect(result.current.queryParameters.search).toBe('Max')
})

test('on search change -> page should be reset', () => {
const { result } = renderHook(() => useSearchAndPagination())
const { result } = renderHook(() =>
useQueryAndPagination({ defaultQueryParameters: { search: '' } })
)

act(() => {
result.current.actions.setPage(2)
Expand All @@ -40,40 +43,73 @@ test('on search change -> page should be reset', () => {
expect(result.current.page).toBe(2)

act(() => {
result.current.actions.search('Max')
result.current.actions.updateQuery({ search: 'Max' })
})

expect(result.current.search).toBe('Max')
expect(result.current.queryParameters.search).toBe('Max')
expect(result.current.page).toBe(0)
})

test('clear pagination should reset search and page', () => {
const { result } = renderHook(() => useSearchAndPagination())
const { result } = renderHook(() =>
useQueryAndPagination({ defaultQueryParameters: { search: '' } })
)

act(() => {
result.current.actions.search('Max')
result.current.actions.updateQuery({ search: 'Max' })
})

act(() => {
result.current.actions.setPage(2)
})

expect(result.current.search).toBe('Max')
expect(result.current.queryParameters.search).toBe('Max')
expect(result.current.page).toBe(2)

act(() => {
result.current.actions.clear()
})

expect(result.current.search).toBe('')
expect(result.current.queryParameters.search).toBe('')
expect(result.current.page).toBe(0)
})

test('change default parameters', () => {
const { result } = renderHook(() =>
useSearchAndPagination({ indexType: IndexType.ONE_BASED, pageSize: 10 })
useQueryAndPagination({ indexType: IndexType.ONE_BASED, pageSize: 10 })
)

expect(result.current.page).toBe(1)
expect(result.current.size).toBe(10)
})

test('query multiple different properties, should keep them all', () => {
const { result } = renderHook(() =>
useQueryAndPagination({
defaultQueryParameters: { search: '', department: '' },
})
)

act(() => {
result.current.actions.updateQuery({ search: 'Max' })
result.current.actions.updateQuery({ department: 'IT' })
})

expect(result.current.queryParameters.search).toBe('Max')
expect(result.current.queryParameters.department).toBe('IT')
})

test('query a property that is not configured, should do nothing', () => {
const { result } = renderHook(() =>
useQueryAndPagination({
defaultQueryParameters: { search: '' },
})
)

act(() => {
result.current.actions.updateQuery({ department: 'IT' })
})

expect(result.current.queryParameters.search).toBe('')
expect(result.current.queryParameters.department).toBeUndefined()
})
Loading