Skip to content

Commit 682c6ab

Browse files
authored
feat(FileUploader): expose ref to clear files (#18267)
1 parent 8acd725 commit 682c6ab

File tree

2 files changed

+25
-39
lines changed

2 files changed

+25
-39
lines changed

packages/react/src/components/FileUploader/FileUploader.tsx

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77

88
import classNames from 'classnames';
99
import PropTypes from 'prop-types';
10-
import React, { useState, ForwardedRef, ReactElement } from 'react';
10+
import React, { useState, ForwardedRef, useImperativeHandle } from 'react';
1111
import Filename from './Filename';
1212
import FileUploaderButton from './FileUploaderButton';
13-
import { ButtonKinds } from '../../prop-types/types';
13+
import { ButtonKinds } from '../Button/Button';
1414
import { keys, matches } from '../../internal/keyboard';
1515
import { usePrefix } from '../../internal/usePrefix';
1616
import { ReactAttr } from '../../types/common';
@@ -107,8 +107,15 @@ export interface FileUploaderProps extends ReactAttr<HTMLSpanElement> {
107107
size?: 'sm' | 'small' | 'md' | 'field' | 'lg';
108108
}
109109

110+
export interface FileUploaderHandle {
111+
/**
112+
* Clear internal state
113+
*/
114+
clearFiles: () => void;
115+
}
116+
110117
const FileUploader = React.forwardRef(
111-
<ItemType,>(
118+
(
112119
{
113120
accept,
114121
buttonKind,
@@ -127,15 +134,14 @@ const FileUploader = React.forwardRef(
127134
size,
128135
...other
129136
}: FileUploaderProps,
130-
ref: ForwardedRef<HTMLButtonElement>
137+
ref: ForwardedRef<FileUploaderHandle>
131138
) => {
132139
const fileUploaderInstanceId = useId('file-uploader');
133140
const [state, updateState] = useState({
134141
fileNames: [] as (string | undefined)[],
135142
});
136143
const nodes: HTMLElement[] = [];
137144
const prefix = usePrefix();
138-
139145
const handleChange = (evt) => {
140146
evt.stopPropagation();
141147
const filenames = Array.prototype.map.call(
@@ -169,10 +175,11 @@ const FileUploader = React.forwardRef(
169175
}
170176
};
171177

172-
const clearFiles = () => {
173-
// A clearFiles function that resets filenames and can be referenced using a ref by the parent.
174-
updateState({ fileNames: [] });
175-
};
178+
useImperativeHandle(ref, () => ({
179+
clearFiles() {
180+
updateState({ fileNames: [] });
181+
},
182+
}));
176183

177184
const uploaderButton = React.createRef<HTMLLabelElement>();
178185
const classes = classNames({
@@ -255,12 +262,7 @@ const FileUploader = React.forwardRef(
255262
</div>
256263
);
257264
}
258-
) as {
259-
<ItemType>(props: FileUploaderProps): ReactElement;
260-
propTypes?: any;
261-
contextTypes?: any;
262-
defaultProps?: any;
263-
};
265+
);
264266

265267
FileUploader.propTypes = {
266268
/**
@@ -342,6 +344,6 @@ FileUploader.propTypes = {
342344
* sizes.
343345
*/
344346
size: PropTypes.oneOf(['sm', 'md', 'lg']),
345-
};
347+
} as PropTypes.ValidationMap<FileUploaderProps>;
346348

347349
export default FileUploader;

packages/react/src/components/FileUploader/__tests__/FileUploader-test.js

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,38 +50,22 @@ describe('FileUploader', () => {
5050

5151
it('should clear all uploaded files when `clearFiles` is called on a ref', () => {
5252
const ref = React.createRef();
53-
const onClick = jest.fn();
54-
let requiredProps1 = {
55-
...requiredProps,
56-
filenameStatus: 'edit',
57-
};
58-
const fileUpload = render(
59-
<FileUploader {...requiredProps1} ref={ref} onClick={onClick} />
60-
);
53+
const { container } = render(<FileUploader {...requiredProps} ref={ref} />);
6154
// eslint-disable-next-line testing-library/no-container, testing-library/no-node-access
62-
const input = fileUpload.container.querySelector('input');
55+
const input = container.querySelector('input');
6356

6457
const filename = 'test.png';
6558
act(() => {
6659
uploadFiles(input, [new File(['test'], filename, { type: 'image/png' })]);
6760
});
68-
expect(getByText(fileUpload.container, filename)).toBeInstanceOf(
69-
HTMLElement
70-
);
71-
72-
const onDelete = jest.fn();
73-
const description = 'test-description';
74-
// eslint-disable-next-line testing-library/render-result-naming-convention
7561

76-
let removeFile = getByLabel(
77-
fileUpload.container,
78-
'test description - test.png'
79-
);
62+
// eslint-disable-next-line testing-library/prefer-screen-queries
63+
expect(getByText(container, filename)).toBeInstanceOf(HTMLElement);
8064
act(() => {
81-
Simulate.click(removeFile);
65+
ref.current.clearFiles();
8266
});
83-
84-
expect(onClick).toHaveBeenCalledTimes(1);
67+
// eslint-disable-next-line testing-library/prefer-screen-queries
68+
expect(getByText(container, filename)).not.toBeInstanceOf(HTMLElement);
8569
});
8670

8771
it('should synchronize the filename status state when its prop changes', () => {

0 commit comments

Comments
 (0)