diff --git a/packages/@react-aria/dnd/src/useDroppableCollection.ts b/packages/@react-aria/dnd/src/useDroppableCollection.ts index 555c75e9380..8d7c90b27ab 100644 --- a/packages/@react-aria/dnd/src/useDroppableCollection.ts +++ b/packages/@react-aria/dnd/src/useDroppableCollection.ts @@ -251,9 +251,9 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state: state.selectionManager.isSelectionEqual(prevSelectedKeys) ) { let newKeys = new Set(); - for (let key of state.collection.getKeys()) { - if (!prevCollection.getItem(key)) { - newKeys.add(key); + for (let item of state.collection) { + if (item.type === 'item' && !prevCollection.getItem(item.key)) { + newKeys.add(item.key); } } diff --git a/packages/react-aria-components/test/Tree.test.tsx b/packages/react-aria-components/test/Tree.test.tsx index 6346062331b..28381047f1f 100644 --- a/packages/react-aria-components/test/Tree.test.tsx +++ b/packages/react-aria-components/test/Tree.test.tsx @@ -24,7 +24,8 @@ import {useTreeData} from 'react-stately'; let { EmptyTreeStaticStory: EmptyLoadingTree, - LoadingStoryDepOnTopStory: LoadingMoreTree + LoadingStoryDepOnTopStory: LoadingMoreTree, + TreeWithDragAndDrop } = composeStories(stories); let onSelectionChange = jest.fn(); @@ -1924,6 +1925,60 @@ describe('Tree', () => { expect(onRootDrop).toHaveBeenCalledTimes(1); }); + it('should automatically focus the newly added dropped item', async () => { + const {getAllByRole} = render(); + + const trees = getAllByRole('treegrid'); + const firstTreeRows = within(trees[0]).getAllByRole('row'); + const dataTransfer = new DataTransfer(); + + fireEvent(firstTreeRows[1], new DragEvent('dragstart', {dataTransfer, clientX: 5, clientY: 5})); + act(() => jest.runAllTimers()); + + fireEvent(trees[1], new DragEvent('dragenter', {dataTransfer, clientX: 50, clientY: 50})); + fireEvent(trees[1], new DragEvent('dragover', {dataTransfer, clientX: 50, clientY: 50})); + expect(trees[1]).toHaveAttribute('data-drop-target', 'true'); + + // ¯\_(ツ)_/¯ + await act(async () => fireEvent(trees[1], new DragEvent('drop', {dataTransfer, clientX: 50, clientY: 50}))); + act(() => jest.runAllTimers()); + + let secondTreeRows = within(trees[1]).getAllByRole('row'); + + expect(secondTreeRows).toHaveLength(1); + // The newly added row in the second tree should be the active element + expect(secondTreeRows[0]).toBe(document.activeElement); + + await user.click(document.body); + act(() => jest.runAllTimers()); + + await user.tab(); + await user.keyboard('{ArrowRight}'); // expand the projects item + await user.keyboard('{ArrowRight}'); + await user.keyboard('{Enter}'); + act(() => jest.runAllTimers()); + + await user.tab(); + act(() => jest.runAllTimers()); + + secondTreeRows = within(trees[1]).getAllByRole('row'); + expect(secondTreeRows).toHaveLength(4); + expect(within(secondTreeRows[3]).getAllByRole('button')[0]).toHaveAttribute('aria-label', 'Insert after Reports'); + expect(document.activeElement).toBe(within(secondTreeRows[3]).getAllByRole('button')[0]); + expect(secondTreeRows[3]).toHaveAttribute('data-drop-target', 'true'); + + await user.keyboard('{ArrowUp}'); + expect(within(secondTreeRows[2]).getAllByRole('button')[0]).toHaveAttribute('aria-label', 'Drop on Reports'); + expect(document.activeElement).toBe(within(secondTreeRows[2]).getAllByRole('button')[0]); + + await user.keyboard('{Enter}'); + act(() => jest.runAllTimers()); + + secondTreeRows = within(trees[1]).getAllByRole('row'); + expect(secondTreeRows).toHaveLength(1); + expect(secondTreeRows[0]).toBe(document.activeElement); + }); + it('should support disabled drag and drop', async () => { let {getByRole, queryAllByRole} = render(