Skip to content

Commit

Permalink
Issue #347 : Add remove button to default file input previews (#526)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
nicoledow and chawes13 authored Jan 24, 2022
1 parent 9690ac7 commit 182987c
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 11 deletions.
4 changes: 2 additions & 2 deletions docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 29 additions & 6 deletions migration-guides/v6.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ This version contains the following breaking changes:
5. The `hideLabel` prop on `<RangeInput />` has been renamed to `hideRangeValue`
6. `<CloudinaryFileInput />` and `<FileInput />` components now store an array of objects representing the file data as opposed to a string
7. `<CloudinaryFileInput />` and `<FileInput />` 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 `<Spinner />` now uses the class `spinner` in place of an id and supports additional classes
10. `<TabBar />` now expects both `options` and `value` as required props.
8. `<CloudinaryFileInput />` and `<FileInput />` 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 `<Spinner />` now uses the class `spinner` in place of an id and supports additional classes
11. `<TabBar />` now expects both `options` and `value` as required props.

The required changes for each item are detailed below.

Expand Down Expand Up @@ -167,8 +168,30 @@ import { Field } from 'redux-form'
}}
/>
```
## 8. `<CloudinaryFileInput />` and `<FileInput />` 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
<Field
name="profilePhoto"
component={FileInput}
/>

// After
function EmptyRemoveComponent() {
return null
}

<Field
name="profilePhoto"
component={FileInput}
removeComponent={EmptyRemoveComponent}
/>
```

## 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.

Expand Down Expand Up @@ -196,7 +219,7 @@ const PreviewComponent = ({ file }) => {
<FileInput previewComponent={PreviewComponent} />
```

#### 9. The tag on `<Spinner />` now uses the `spinner` class in place of an id
## 10. The tag on `<Spinner />` now uses the `spinner` class in place of an id

Replace any styling rules that target `#spinner` with `.spinner`.

Expand All @@ -215,7 +238,7 @@ Replace any styling rules that target `#spinner` with `.spinner`.
}
}
```
#### 10. `<TabBar />` now expects both `options` and `value` as required props.
## 11. `<TabBar />` now expects both `options` and `value` as required props.

Make sure that any instances of `<TabBar />` in your application are already sending these two props.

Expand Down
6 changes: 3 additions & 3 deletions src/forms/inputs/file-input/file-input.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -160,7 +160,7 @@ function FileInput(props) {
{files.map((file, idx) => (
<div key={file?.name || idx} className="fileupload-preview-container">
<RenderPreview file={file} thumbnail={thumbnail} {...rest} />
{multiple && file && <RemoveComponent file={file} onRemove={() => removeFile(idx)} />}
{file && <RemoveComponent file={file} onRemove={() => removeFile(idx)} />}
</div>
))}
</React.Fragment>
Expand Down
12 changes: 12 additions & 0 deletions test/forms/inputs/file-input.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(<FileInput { ...props }/>)
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(<FileInput { ...props }/>)
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 }
Expand Down

0 comments on commit 182987c

Please sign in to comment.