-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
1 parent
5a78a99
commit 2528ea5
Showing
2 changed files
with
136 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |