Skip to content

Commit

Permalink
Add: Add a useFilterSortBy hook
Browse files Browse the repository at this point in the history
The hook determines the sort field and direction of a filter and allows
to change both via a returned function. The hook can be used to
implement the filter changes when clicking on the different header
columns of a entities list.
  • Loading branch information
bjoernricks committed Jun 13, 2024
1 parent 2bf49dc commit 3af32a4
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 0 deletions.
67 changes: 67 additions & 0 deletions src/web/hooks/__tests__/useFilterSortBy.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/* 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 useFilterSortBy from '../useFilterSortBy';

const TestComponent = ({filter, changeFilter}) => {
const [sortBy, sortDir, sortChange] = useFilterSortBy(filter, changeFilter);
return (
<>
<span data-testid="sortBy">{sortBy}</span>
<span data-testid="sortDir">{sortDir}</span>
<button onClick={() => sortChange('field')} data-testid="sortDirChange" />
<button
onClick={() => sortChange('other-field')}
data-testid="sortFieldChange"
/>
</>
);
};

describe('useFilterSortBy', () => {
test('should return the sort by field and direction', () => {
const changeFilter = testing.fn();
const filter = Filter.fromString('sort=field');

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

expect(screen.getByTestId('sortBy')).toHaveTextContent('field');
expect(screen.getByTestId('sortDir')).toHaveTextContent('asc');
});

test('should change the sort direction', () => {
const filter = Filter.fromString('sort=field');
let currentFilter = filter;
const changeFilter = testing.fn().mockImplementation(filter => {
currentFilter = filter;
});

const {rerender} = render(
<TestComponent filter={currentFilter} changeFilter={changeFilter} />,
);

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

expect(changeFilter).toHaveBeenCalledWith(
Filter.fromString('first=1 sort-reverse=field'),
);

// the filter is not in the state. so a rerender with the new filter is needed
rerender(
<TestComponent filter={currentFilter} changeFilter={changeFilter} />,
);

expect(screen.getByTestId('sortBy')).toHaveTextContent('field');
expect(screen.getByTestId('sortDir')).toHaveTextContent('desc');
});
});
49 changes: 49 additions & 0 deletions src/web/hooks/useFilterSortBy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {useCallback} from 'react';

import {isDefined} from 'gmp/utils/identity';

const ASC = 'asc';
const DESC = 'desc';

/**
* Hook to get the sort by field and direction from a filter
*
* @param {Filter} filter The current filter
* @param {Function} changeFilter Function to call when the filter changes
* @returns Array of the sort by field, sort direction and a function to change the sort by field
*/
const useFilterSortBy = (filter, changeFilter) => {
const reverseField = isDefined(filter)
? filter.get('sort-reverse')
: undefined;
const reverse = isDefined(reverseField);
const sortBy =
reverse || !isDefined(filter) ? reverseField : filter.get('sort');
const sortDir = reverse ? DESC : ASC;

const sortChange = useCallback(
field => {
let sort = 'sort';
const newFilter = filter.copy().first();
const sortField = filter.getSortBy();

if (sortField && sortField === field) {
sort = filter.getSortOrder() === 'sort' ? 'sort-reverse' : 'sort';
}

newFilter.set(sort, field);

changeFilter(newFilter);
},
[changeFilter, filter],
);

return [sortBy, sortDir, sortChange];
};

export default useFilterSortBy;

0 comments on commit 3af32a4

Please sign in to comment.