Skip to content

Commit 6a858e3

Browse files
author
Michael Jordan
authored
fix(#3842): DnD Nested Drop Regions example can't focus child drop target (#3843)
1 parent 7417956 commit 6a858e3

File tree

2 files changed

+141
-1
lines changed

2 files changed

+141
-1
lines changed

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,10 @@ class DragSession {
258258
return;
259259
}
260260

261-
let dropTarget = this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
261+
let dropTarget =
262+
this.validDropTargets.find(target => target.element === e.target as HTMLElement) ||
263+
this.validDropTargets.find(target => target.element.contains(e.target as HTMLElement));
264+
262265
if (!dropTarget) {
263266
if (this.currentDropTarget) {
264267
this.currentDropTarget.element.focus();

packages/@react-aria/dnd/test/dnd.test.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,6 +1992,143 @@ describe('useDrag and useDrop', function () {
19921992
expect(onDropEnter2).toHaveBeenCalledTimes(1);
19931993
});
19941994
});
1995+
1996+
it('should support nested drop targets', async () => {
1997+
let onDragStart = jest.fn();
1998+
let onDragMove = jest.fn();
1999+
let onDragEnd = jest.fn();
2000+
let onDropEnter = jest.fn();
2001+
let onDropMove = jest.fn();
2002+
let onDropExit = jest.fn();
2003+
let onDrop = jest.fn();
2004+
let onDropEnter2 = jest.fn();
2005+
let onDropMove2 = jest.fn();
2006+
let onDropExit2 = jest.fn();
2007+
let onDrop2 = jest.fn();
2008+
let tree = render(<>
2009+
<Draggable onDragStart={onDragStart} onDragMove={onDragMove} onDragEnd={onDragEnd} />
2010+
<Droppable onDropEnter={onDropEnter} onDropMove={onDropMove} onDrop={onDrop} onDropExit={onDropExit}>
2011+
Drop here 1
2012+
<Droppable onDropEnter={onDropEnter2} onDropMove={onDropMove2} onDrop={onDrop2} onDropExit={onDropExit2}>Drop here 2</Droppable>
2013+
</Droppable>
2014+
</>);
2015+
2016+
let buttons = tree.getAllByRole('button');
2017+
let draggable = buttons[0];
2018+
let droppable = buttons[1];
2019+
let droppable2 = buttons[2];
2020+
2021+
expect(draggable).toHaveAttribute('data-dragging', 'false');
2022+
expect(droppable).toHaveAttribute('data-droptarget', 'false');
2023+
expect(droppable2).toHaveAttribute('data-droptarget', 'false');
2024+
2025+
userEvent.tab();
2026+
2027+
expect(document.activeElement).toBe(draggable);
2028+
expect(draggable).toHaveAttribute('aria-describedby');
2029+
expect(document.getElementById(draggable.getAttribute('aria-describedby'))).toHaveTextContent('Press Enter to start dragging');
2030+
2031+
fireEvent.keyDown(draggable, {key: 'Enter'});
2032+
fireEvent.keyUp(draggable, {key: 'Enter'});
2033+
2034+
expect(draggable).toHaveAttribute('data-dragging', 'true');
2035+
expect(onDragStart).toHaveBeenCalledTimes(1);
2036+
expect(onDragStart).toHaveBeenCalledWith({
2037+
type: 'dragstart',
2038+
x: 50,
2039+
y: 25
2040+
});
2041+
2042+
act(() => jest.runAllTimers());
2043+
expect(document.activeElement).toBe(droppable2);
2044+
expect(droppable2).toHaveAttribute('aria-describedby');
2045+
expect(document.getElementById(droppable2.getAttribute('aria-describedby'))).toHaveTextContent('Press Enter to drop');
2046+
expect(announce).toHaveBeenCalledWith('Started dragging. Press Tab to navigate to a drop target, then press Enter to drop, or press Escape to cancel.');
2047+
2048+
expect(onDropEnter2).toHaveBeenCalledTimes(1);
2049+
expect(onDropEnter).toHaveBeenCalledTimes(0);
2050+
expect(onDropEnter2).toHaveBeenCalledWith({
2051+
type: 'dropenter',
2052+
x: 50,
2053+
y: 25
2054+
});
2055+
2056+
expect(draggable).toHaveAttribute('data-dragging', 'true');
2057+
expect(droppable).toHaveAttribute('data-droptarget', 'false');
2058+
expect(droppable2).toHaveAttribute('data-droptarget', 'true');
2059+
2060+
userEvent.tab();
2061+
2062+
expect(document.activeElement).toBe(droppable);
2063+
expect(droppable).toHaveAttribute('aria-describedby');
2064+
expect(document.getElementById(droppable.getAttribute('aria-describedby'))).toHaveTextContent('Press Enter to drop');
2065+
2066+
expect(onDropExit2).toHaveBeenCalledTimes(1);
2067+
expect(onDropExit).not.toHaveBeenCalled();
2068+
expect(onDropExit2).toHaveBeenCalledWith({
2069+
type: 'dropexit',
2070+
x: 50,
2071+
y: 25
2072+
});
2073+
2074+
expect(onDropEnter).toHaveBeenCalledTimes(1);
2075+
expect(onDropEnter2).toHaveBeenCalledTimes(1);
2076+
expect(onDropEnter).toHaveBeenCalledWith({
2077+
type: 'dropenter',
2078+
x: 50,
2079+
y: 25
2080+
});
2081+
2082+
expect(draggable).toHaveAttribute('data-dragging', 'true');
2083+
expect(droppable).toHaveAttribute('data-droptarget', 'true');
2084+
expect(droppable2).toHaveAttribute('data-droptarget', 'false');
2085+
2086+
fireEvent.keyDown(droppable, {key: 'Enter'});
2087+
fireEvent.keyUp(droppable, {key: 'Enter'});
2088+
2089+
expect(document.activeElement).toBe(droppable);
2090+
expect(droppable).not.toHaveAttribute('aria-describedby');
2091+
expect(droppable2).not.toHaveAttribute('aria-describedby');
2092+
2093+
expect(onDrop2).not.toHaveBeenCalled();
2094+
expect(onDrop).toHaveBeenCalledTimes(1);
2095+
expect(onDrop).toHaveBeenCalledWith({
2096+
type: 'drop',
2097+
x: 50,
2098+
y: 25,
2099+
dropOperation: 'move',
2100+
items: [
2101+
{
2102+
kind: 'text',
2103+
types: new Set(['text/plain']),
2104+
getText: expect.any(Function)
2105+
}
2106+
]
2107+
});
2108+
2109+
expect(await onDrop.mock.calls[0][0].items[0].getText('text/plain')).toBe('hello world');
2110+
expect(announce).toHaveBeenCalledWith('Drop complete.');
2111+
2112+
expect(onDropExit).toHaveBeenCalledTimes(1);
2113+
expect(onDropExit2).toHaveBeenCalledTimes(1);
2114+
expect(onDropExit).toHaveBeenCalledWith({
2115+
type: 'dropexit',
2116+
x: 50,
2117+
y: 25
2118+
});
2119+
2120+
expect(onDragEnd).toHaveBeenCalledTimes(1);
2121+
expect(onDragEnd).toHaveBeenCalledWith({
2122+
type: 'dragend',
2123+
x: 50,
2124+
y: 25,
2125+
dropOperation: 'move'
2126+
});
2127+
2128+
expect(draggable).toHaveAttribute('data-dragging', 'false');
2129+
expect(droppable).toHaveAttribute('data-droptarget', 'false');
2130+
expect(droppable2).toHaveAttribute('data-droptarget', 'false');
2131+
});
19952132
});
19962133

19972134
describe('screen reader', () => {

0 commit comments

Comments
 (0)