Skip to content

Commit

Permalink
Add: Add a usePagination hook
Browse files Browse the repository at this point in the history
The hook returns functions the update a filter for getting the next,
previous, first and last page for a list of entities.
  • Loading branch information
bjoernricks committed Jun 13, 2024
1 parent 3af32a4 commit 5a78a99
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 0 deletions.
108 changes: 108 additions & 0 deletions src/web/hooks/__tests__/usePagination.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

/* eslint-disable react/prop-types */

import {describe, test, expect, testing} from '@gsa/testing';

import {fireEvent, render, screen} from 'web/utils/testing';

import Filter from 'gmp/models/filter';

import usePagination from '../usePagination';

const TestComponent = ({filter, counts, changeFilter}) => {
const [first, last, next, previous] = usePagination(
filter,
counts,
changeFilter,
);
return (
<>
<button data-testid="first" onClick={first} />
<button data-testid="last" onClick={last} />
<button data-testid="next" onClick={next} />
<button data-testid="previous" onClick={previous} />
</>
);
};

describe('usePageFilter', () => {
test('should change the filter for the first page', () => {
const filter = Filter.fromString('first=10');
const counts = {filtered: 100, rows: 10};
const changeFilter = testing.fn();

render(
<TestComponent
filter={filter}
counts={counts}
changeFilter={changeFilter}
/>,
);

fireEvent.click(screen.getByTestId('first'));

expect(changeFilter).toHaveBeenCalledWith(Filter.fromString('first=1'));
});

test('should change the filter for the last page', () => {
const filter = Filter.fromString('first=10');
const counts = {filtered: 100, rows: 10};
const changeFilter = testing.fn();

render(
<TestComponent
filter={filter}
counts={counts}
changeFilter={changeFilter}
/>,
);

fireEvent.click(screen.getByTestId('last'));

expect(changeFilter).toHaveBeenCalledWith(Filter.fromString('first=91'));
});

test('should change the filter for the next page', () => {
const filter = Filter.fromString('first=10');
const counts = {filtered: 100, rows: 10};
const changeFilter = testing.fn();

render(
<TestComponent
filter={filter}
counts={counts}
changeFilter={changeFilter}
/>,
);

fireEvent.click(screen.getByTestId('next'));

expect(changeFilter).toHaveBeenCalledWith(
Filter.fromString('first=20 rows=10'),
);
});

test('should change the filter for the previous page', () => {
const filter = Filter.fromString('first=10');
const counts = {filtered: 100, rows: 10};
const changeFilter = testing.fn();

render(
<TestComponent
filter={filter}
counts={counts}
changeFilter={changeFilter}
/>,
);

fireEvent.click(screen.getByTestId('previous'));

expect(changeFilter).toHaveBeenCalledWith(
Filter.fromString('first=1 rows=10'),
);
});
});
54 changes: 54 additions & 0 deletions src/web/hooks/usePagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {useCallback} from 'react';

/**
* Hook to to get the filter for the next, previous, last and first page for a
* list of entities
*
* @example
*
* const gmp = useGmp();
* const [filter, setFilter] = useState(new Filter());
* const [entities, setEntities] = useState([]);
* const [counts, setCounts] = useState({});
* const updateFilter = useCallback(filter => {
* setFilter(filter);
* gmp.tasks.get(filter).then(response => {setEntities(response.data);setCounts(response.meta.counts)});
* }, [gmp.tasks]);
* const [first, last, next, previous] = usePagination(filter, counts, updateFilter)
*
* @param {Filter} filter Current applied filter
* @param {Object} counts Current entities counts. Required for calculating the
* last page.
* @param {Function} changeFilter Function to call when the new filter is applied
* @returns {Array} Tuple of functions to update the filter for the first, last,
* next and previous page.
*/
const usePagination = (filter, counts, changeFilter) => {
const getNext = useCallback(() => {
changeFilter(filter.next());
}, [filter, changeFilter]);

const getPrevious = useCallback(() => {
changeFilter(filter.previous());
}, [filter, changeFilter]);

const getFirst = useCallback(() => {
changeFilter(filter.first());
}, [filter, changeFilter]);

const getLast = useCallback(() => {
const last =
Math.floor((counts.filtered - 1) / counts.rows) * counts.rows + 1;
const newFilter = filter.first(last);
changeFilter(newFilter);
}, [filter, counts, changeFilter]);

return [getFirst, getLast, getNext, getPrevious];
};

export default usePagination;

0 comments on commit 5a78a99

Please sign in to comment.