@@ -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