diff --git a/example/src/demos/FileDragDrop.tsx b/example/src/demos/FileDragDrop.tsx index aceafb0880..bb8407f422 100644 --- a/example/src/demos/FileDragDrop.tsx +++ b/example/src/demos/FileDragDrop.tsx @@ -24,14 +24,21 @@ export default function Box() { return ( console.log('drop missed!')} - onDragOverMissed={(e) => setActiveBg(1)}> + onDropMissed={(e) => { + console.log('drop missed!') + setActiveBg(0) + }} + onDragOverMissed={(e) => setActiveBg(1)} + onDragLeave={() => setActiveBg(0)}> console.log('dropped!', e)} + onDrop={(e) => { + console.log('dropped!') + setActive(0) + }} onDragOverEnter={() => { setActive(1) setActiveBg(0) diff --git a/packages/fiber/src/core/events.ts b/packages/fiber/src/core/events.ts index 3d62dfd81c..a16d372b32 100644 --- a/packages/fiber/src/core/events.ts +++ b/packages/fiber/src/core/events.ts @@ -41,9 +41,12 @@ export type Events = { onClick: EventListener onContextMenu: EventListener onDoubleClick: EventListener + onDragEnter: EventListener + onDragLeave: EventListener onDragOverEnter: EventListener onDragOverLeave: EventListener onDrop: EventListener + onDropMissed: EventListener onWheel: EventListener onPointerDown: EventListener onPointerUp: EventListener @@ -57,6 +60,8 @@ export type EventHandlers = { onClick?: (event: ThreeEvent) => void onContextMenu?: (event: ThreeEvent) => void onDoubleClick?: (event: ThreeEvent) => void + onDragEnter?: (event: ThreeEvent) => void + onDragLeave?: (event: ThreeEvent) => void onDragOverEnter?: (event: ThreeEvent) => void onDragOverLeave?: (event: ThreeEvent) => void onDragOverMissed?: (event: DragEvent) => void @@ -113,6 +118,8 @@ export function getEventPriority() { case 'click': case 'contextmenu': case 'dblclick': + case 'dragenter': + case 'dragleave': case 'drop': case 'pointercancel': case 'pointerdown': @@ -186,7 +193,9 @@ export function createEvents(store: UseBoundStore) { ['Move', 'Over', 'Enter', 'Out', 'Leave'].some( (name) => (obj as unknown as Instance).__r3f?.handlers[('onPointer' + name) as keyof EventHandlers], ) || - ['Over'].some((name) => (obj as unknown as Instance).__r3f?.handlers[('onDrag' + name) as keyof EventHandlers]), + ['Over', 'Enter', 'Leave'].some( + (name) => (obj as unknown as Instance).__r3f?.handlers[('onDrag' + name) as keyof EventHandlers], + ), ) } @@ -401,6 +410,7 @@ export function createEvents(store: UseBoundStore) { switch (name) { case 'onPointerLeave': case 'onPointerCancel': + case 'onDragLeave': return () => cancelPointer([]) case 'onLostPointerCapture': return (event: DomEvent) => { @@ -423,7 +433,7 @@ export function createEvents(store: UseBoundStore) { // Get fresh intersects const isPointerMove = name === 'onPointerMove' - const isDragOver = name === 'onDragOverEnter' || name === 'onDragOverLeave' + const isDragOver = name === 'onDragOver' const isDrop = name === 'onDrop' const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick' const filter = isPointerMove ? filterPointerEvents : undefined diff --git a/packages/fiber/src/web/events.ts b/packages/fiber/src/web/events.ts index e6a413eb86..5e7da7cc5d 100644 --- a/packages/fiber/src/web/events.ts +++ b/packages/fiber/src/web/events.ts @@ -6,8 +6,9 @@ const DOM_EVENTS = { onClick: ['click', false], onContextMenu: ['contextmenu', false], onDoubleClick: ['dblclick', false], - onDragOverEnter: ['dragover', false], - onDragOverLeave: ['dragover', false], + onDragEnter: ['dragenter', false], + onDragLeave: ['dragleave', false], + onDragOver: ['dragover', false], onDrop: ['drop', false], onWheel: ['wheel', true], onPointerDown: ['pointerdown', true], diff --git a/packages/fiber/tests/core/events.test.tsx b/packages/fiber/tests/core/events.test.tsx index b933bf3a2d..ccb4c4eb25 100644 --- a/packages/fiber/tests/core/events.test.tsx +++ b/packages/fiber/tests/core/events.test.tsx @@ -210,7 +210,7 @@ describe('events', () => { expect(handlePointerOut).toHaveBeenCalled() }) - it('can handle onDragOverEnter & onDragOverLeave', async () => { + it('can handle dragover events via onDragOverEnter & onDragOverLeave', async () => { const handleDragOverEnter = jest.fn() const handleDragOverLeave = jest.fn() @@ -226,7 +226,6 @@ describe('events', () => { }) // Note: DragEvent is not implemented in jsdom yet: https://github.com/jsdom/jsdom/issues/2913 - // https://developer.mozilla.org/en-US/docs/Web/API/DragEvent // however, @react-testing/library does simulate it let evt = createEvent.dragOver(getContainer()) //@ts-ignore @@ -248,12 +247,80 @@ describe('events', () => { expect(handleDragOverLeave).toHaveBeenCalled() }) - it('can handle onDrop', async () => { + it('can handle onDragOverMissed', async () => { + const handleDragOverMissed = jest.fn() + + await act(async () => { + render( + + + + + + , + ) + }) + + // Note: DragEvent is not implemented in jsdom yet: https://github.com/jsdom/jsdom/issues/2913 + // https://developer.mozilla.org/en-US/docs/Web/API/DragEvent + // however, @react-testing/library does simulate it + let evt = createEvent.dragOver(getContainer()) + //@ts-ignore + evt.offsetX = 1 + //@ts-ignore + evt.offsetY = 1 + + fireEvent(getContainer(), evt) + + expect(handleDragOverMissed).toHaveBeenCalled() + }) + + it('can handle onDragEnter & onDragLeave', async () => { + const handleDragEnter = jest.fn() + const handleDragLeave = jest.fn() + + await act(async () => { + render( + + + + + + , + ) + }) + + // Note: DragEvent is not implemented in jsdom yet: https://github.com/jsdom/jsdom/issues/2913 + // https://developer.mozilla.org/en-US/docs/Web/API/DragEvent + // however, @react-testing/library does simulate it + let evt = createEvent.dragEnter(getContainer()) + //@ts-ignore + evt.offsetX = 10 + //@ts-ignore + evt.offsetY = 10 + + fireEvent(getContainer(), evt) + + expect(handleDragEnter).toHaveBeenCalled() + + evt = createEvent.dragLeave(getContainer()) + //@ts-ignore + evt.offsetX = 0 + //@ts-ignore + evt.offsetY = 0 + + fireEvent(getContainer(), evt) + + expect(handleDragLeave).toHaveBeenCalled() + }) + + it('can handle onDrop & onDropMissed', async () => { const handleOnDrop = jest.fn() + const handleOnDropMissed = jest.fn() await act(async () => { render( - + @@ -263,7 +330,6 @@ describe('events', () => { }) // Note: DragEvent is not implemented in jsdom yet: https://github.com/jsdom/jsdom/issues/2913 - // https://developer.mozilla.org/en-US/docs/Web/API/DragEvent // however, @react-testing/library does simulate it let evt = createEvent.drop(getContainer()) //@ts-ignore @@ -284,6 +350,7 @@ describe('events', () => { // second event shouldn't register expect(handleOnDrop).toHaveBeenCalledTimes(1) + expect(handleOnDropMissed).toHaveBeenCalled() }) it('should handle stopPropagation', async () => {