Skip to content

Commit

Permalink
Add: Add a useSelection hook
Browse files Browse the repository at this point in the history
The useSelection hook implements the entity selection at a entities list
table. It is possible to select/deselect specific entities from the
list/table, to select all entities displayed at the page or all entities
for the current filter (filter without rows value applied).
  • Loading branch information
bjoernricks committed Jun 14, 2024
1 parent 5a78a99 commit 2528ea5
Show file tree
Hide file tree
Showing 2 changed files with 136 additions and 0 deletions.
78 changes: 78 additions & 0 deletions src/web/hooks/__tests__/useSelection.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

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

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

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

import useSelection from '../useSelection';

const TestComponent = () => {
const {selected, selectionType, select, deselect, changeSelectionType} =
useSelection(SelectionType.SELECTION_USER);
return (
<>
<span data-testid="selectionType">{selectionType}</span>
<input
type="checkbox"
checked={selected?.includes(1) === true}
readOnly
data-testid="checked1"
/>
<input
type="checkbox"
checked={selected?.includes(2) === true}
readOnly
data-testid="checked2"
/>
<button onClick={() => select(1)} data-testid="select1" />
<button onClick={() => select(2)} data-testid="select2" />
<button onClick={() => deselect(1)} data-testid="deselect1" />
<button
onClick={() =>
changeSelectionType(SelectionType.SELECTION_PAGE_CONTENTS)
}
data-testid="selectPageContent"
/>
</>
);
};

describe('useSelection', () => {
test('should select and deselect entities', () => {
render(<TestComponent />);

fireEvent.click(screen.getByTestId('select1'));
fireEvent.click(screen.getByTestId('select2'));

expect(screen.getByTestId('checked1')).toBeChecked();
expect(screen.getByTestId('checked2')).toBeChecked();

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

expect(screen.getByTestId('checked1')).not.toBeChecked();
});

test('should change selection type', () => {
render(<TestComponent />);

fireEvent.click(screen.getByTestId('select1'));
fireEvent.click(screen.getByTestId('select2'));

expect(screen.getByTestId('checked1')).toBeChecked();
expect(screen.getByTestId('checked2')).toBeChecked();

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

expect(screen.getByTestId('checked1')).not.toBeChecked();
expect(screen.getByTestId('checked2')).not.toBeChecked();
expect(screen.getByTestId('selectionType')).toHaveTextContent(
SelectionType.SELECTION_PAGE_CONTENTS,
);
});
});
58 changes: 58 additions & 0 deletions src/web/hooks/useSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* SPDX-FileCopyrightText: 2024 Greenbone AG
*
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import {useState, useCallback} from 'react';

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

import SelectionType from 'web/utils/selectiontype';

/**
* Hook to manage selection of entities
*
* @param {int} initialSelectionType The initial selection type to be used.
* Default is SelectionType.SELECTION_PAGE_CONTENTS
* @returns {Object} selected, selectionType, select, deselect, changeSelectionType
*/
const useSelection = (
initialSelectionType = SelectionType.SELECTION_PAGE_CONTENTS,
) => {
const [selected, setSelected] = useState(
initialSelectionType === SelectionType.SELECTION_USER ? [] : undefined,
);
const [selectionType, setSelectionType] = useState(initialSelectionType);

const select = useCallback(obj => {
// ensure the using component gets re-rendered by creating new array
setSelected(prevSelected =>
isDefined(prevSelected) && !prevSelected.includes(obj)
? [...prevSelected, obj]
: undefined,
);
}, []);

const deselect = useCallback(obj => {
// ensure the using component gets re-rendered by creating new array
setSelected(prevSelected => {
if (isDefined(prevSelected) && prevSelected.includes(obj)) {
return prevSelected.filter(o => o !== obj);
}
return undefined;
});
}, []);

const changeSelectionType = useCallback(newSelectionType => {
if (newSelectionType === SelectionType.SELECTION_USER) {
setSelected([]);
} else {
setSelected();
}

setSelectionType(newSelectionType);
}, []);
return {selected, selectionType, select, deselect, changeSelectionType};
};

export default useSelection;

0 comments on commit 2528ea5

Please sign in to comment.