-
-
Notifications
You must be signed in to change notification settings - Fork 298
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
207 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import React, { ReactElement } from "react"; | ||
import cn from "classnames"; | ||
import { fireEvent, render } from "@testing-library/react"; | ||
|
||
import { DropzoneHanders, useDropzone } from "../useDropzone"; | ||
|
||
function Test(props: DropzoneHanders<HTMLElement>): ReactElement { | ||
const [isOver, handlers] = useDropzone(props); | ||
|
||
return ( | ||
<div | ||
data-testid="dropzone" | ||
{...handlers} | ||
className={cn(isOver && "over")} | ||
/> | ||
); | ||
} | ||
|
||
describe("useDropzone", () => { | ||
it("should work correctly", () => { | ||
const onDragOver = jest.fn(); | ||
const onDragEnter = jest.fn(); | ||
const onDragLeave = jest.fn(); | ||
const onDrop = jest.fn(); | ||
|
||
const { getByTestId } = render( | ||
<Test | ||
onDragLeave={onDragLeave} | ||
onDragEnter={onDragEnter} | ||
onDragOver={onDragOver} | ||
onDrop={onDrop} | ||
/> | ||
); | ||
const dropzone = getByTestId("dropzone"); | ||
expect(dropzone).not.toHaveClass("over"); | ||
expect(onDragOver).not.toBeCalled(); | ||
expect(onDragEnter).not.toBeCalled(); | ||
expect(onDragLeave).not.toBeCalled(); | ||
expect(onDrop).not.toBeCalled(); | ||
|
||
fireEvent.dragEnter(dropzone); | ||
expect(dropzone).toHaveClass("over"); | ||
expect(onDragOver).not.toBeCalled(); | ||
expect(onDragEnter).toBeCalled(); | ||
expect(onDragLeave).not.toBeCalled(); | ||
expect(onDrop).not.toBeCalled(); | ||
|
||
fireEvent.dragLeave(dropzone); | ||
expect(dropzone).not.toHaveClass("over"); | ||
expect(onDragOver).not.toBeCalled(); | ||
expect(onDragEnter).toBeCalled(); | ||
expect(onDragLeave).toBeCalled(); | ||
expect(onDrop).not.toBeCalled(); | ||
|
||
fireEvent.dragOver(dropzone); | ||
expect(dropzone).toHaveClass("over"); | ||
expect(onDragOver).toBeCalled(); | ||
expect(onDragEnter).toBeCalled(); | ||
expect(onDragLeave).toBeCalled(); | ||
expect(onDrop).not.toBeCalled(); | ||
|
||
fireEvent.drop(dropzone); | ||
expect(dropzone).not.toHaveClass("over"); | ||
expect(onDragOver).toBeCalled(); | ||
expect(onDragEnter).toBeCalled(); | ||
expect(onDragLeave).toBeCalled(); | ||
expect(onDrop).toBeCalled(); | ||
}); | ||
|
||
it("should prevent default and stop propagation", () => { | ||
const onDragOver = jest.fn(); | ||
const onDragEnter = jest.fn(); | ||
const onDragLeave = jest.fn(); | ||
const onDrop = jest.fn(); | ||
const { getByTestId } = render( | ||
<div | ||
data-testid="container" | ||
onDragLeave={onDragLeave} | ||
onDragEnter={onDragEnter} | ||
onDragOver={onDragOver} | ||
onDrop={onDrop} | ||
> | ||
<Test /> | ||
</div> | ||
); | ||
|
||
const dropzone = getByTestId("dropzone"); | ||
fireEvent.dragEnter(dropzone); | ||
fireEvent.dragLeave(dropzone); | ||
fireEvent.dragOver(dropzone); | ||
fireEvent.drop(dropzone); | ||
|
||
expect(onDragOver).not.toBeCalled(); | ||
expect(onDragEnter).not.toBeCalled(); | ||
expect(onDragLeave).not.toBeCalled(); | ||
expect(onDrop).not.toBeCalled(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
import { DragEvent, HTMLAttributes, useCallback, useState } from "react"; | ||
|
||
/** @remarks \@since 2.9.0 */ | ||
export type DropzoneHanders<E extends HTMLElement> = Pick< | ||
HTMLAttributes<E>, | ||
"onDragEnter" | "onDragOver" | "onDrop" | "onDragLeave" | ||
>; | ||
|
||
/** @remarks \@since 2.9.0 */ | ||
export type DropzoneHookReturnValue<E extends HTMLElement> = [ | ||
boolean, | ||
DropzoneHanders<E> | ||
]; | ||
|
||
/** | ||
* This hook can be used to implement simple drag-and-drop behavior for file | ||
* uploads or special styles while dragging an element over a part of a page. | ||
* | ||
* @example | ||
* Simple File | ||
* ```ts | ||
* const style: CSSProperties = { | ||
* border: '1px solid blue', | ||
* }; | ||
* | ||
* function Example(): ReactElement { | ||
* const { onDrop } = useFileUpload() | ||
* const [isOver, handlers] = useDropzone({ | ||
* onDrop: (event) => { | ||
* // normally use the `onDrop` behavior from `useFileUpload` to upload | ||
* // files: | ||
* // onDrop(event); | ||
* } | ||
* }); | ||
* | ||
* return ( | ||
* <div {...handlers} style={isOver ? style : {}}> | ||
* Drag and drop some files! | ||
* {isOver && <UploadSVGIcon />} | ||
* </div> | ||
* ); | ||
* } | ||
* ``` | ||
* | ||
* @see {@link useFileUpload} for a more complex example | ||
* @param options - The {@link DropzoneHanders} that can be merged with the | ||
* default functionality. | ||
* @returns the {@link DropzoneHookReturnValue} | ||
* @remarks \@since 2.9.0 | ||
*/ | ||
export function useDropzone<E extends HTMLElement>( | ||
options: DropzoneHanders<E> | ||
): DropzoneHookReturnValue<E> { | ||
const { | ||
onDragEnter: propOnDragEnter, | ||
onDragOver: propOnDragOver, | ||
onDragLeave: propOnDragLeave, | ||
onDrop: propOnDrop, | ||
} = options; | ||
const [isOver, setOver] = useState(false); | ||
|
||
const onDragOver = useCallback( | ||
(event: DragEvent<E>) => { | ||
propOnDragOver?.(event); | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
setOver(true); | ||
}, | ||
[propOnDragOver] | ||
); | ||
const onDragEnter = useCallback( | ||
(event: DragEvent<E>) => { | ||
propOnDragEnter?.(event); | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
setOver(true); | ||
}, | ||
[propOnDragEnter] | ||
); | ||
const onDrop = useCallback( | ||
(event: DragEvent<E>) => { | ||
propOnDrop?.(event); | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
setOver(false); | ||
}, | ||
[propOnDrop] | ||
); | ||
const onDragLeave = useCallback( | ||
(event: DragEvent<E>) => { | ||
propOnDragLeave?.(event); | ||
event.preventDefault(); | ||
event.stopPropagation(); | ||
setOver(false); | ||
}, | ||
[propOnDragLeave] | ||
); | ||
|
||
return [ | ||
isOver, | ||
{ | ||
onDragOver, | ||
onDragEnter, | ||
onDrop, | ||
onDragLeave, | ||
}, | ||
]; | ||
} |