Skip to content

feat(dnd): pass draggedKey into getItems #8448

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/@react-aria/dnd/stories/DraggableCollection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
19 changes: 19 additions & 0 deletions packages/@react-aria/dnd/stories/dnd.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -575,3 +576,21 @@ export const DroppableEnabledDisabledControl: DnDStoryObj = {
}
}
};

export const DraggedItemOnly: DnDStoryObj = {
render: () => (
<Flex direction="row" gap="size-200" alignItems="center" wrap>
<DraggableGridExampleStandalone
draggableCollectionStateProps={{
getItems: (_, draggedKey) => {
action('getItems')(draggedKey);
return [{
'text/plain': `Dragged item: ${draggedKey}`
}];
}
}} />
<Droppable />
</Flex>
),
name: 'Drag only dragged item'
};
29 changes: 29 additions & 0 deletions packages/@react-aria/dnd/test/useDraggableCollection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<Provider theme={theme}>
<DraggableCollectionExample draggableCollectionStateProps={{getItems}} />
</Provider>
);

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', () => {
Expand Down
7 changes: 6 additions & 1 deletion packages/@react-spectrum/dnd/src/useDragAndDrop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,12 @@ export interface DragAndDropOptions extends Omit<DraggableCollectionProps, 'prev
* A function that returns the items being dragged. If not specified, we assume that the collection is not draggable.
* @default () => []
*/
getItems?: (keys: Set<Key>) => DragItem[],
getItems?: (
/** The set of keys that can be affected by the drag (e.g. the current selection). */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the individual ones are useful for autofill. But they still don't appear in our docs. Are you seeing them somewhere? maybe I'm just looking in the wrong place
Screenshot 2025-06-26 at 9 34 21 am
https://reactspectrum.blob.core.windows.net/reactspectrum/a00898e3b00bd2f2ad3954a86d8e8df764b7623b/docs/react-aria/useDraggableCollection.html

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just in VSCode, not seeing them in the docs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we should probably add it to the main description body for the prop as well. I think leave in the more granular ones for VSCode though as well

keys: Set<Key>,
/** 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<Key>, draggedKey: Key) => JSX.Element
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function useDraggableCollectionState(props: DraggableCollectionStateOptio
},
getKeysForDrag: getKeys,
getItems(key) {
return getItems(getKeys(key));
return getItems(getKeys(key), key);
},
isDisabled,
preview,
Expand Down
7 changes: 6 additions & 1 deletion packages/@react-types/shared/src/dnd.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Key>) => DragItem[],
getItems: (
/** The set of keys that can be affected by the drag (e.g. the current selection). */
keys: Set<Key>,
/** 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<DragPreviewRenderer | null>,
/** Function that returns the drop operations that are allowed for the dragged items. If not provided, all drop operations are allowed. */
Expand Down
7 changes: 6 additions & 1 deletion packages/react-aria-components/src/useDragAndDrop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,12 @@ export interface DragAndDropOptions extends Omit<DraggableCollectionProps, 'prev
* A function that returns the items being dragged. If not specified, we assume that the collection is not draggable.
* @default () => []
*/
getItems?: (keys: Set<Key>) => DragItem[],
getItems?: (
/** The set of keys that can be affected by the drag (e.g. the current selection). */
keys: Set<Key>,
/** 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.
Expand Down