From 182987c03185e5a11331d27502934fd7c06ef2a1 Mon Sep 17 00:00:00 2001 From: Nicole Dow Date: Mon, 24 Jan 2022 07:11:13 -0700 Subject: [PATCH] Issue #347 : Add remove button to default file input previews (#526) * show clear button for single file upload * formatting * tests for clear file input btn * make remove button for single file consistent with remove button for multiple files * update docs * run yarn docs manually * Add small note to migration guide Co-authored-by: Conor Hawes --- docs.md | 4 +-- migration-guides/v6.0.0.md | 35 +++++++++++++++++++---- src/forms/inputs/file-input/file-input.js | 6 ++-- test/forms/inputs/file-input.test.js | 12 ++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/docs.md b/docs.md index 20091fed..53352859 100644 --- a/docs.md +++ b/docs.md @@ -593,9 +593,9 @@ A component passed using `previewComponent` will receive the following props: - `multiple` **[Boolean][151]** A flag indicating whether or not to accept multiple files (optional, default `false`) - `accept` **[String][149]?** Value that defines the file types the file input should accept (e.g., ".doc,.docx"). More info: [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept][166] - `capture` **(`"user"` \| `"environment"`)?** Value that specifies which camera to use, if the accept attribute indicates the input type of image or video. This is not available for all devices (e.g., desktops). More info: [https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture][167] -- `onRemove` **[Function][150]** A callback fired when the file is removed (only available when `multiple` is set to `true`) (optional, default `noop`) +- `onRemove` **[Function][150]** A callback fired when a file is removed (optional, default `noop`) - `previewComponent` **[Function][150]** A custom component that is used to display a preview of each attached file (optional, default `RenderPreview`) -- `removeComponent` **[Function][150]** A custom component that receives `value` and `onRemove` props (only available when `multiple` is set to `true`) (optional, default `RemoveButton`) +- `removeComponent` **[Function][150]** A custom component that receives `value` and `onRemove` props (optional, default `RemoveButton`) - `thumbnail` **[String][149]?** A placeholder image to display before the file is loaded - `hidePreview` **[Boolean][151]** A flag indicating whether or not to hide the file preview (optional, default `false`) - `selectText` **[String][149]?** An override for customizing the text that is displayed on the input's label. Defaults to 'Select File' or 'Select File(s)' depending on the `multiple` prop value diff --git a/migration-guides/v6.0.0.md b/migration-guides/v6.0.0.md index f1d06f03..790fef37 100644 --- a/migration-guides/v6.0.0.md +++ b/migration-guides/v6.0.0.md @@ -8,9 +8,10 @@ This version contains the following breaking changes: 5. The `hideLabel` prop on `` has been renamed to `hideRangeValue` 6. `` and `` components now store an array of objects representing the file data as opposed to a string 7. `` and `` components no longer accept an `onLoad` prop -8. The `previewComponent` for a file input no longer receives a `value` prop and `file` is a file object (with the url) -9. The tag on `` now uses the class `spinner` in place of an id and supports additional classes -10. `` now expects both `options` and `value` as required props. +8. `` and `` components now default to allowing the user to remove a selected file +9. The `previewComponent` for a file input no longer receives a `value` prop and `file` is a file object (with the url) +10. The tag on `` now uses the class `spinner` in place of an id and supports additional classes +11. `` now expects both `options` and `value` as required props. The required changes for each item are detailed below. @@ -167,8 +168,30 @@ import { Field } from 'redux-form' }} /> ``` +## 8. `` and `` components now default to allowing the user to remove a selected file -## 8. The `previewComponent` for a file input no longer receives a `value` prop and `file` is a file object (with the url) +You may need to add styling to account for the new button that is available next to file previews. If you want to keep your application as-is (i.e., not showing a remove button), then you can specify a custom `removeComponent` that returns `null`. + +```jsx +// Before + + +// After +function EmptyRemoveComponent() { + return null +} + + +``` + +## 9. The `previewComponent` for a file input no longer receives a `value` prop and `file` is a file object (with the url) If you're passing in a custom `previewComponent`, you must modify your logic to only access the `value` object. @@ -196,7 +219,7 @@ const PreviewComponent = ({ file }) => { ``` -#### 9. The tag on `` now uses the `spinner` class in place of an id +## 10. The tag on `` now uses the `spinner` class in place of an id Replace any styling rules that target `#spinner` with `.spinner`. @@ -215,7 +238,7 @@ Replace any styling rules that target `#spinner` with `.spinner`. } } ``` -#### 10. `` now expects both `options` and `value` as required props. +## 11. `` now expects both `options` and `value` as required props. Make sure that any instances of `` in your application are already sending these two props. diff --git a/src/forms/inputs/file-input/file-input.js b/src/forms/inputs/file-input/file-input.js index e31d7e72..094dc196 100644 --- a/src/forms/inputs/file-input/file-input.js +++ b/src/forms/inputs/file-input/file-input.js @@ -34,9 +34,9 @@ import classnames from 'classnames' * @param {Boolean} [multiple=false] - A flag indicating whether or not to accept multiple files * @param {String} [accept] - Value that defines the file types the file input should accept (e.g., ".doc,.docx"). More info: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept * @param {("user"|"environment")} [capture] - Value that specifies which camera to use, if the accept attribute indicates the input type of image or video. This is not available for all devices (e.g., desktops). More info: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture - * @param {Function} [onRemove=noop] - A callback fired when the file is removed (only available when `multiple` is set to `true`) + * @param {Function} [onRemove=noop] - A callback fired when a file is removed * @param {Function} [previewComponent=RenderPreview] - A custom component that is used to display a preview of each attached file - * @param {Function} [removeComponent=RemoveButton] - A custom component that receives `value` and `onRemove` props (only available when `multiple` is set to `true`) + * @param {Function} [removeComponent=RemoveButton] - A custom component that receives `value` and `onRemove` props * @param {String} [thumbnail] - A placeholder image to display before the file is loaded * @param {Boolean} [hidePreview=false] - A flag indicating whether or not to hide the file preview * @param {String} [selectText] - An override for customizing the text that is displayed on the input's label. Defaults to 'Select File' or 'Select File(s)' depending on the `multiple` prop value @@ -160,7 +160,7 @@ function FileInput(props) { {files.map((file, idx) => (
- {multiple && file && removeFile(idx)} />} + {file && removeFile(idx)} />}
))} diff --git a/test/forms/inputs/file-input.test.js b/test/forms/inputs/file-input.test.js index 833fdc76..4e97ab1c 100644 --- a/test/forms/inputs/file-input.test.js +++ b/test/forms/inputs/file-input.test.js @@ -193,6 +193,18 @@ describe('FileInput', () => { expect(wrapper.find('button.remove-file').exists()).toBe(true) }) + test('shows a clear input button component when multiple prop is false and a file is selected', () => { + const props = { input: { name, value: [{ name: 'fileName', type: 'image/png' }], onChange: defaultOnChange }, meta: {}, multiple: false } + const wrapper = mount() + expect(wrapper.find('button.remove-file').exists()).toBe(true) + }) + + test('does not show a clear input button component when multiple prop is false and a file is not selected', () => { + const props = { input: { name, value: [], onChange: defaultOnChange }, meta: {}, multiple: false } + const wrapper = mount() + expect(wrapper.find('button.remove-file').exists()).toBe(false) + }) + test('adds custom aria-label to default remove button', () => { const file = { name: 'fileName.png', type: 'image/png' } const props = { input: { name, value: [file], onChange: defaultOnChange }, meta: {}, multiple: true }