Skip to content

Commit e8c3052

Browse files
committed
fix: correctly update focus to the first inserted item after a drop in Tree
1 parent ee49fdb commit e8c3052

2 files changed

Lines changed: 48 additions & 1 deletion

File tree

packages/@react-aria/dnd/src/useDroppableCollection.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,15 @@ export function useDroppableCollection(props: DroppableCollectionOptions, state:
273273
first = item.parentKey;
274274
}
275275

276+
if (item?.type === 'content') {
277+
for (const node of state.collection) {
278+
if (node.type === 'item' && newKeys.has(node.key)) {
279+
first = node.key;
280+
break;
281+
}
282+
}
283+
}
284+
276285
// eslint-disable-next-line max-depth
277286
if (first != null) {
278287
state.selectionManager.setFocusedKey(first);

packages/react-aria-components/test/Tree.test.tsx

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {useTreeData} from 'react-stately';
2424

2525
let {
2626
EmptyTreeStaticStory: EmptyLoadingTree,
27-
LoadingStoryDepOnTopStory: LoadingMoreTree
27+
LoadingStoryDepOnTopStory: LoadingMoreTree,
28+
TreeWithDragAndDrop
2829
} = composeStories(stories);
2930

3031
let onSelectionChange = jest.fn();
@@ -1886,6 +1887,43 @@ describe('Tree', () => {
18861887
expect(onRootDrop).toHaveBeenCalledTimes(1);
18871888
});
18881889

1890+
it('should focus the first inserted item after a drop', async () => {
1891+
const {getAllByRole} = render(<TreeWithDragAndDrop />);
1892+
1893+
const trees = getAllByRole('treegrid');
1894+
let rows = within(trees[0]).getAllByRole('row');
1895+
const dataTransfer = new DataTransfer();
1896+
1897+
fireEvent(rows[0], new DragEvent('dragstart', {dataTransfer, clientX: 5, clientY: 5}));
1898+
act(() => jest.runAllTimers());
1899+
1900+
fireEvent(trees[1], new DragEvent('dragenter', {dataTransfer, clientX: 50, clientY: 50}));
1901+
fireEvent(trees[1], new DragEvent('dragover', {dataTransfer, clientX: 50, clientY: 50}));
1902+
expect(trees[1]).toHaveAttribute('data-drop-target', 'true');
1903+
1904+
await act(() => fireEvent(trees[1], new DragEvent('drop', {dataTransfer, clientX: 50, clientY: 50})));
1905+
act(() => jest.runAllTimers());
1906+
await user.click(document.body);
1907+
act(() => jest.runAllTimers());
1908+
1909+
await user.tab();
1910+
await user.keyboard('{ArrowRight}');
1911+
await user.keyboard('{ArrowRight}');
1912+
await user.keyboard('{Enter}');
1913+
act(() => jest.runAllTimers());
1914+
await user.tab();
1915+
act(() => jest.runAllTimers());
1916+
1917+
rows = within(trees[1]).getAllByRole('row');
1918+
expect(rows).toHaveLength(4);
1919+
expect(within(rows[3]).getAllByRole('button')[0]).toHaveAttribute('aria-label', 'Insert after Projects');
1920+
expect(document.activeElement).toBe(within(rows[3]).getAllByRole('button')[0]);
1921+
expect(rows[3]).toHaveAttribute('data-drop-target', 'true');
1922+
1923+
await user.keyboard('{Enter}');
1924+
act(() => jest.runAllTimers());
1925+
});
1926+
18891927
it('should support disabled drag and drop', async () => {
18901928
let {getByRole, queryAllByRole} = render(
18911929
<DraggableTree isDisabled />

0 commit comments

Comments
 (0)