diff --git a/packages/@react-aria/dnd/stories/DraggableCollection.tsx b/packages/@react-aria/dnd/stories/DraggableCollection.tsx index 75a06aee855..355874a9224 100644 --- a/packages/@react-aria/dnd/stories/DraggableCollection.tsx +++ b/packages/@react-aria/dnd/stories/DraggableCollection.tsx @@ -100,7 +100,8 @@ function DraggableCollection(props) { preview, onDragStart: props.onDragStart, onDragMove: props.onDragMove, - onDragEnd: props.onDragEnd + onDragEnd: props.onDragEnd, + ...props.draggableCollectionStateProps }); useDraggableCollection({}, dragState, ref); diff --git a/packages/@react-aria/dnd/stories/dnd.stories.tsx b/packages/@react-aria/dnd/stories/dnd.stories.tsx index 47592d22f4d..9b051365f91 100644 --- a/packages/@react-aria/dnd/stories/dnd.stories.tsx +++ b/packages/@react-aria/dnd/stories/dnd.stories.tsx @@ -20,6 +20,7 @@ import Copy from '@spectrum-icons/workflow/Copy'; import Cut from '@spectrum-icons/workflow/Cut'; import {Dialog, DialogTrigger} from '@react-spectrum/dialog'; import dndStyles from './dnd.css'; +import {DraggableCollectionExample as DraggableGridExampleStandalone} from './DraggableCollection'; import {DraggableListBox} from './DraggableListBox'; import {DragPreview} from '../src/DragPreview'; import {DroppableGridExample} from './DroppableGrid'; @@ -575,3 +576,21 @@ export const DroppableEnabledDisabledControl: DnDStoryObj = { } } }; + +export const DraggedItemOnly: DnDStoryObj = { + render: () => ( + + { + action('getItems')(draggedKey); + return [{ + 'text/plain': `Dragged item: ${draggedKey}` + }]; + } + }} /> + + + ), + name: 'Drag only dragged item' +}; diff --git a/packages/@react-aria/dnd/test/useDraggableCollection.test.js b/packages/@react-aria/dnd/test/useDraggableCollection.test.js index 378a076913c..92e1afe1120 100644 --- a/packages/@react-aria/dnd/test/useDraggableCollection.test.js +++ b/packages/@react-aria/dnd/test/useDraggableCollection.test.js @@ -313,6 +313,35 @@ describe('useDraggableCollection', () => { expect(cells).toHaveLength(2); expect(cells.map(c => c.textContent)).toEqual(['Foo', 'Baz']); }); + + it('should pass the dragged key as second argument to getItems', async () => { + let getItems = jest.fn().mockImplementation((keys) => { + return [...keys].map(key => ({'text/plain': key})); + }); + + let tree = render( + + + + ); + + let grid = tree.getByRole('grid'); + let cells = within(grid).getAllByRole('gridcell'); + expect(cells).toHaveLength(3); + + // Select 'foo' and 'bar' + await user.click(cells[0]); + await user.click(cells[1]); + + let dataTransfer = new DataTransfer(); + // Start dragging 'bar' + fireEvent(cells[1], new DragEvent('dragstart', {dataTransfer, clientX: 0, clientY: 0})); + + expect(getItems).toHaveBeenCalledTimes(1); + let [keysArg, draggedKeyArg] = getItems.mock.calls[0]; + expect(keysArg).toEqual(new Set(['foo', 'bar'])); + expect(draggedKeyArg).toBe('bar'); + }); }); describe('keyboard', () => { diff --git a/packages/@react-spectrum/dnd/src/useDragAndDrop.ts b/packages/@react-spectrum/dnd/src/useDragAndDrop.ts index e69e3c73702..ce2babd0d1f 100644 --- a/packages/@react-spectrum/dnd/src/useDragAndDrop.ts +++ b/packages/@react-spectrum/dnd/src/useDragAndDrop.ts @@ -65,7 +65,12 @@ export interface DragAndDropOptions extends Omit [] */ - getItems?: (keys: Set) => DragItem[], + getItems?: ( + /** The set of keys that can be affected by the drag (e.g. the current selection). */ + keys: Set, + /** The key of the item the user actually dragged. */ + draggedKey: Key + ) => DragItem[], /** Provide a custom drag preview. `draggedKey` represents the key of the item the user actually dragged. */ renderPreview?: (keys: Set, draggedKey: Key) => JSX.Element } diff --git a/packages/@react-stately/dnd/src/useDraggableCollectionState.ts b/packages/@react-stately/dnd/src/useDraggableCollectionState.ts index 5114fbe22a6..5e23743e81b 100644 --- a/packages/@react-stately/dnd/src/useDraggableCollectionState.ts +++ b/packages/@react-stately/dnd/src/useDraggableCollectionState.ts @@ -118,7 +118,7 @@ export function useDraggableCollectionState(props: DraggableCollectionStateOptio }, getKeysForDrag: getKeys, getItems(key) { - return getItems(getKeys(key)); + return getItems(getKeys(key), key); }, isDisabled, preview, diff --git a/packages/@react-types/shared/src/dnd.d.ts b/packages/@react-types/shared/src/dnd.d.ts index 2fedddd5fa9..96d886973be 100644 --- a/packages/@react-types/shared/src/dnd.d.ts +++ b/packages/@react-types/shared/src/dnd.d.ts @@ -285,7 +285,12 @@ export interface DraggableCollectionProps { /** Handler that is called when the drag operation is ended, either as a result of a drop or a cancellation. */ onDragEnd?: (e: DraggableCollectionEndEvent) => void, /** A function that returns the items being dragged. */ - getItems: (keys: Set) => DragItem[], + getItems: ( + /** The set of keys that can be affected by the drag (e.g. the current selection). */ + keys: Set, + /** The key of the item the user actually dragged. */ + draggedKey: Key + ) => DragItem[], /** The ref of the element that will be rendered as the drag preview while dragging. */ preview?: RefObject, /** Function that returns the drop operations that are allowed for the dragged items. If not provided, all drop operations are allowed. */ diff --git a/packages/react-aria-components/src/useDragAndDrop.tsx b/packages/react-aria-components/src/useDragAndDrop.tsx index bf59fd687b7..fb1e98c91a2 100644 --- a/packages/react-aria-components/src/useDragAndDrop.tsx +++ b/packages/react-aria-components/src/useDragAndDrop.tsx @@ -76,7 +76,12 @@ export interface DragAndDropOptions extends Omit [] */ - getItems?: (keys: Set) => DragItem[], + getItems?: ( + /** The set of keys that can be affected by the drag (e.g. the current selection). */ + keys: Set, + /** The key of the item the user actually dragged. */ + draggedKey: Key + ) => DragItem[], /** * A function that renders a drag preview, which is shown under the user's cursor while dragging. * By default, a copy of the dragged element is rendered.