diff --git a/cypress/component/DataViewToolbar.cy.tsx b/cypress/component/DataViewToolbar.cy.tsx index 63f2dcaf..242e1387 100644 --- a/cypress/component/DataViewToolbar.cy.tsx +++ b/cypress/component/DataViewToolbar.cy.tsx @@ -34,4 +34,4 @@ describe('DataViewToolbar', () => { cy.get('[data-ouia-component-id="DataViewToolbar-bulk-select"]').should('exist'); cy.get('[data-ouia-component-id="ResponsiveActions-menu"]').should('exist'); }); -}); \ No newline at end of file +}); diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/SelectionExample.tsx b/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/SelectionExample.tsx index fc9a0e7f..9d63c2ad 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/SelectionExample.tsx +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/SelectionExample.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { useDataViewSelection } from '@patternfly/react-data-view/dist/dynamic/Hooks'; import { BulkSelect, BulkSelectValue } from '@patternfly/react-component-groups/dist/dynamic/BulkSelect'; +import { Button } from '@patternfly/react-core'; import { DataView } from '@patternfly/react-data-view/dist/dynamic/DataView'; import { DataViewToolbar } from '@patternfly/react-data-view/dist/dynamic/DataViewToolbar'; import { DataViewTable } from '@patternfly/react-data-view/dist/dynamic/DataViewTable'; @@ -30,13 +31,18 @@ const ouiaId = 'LayoutExample'; export const BasicExample: React.FunctionComponent = () => { const selection = useDataViewSelection({ matchOption: (a, b) => a[0] === b[0] }); - const { selected, onSelect } = selection; + const { selected, onSelect, setSelected } = selection; const handleBulkSelect = (value: BulkSelectValue) => { value === BulkSelectValue.none && onSelect(false); value === BulkSelectValue.all && onSelect(true, rows); }; + // Example: Select first two rows programmatically using setSelected + const handleSelectFirstTwo = () => { + setSelected(rows.slice(0, 2)); + }; + return ( { selectedCount={selected.length} onSelect={handleBulkSelect} /> - } + } + actions={ + + } /> ); -} \ No newline at end of file +} diff --git a/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md b/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md index 1901f424..b936a8f6 100644 --- a/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md +++ b/packages/module/patternfly-docs/content/extensions/data-view/examples/Toolbar/Toolbar.md @@ -104,6 +104,7 @@ The `useDataViewSelection` hook manages the selection state of the data view. - `selected` array of currently selected records. - `isSelected` function returning the selection state for the record. - `onSelect` callback to modify the selection state. This accepts the `isSelecting` flag (indicates if records are being selected or deselected) and `items` (affected records). +- `setSelected` function to directly set the selected items array. This is useful for programmatically setting a specific selection state without needing to use the `onSelect` callback. ### Selection example diff --git a/packages/module/src/Hooks/selection.test.tsx b/packages/module/src/Hooks/selection.test.tsx index ce21687e..270d2e88 100644 --- a/packages/module/src/Hooks/selection.test.tsx +++ b/packages/module/src/Hooks/selection.test.tsx @@ -9,6 +9,7 @@ describe('useDataViewSelection', () => { selected: [], onSelect: expect.any(Function), isSelected: expect.any(Function), + setSelected: expect.any(Function), }) }); @@ -19,6 +20,7 @@ describe('useDataViewSelection', () => { selected: initialSelected, onSelect: expect.any(Function), isSelected: expect.any(Function), + setSelected: expect.any(Function), }) }); @@ -49,4 +51,66 @@ describe('useDataViewSelection', () => { expect(result.current.isSelected({ id: 1, name: 'test1' })).toBe(true); expect(result.current.isSelected({ id: 3, name: 'test2' })).toBe(false); }); -}); \ No newline at end of file + + it('should have setSelected function in return object', () => { + const { result } = renderHook(() => useDataViewSelection({ matchOption: (a, b) => a.id === b.id })) + expect(result.current).toEqual({ + selected: [], + onSelect: expect.any(Function), + isSelected: expect.any(Function), + setSelected: expect.any(Function), + }) + }); + + it('should set selected items directly using setSelected - objects', async () => { + const initialSelected = [ { id: 1, name: 'test1' } ]; + const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a.id === b.id })) + + const newSelected = [ { id: 2, name: 'test2' }, { id: 3, name: 'test3' } ]; + + await act(async () => { + result.current.setSelected(newSelected); + }); + + expect(result.current.selected).toEqual(newSelected); + }); + + it('should set selected items directly using setSelected - strings', async () => { + const initialSelected = [ 'test1', 'test2' ]; + const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a === b })) + + const newSelected = [ 'test3', 'test4', 'test5' ]; + + await act(async () => { + result.current.setSelected(newSelected); + }); + + expect(result.current.selected).toEqual(newSelected); + }); + + it('should clear all selections using setSelected with empty array', async () => { + const initialSelected = [ { id: 1, name: 'test1' }, { id: 2, name: 'test2' } ]; + const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a.id === b.id })) + + await act(async () => { + result.current.setSelected([]); + }); + + expect(result.current.selected).toEqual([]); + }); + + it('should update isSelected correctly after using setSelected', async () => { + const initialSelected = [ { id: 1, name: 'test1' } ]; + const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a.id === b.id })) + + const newSelected = [ { id: 2, name: 'test2' }, { id: 3, name: 'test3' } ]; + + await act(async () => { + result.current.setSelected(newSelected); + }); + + expect(result.current.isSelected({ id: 1, name: 'test1' })).toBe(false); + expect(result.current.isSelected({ id: 2, name: 'test2' })).toBe(true); + expect(result.current.isSelected({ id: 3, name: 'test3' })).toBe(true); + }); +}); diff --git a/packages/module/src/Hooks/selection.ts b/packages/module/src/Hooks/selection.ts index 2ba443f6..cd699087 100644 --- a/packages/module/src/Hooks/selection.ts +++ b/packages/module/src/Hooks/selection.ts @@ -24,9 +24,14 @@ export const useDataViewSelection = (props?: UseDataViewSelectionProps) => { const isSelected = (item: any): boolean => Boolean(selected.find(selected => matchOption(selected, item))); + const setSelectedItems = (items: any[]) => { + setSelected(items); + }; + return { selected, onSelect, - isSelected + isSelected, + setSelected: setSelectedItems }; }; diff --git a/packages/module/src/InternalContext/InternalContext.tsx b/packages/module/src/InternalContext/InternalContext.tsx index 740c1a8d..5748db3e 100644 --- a/packages/module/src/InternalContext/InternalContext.tsx +++ b/packages/module/src/InternalContext/InternalContext.tsx @@ -6,6 +6,8 @@ export interface DataViewSelection { onSelect: (isSelecting: boolean, items?: any[] | any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any /** Checks if a specific item is currently selected */ isSelected: (item: any) => boolean; // eslint-disable-line @typescript-eslint/no-explicit-any + /** Directly sets the selected items */ + setSelected?: (items: any[]) => void; // eslint-disable-line @typescript-eslint/no-explicit-any /** Determines if selection is disabled for a given item */ isSelectDisabled?: (item: any) => boolean; // eslint-disable-line @typescript-eslint/no-explicit-any }