Skip to content

Commit 1b91e50

Browse files
committed
feat: add option to auto focus the root and close react-dropzone#1145
1 parent 7a2f405 commit 1b91e50

File tree

3 files changed

+67
-0
lines changed

3 files changed

+67
-0
lines changed

src/index.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ const defaultProps = {
6868
noDragEventsBubbling: false,
6969
validator: null,
7070
useFsAccessApi: true,
71+
autoFocus: false,
7172
};
7273

7374
Dropzone.defaultProps = defaultProps;
@@ -173,6 +174,11 @@ Dropzone.propTypes = {
173174
*/
174175
useFsAccessApi: PropTypes.bool,
175176

177+
/**
178+
* Set to true to focus the root element on render
179+
*/
180+
autoFocus: PropTypes.bool,
181+
176182
/**
177183
* Cb for when the `dragenter` event occurs.
178184
*
@@ -381,6 +387,7 @@ const initialState = {
381387
* @param {Function} [props.onFileDialogCancel] Cb for when closing the file dialog with no selection
382388
* @param {boolean} [props.useFsAccessApi] Set to true to use the https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API
383389
* to open the file picker instead of using an `<input type="file">` click event.
390+
* @param {boolean} autoFocus Set to true to auto focus the root element.
384391
* @param {Function} [props.onFileDialogOpen] Cb for when opening the file dialog
385392
* @param {dragCb} [props.onDragEnter] Cb for when the `dragenter` event occurs.
386393
* @param {dragCb} [props.onDragLeave] Cb for when the `dragleave` event occurs
@@ -433,6 +440,7 @@ export function useDropzone(props = {}) {
433440
onFileDialogCancel,
434441
onFileDialogOpen,
435442
useFsAccessApi,
443+
autoFocus,
436444
preventDropOnDocument,
437445
noClick,
438446
noKeyboard,
@@ -458,7 +466,12 @@ export function useDropzone(props = {}) {
458466
[onFileDialogCancel]
459467
);
460468

469+
/**
470+
* @constant
471+
* @type {React.MutableRefObject<HTMLElement>}
472+
*/
461473
const rootRef = useRef(null);
474+
462475
const inputRef = useRef(null);
463476

464477
const [state, dispatch] = useReducer(reducer, initialState);
@@ -518,6 +531,14 @@ export function useDropzone(props = {}) {
518531
};
519532
}, [rootRef, preventDropOnDocument]);
520533

534+
// Auto focus the root when autoFocus is true
535+
useEffect(() => {
536+
if (!disabled && autoFocus && rootRef.current) {
537+
rootRef.current.focus();
538+
}
539+
return () => {};
540+
}, [rootRef, autoFocus, disabled]);
541+
521542
const onErrCb = useCallback(
522543
(e) => {
523544
if (onError) {

src/index.spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1064,6 +1064,51 @@ describe("useDropzone() hook", () => {
10641064
fireEvent.focus(dropzone);
10651065
expect(dropzone.querySelector("#focus")).not.toBeNull();
10661066
});
1067+
1068+
it("{autoFocus} sets the focus state on render", () => {
1069+
const { container, rerender } = render(
1070+
<Dropzone>
1071+
{({ getRootProps, getInputProps, isFocused }) => (
1072+
<div {...getRootProps()}>
1073+
<input {...getInputProps()} />
1074+
{isFocused && <div id="focus" />}
1075+
</div>
1076+
)}
1077+
</Dropzone>
1078+
);
1079+
1080+
const dropzone = container.querySelector("div");
1081+
1082+
expect(dropzone.querySelector("#focus")).toBeNull();
1083+
1084+
rerender(
1085+
/* eslint-disable-next-line jsx-a11y/no-autofocus */
1086+
<Dropzone autoFocus>
1087+
{({ getRootProps, getInputProps, isFocused }) => (
1088+
<div {...getRootProps()}>
1089+
<input {...getInputProps()} />
1090+
{isFocused && <div id="focus" />}
1091+
</div>
1092+
)}
1093+
</Dropzone>
1094+
);
1095+
1096+
expect(dropzone.querySelector("#focus")).not.toBeNull();
1097+
1098+
rerender(
1099+
/* eslint-disable-next-line jsx-a11y/no-autofocus */
1100+
<Dropzone autoFocus disabled>
1101+
{({ getRootProps, getInputProps, isFocused }) => (
1102+
<div {...getRootProps()}>
1103+
<input {...getInputProps()} />
1104+
{isFocused && <div id="focus" />}
1105+
</div>
1106+
)}
1107+
</Dropzone>
1108+
);
1109+
1110+
expect(dropzone.querySelector("#focus")).toBeNull();
1111+
});
10671112
});
10681113

10691114
describe("onBlur", () => {

typings/react-dropzone.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export type DropzoneOptions = Pick<React.HTMLProps<HTMLElement>, PropTypes> & {
5252
onFileDialogOpen?: () => void;
5353
validator?: <T extends File>(file: T) => FileError | FileError[] | null;
5454
useFsAccessApi?: boolean;
55+
autoFocus?: boolean;
5556
};
5657

5758
export type DropEvent =

0 commit comments

Comments
 (0)