Skip to content

Commit

Permalink
feat: add disabled flag to disable pagination, closes #57
Browse files Browse the repository at this point in the history
  • Loading branch information
felixmosh committed Dec 23, 2024
1 parent d1e4a79 commit 46e4f47
Show file tree
Hide file tree
Showing 6 changed files with 101 additions and 28 deletions.
21 changes: 16 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ interface IPaginateParams {
currentPage: number,
isFromStart?: boolean,
isLengthAware?: boolean,
disabled?: boolean,
}

interface IWithPagination<T = any> {
Expand Down Expand Up @@ -62,6 +63,14 @@ const result = await knex('persons')
// result.data - will hold persons data
// result.pagination - will hold pagination object
```
## paginate options

| Key | type | Value |
|-----------------------------|---------|----------------------------------------------------------------------|
| perPage | number | Items per page. |
| currentPage | number | The current page |
| isFromStart / isLengthAware | boolean | Indicates if the returned list should get all pages till currentPage |
| disabled | boolean | Disables the pagination functionality, returns all relevant rows |

## `pagination` object

Expand All @@ -72,12 +81,14 @@ const result = await knex('persons')
| from | Counting ID of the first item of the current page. |
| to | Counting ID of the last item of the current page. |

If `isLengthAware == true` or `currentPage == 1` or `isFromStart == true` pagination object will contain `total` & `lastPage`:
If `isLengthAware == true` or `currentPage == 1` or `isFromStart == true` pagination object will contain additional fields:

| Key | Value |
|----------|-------------------------------------------|
| total | Total items that the full query contains. |
| lastPage | Last page number. |
| Key | Value |
|----------|-----------------------------------------------------|
| total | Total items that the full query contains. |
| lastPage | Last page number. |
| nextPage | The next page or `null` when at the last page. |
| prevPage | The previous page or `null` when at the first page. |


This lib got inspiration from [`knex-paginator`](https://github.com/cannblw/knex-paginator).
11 changes: 11 additions & 0 deletions __tests-tsd__/types.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,15 @@ interface User {
})
).pagination
);

expectType<IBasePagination>(
(
await db('users').select('*').paginate({
perPage: 10,
currentPage: 1,
isFromStart: true,
disabled: true,
})
).pagination
);
})();
34 changes: 33 additions & 1 deletion __tests__/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ describe('paginate', () => {
});
});

['isFromStart', 'isLengthAware'].forEach((param) => {
['isFromStart', 'isLengthAware', 'disabled'].forEach((param) => {
it(`should throw if ${param} is not a boolean`, () => {
expect(() => db('persons').paginate({ [param]: 'x' })).toThrowError(
`Paginate error: ${param} must be a boolean.`
Expand Down Expand Up @@ -341,5 +341,37 @@ describe('paginate', () => {
});
});
});

describe('disabled mode', () => {
it('should fetch all data when disabled', async () => {
const perPage = 2;
const currentPage = 2;

const result = await db('persons').paginate({ perPage, currentPage, disabled: true });

expect(result.data).toHaveLength(total);
expect(result.pagination).toMatchObject({
current_page: 1,
per_page: total,
from: 0,
to: total,
});
});

it('should not fetch totals when disabled', async () => {
const perPage = 2;
const currentPage = 1;

const result = await db('persons').paginate({
perPage,
currentPage,
isFromStart: true,
disabled: true,
});

expect(result.data).toHaveLength(total);
expect(result.pagination).not.toHaveProperty('total', 'last_page');
});
});
});
});
43 changes: 22 additions & 21 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
const Knex = require('knex');
const { assertNumber, assertBoolean } = require('./utils');

module.exports.attachPaginate = function attachPaginate() {
function paginate({ perPage = 10, currentPage = 1, isFromStart = false, isLengthAware = false }) {
if (isNaN(perPage)) {
throw new Error('Paginate error: perPage must be a number.');
}

if (isNaN(currentPage)) {
throw new Error('Paginate error: currentPage must be a number.');
}

if (typeof isFromStart !== 'boolean') {
throw new Error('Paginate error: isFromStart must be a boolean.');
}

if (typeof isLengthAware !== 'boolean') {
throw new Error('Paginate error: isLengthAware must be a boolean.');
}
function paginate({
perPage = 10,
currentPage = 1,
isFromStart = false,
isLengthAware = false,
disabled = false,
}) {
assertNumber('perPage', perPage);
assertNumber('currentPage', currentPage);
assertBoolean('isFromStart', isFromStart);
assertBoolean('isLengthAware', isLengthAware);
assertBoolean('disabled', disabled);

if (currentPage < 1) {
currentPage = 1;
Expand All @@ -29,8 +26,8 @@ module.exports.attachPaginate = function attachPaginate() {

let pagination = {
perPage,
currentPage,
from: offset,
currentPage: disabled ? 1 : currentPage,
from: disabled ? 0 : offset,
to: undefined, // will be assigned when we fetch the data
};

Expand All @@ -41,14 +38,16 @@ module.exports.attachPaginate = function attachPaginate() {

const originalQuery = shouldFetchTotals ? this.clone() : null;

// This will paginate the data itself
this.offset(offset).limit(limit);
if (!disabled) {
// This will paginate the data itself
this.offset(offset).limit(limit);
}

return this.client.transaction(async (trx) => {
const data = await this.transacting(trx);
pagination.to = offset + data.length;

if (shouldFetchTotals) {
if (shouldFetchTotals && !disabled) {
const countResult = await new this.constructor(this.client)
.count('* as total')
.from(originalQuery.clear('offset').clearOrder().as('count__query__'))
Expand All @@ -65,6 +64,8 @@ module.exports.attachPaginate = function attachPaginate() {
prevPage: currentPage > 1 ? currentPage - 1 : null,
nextPage: currentPage < lastPage ? currentPage + 1 : null,
};
} else if (disabled) {
pagination.perPage = pagination.to = data.length;
}

return { data, pagination: postProcessResponse(pagination) };
Expand Down
16 changes: 16 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function assertNumber(paramName, value) {
if (isNaN(value)) {
throw new Error(`Paginate error: ${paramName} must be a number.`);
}
}

function assertBoolean(paramName, value) {
if (typeof value !== 'boolean') {
throw new Error(`Paginate error: ${paramName} must be a boolean.`);
}
}

module.exports = {
assertNumber,
assertBoolean,
};
4 changes: 3 additions & 1 deletion types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@ interface IPaginateParams {
currentPage: number;
isFromStart?: boolean;
isLengthAware?: boolean;
disabled?: boolean;
}

interface IWithPagination<Data, TParams = IPaginateParams> {
data: Data[];
pagination: IPagination<TParams>;
}

type IPagination<TParams> = TParams extends
type IPagination<TParams> = TParams extends (
| { currentPage: 1 }
| { isFromStart: true }
| { isLengthAware: true }
) & { disabled?: false }
? ILengthAwarePagination
: IBasePagination;

Expand Down

0 comments on commit 46e4f47

Please sign in to comment.