Skip to content
This repository was archived by the owner on Jun 9, 2024. It is now read-only.

Commit de1859b

Browse files
committed
feat(FileInput): add FileInput component
1 parent ea10cb7 commit de1859b

File tree

6 files changed

+244
-0
lines changed

6 files changed

+244
-0
lines changed

__tests__/FileInput.test.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import { renderToStaticMarkup } from 'react-dom/server';
3+
import { FileInput } from '../src/index';
4+
5+
describe('<FileInput />', () => {
6+
it('renders basic <FileInput />', () => {
7+
expect(renderToStaticMarkup(<FileInput />)).toEqual(
8+
'<label class="pt-file-upload"><input type="file" accept="*"/><span class="pt-file-upload-input">Choose file...</span></label>'
9+
);
10+
});
11+
12+
it('renders basic <FileInput /> with props', () => {
13+
expect(
14+
renderToStaticMarkup(<FileInput className="extra-css-class" />)
15+
).toEqual(
16+
'<label class="pt-file-upload extra-css-class"><input type="file" accept="*"/><span class="pt-file-upload-input">Choose file...</span></label>'
17+
);
18+
19+
expect(renderToStaticMarkup(<FileInput large />)).toEqual(
20+
'<label class="pt-file-upload pt-large"><input type="file" accept="*"/><span class="pt-file-upload-input">Choose file...</span></label>'
21+
);
22+
23+
expect(renderToStaticMarkup(<FileInput fill />)).toEqual(
24+
'<label class="pt-file-upload pt-fill"><input type="file" accept="*"/><span class="pt-file-upload-input">Choose file...</span></label>'
25+
);
26+
27+
expect(renderToStaticMarkup(<FileInput disabled />)).toEqual(
28+
'<label class="pt-file-upload"><input type="file" accept="*" disabled=""/><span class="pt-file-upload-input">Choose file...</span></label>'
29+
);
30+
31+
expect(renderToStaticMarkup(<FileInput multiple />)).toEqual(
32+
'<label class="pt-file-upload"><input type="file" multiple="" accept="*"/><span class="pt-file-upload-input">Choose file...</span></label>'
33+
);
34+
35+
expect(renderToStaticMarkup(<FileInput accept="image/png" />)).toEqual(
36+
'<label class="pt-file-upload"><input type="file" accept="image/png"/><span class="pt-file-upload-input">Choose file...</span></label>'
37+
);
38+
39+
expect(
40+
renderToStaticMarkup(<FileInput placeholder="Select file(s)..." />)
41+
).toEqual(
42+
'<label class="pt-file-upload"><input type="file" accept="*"/><span class="pt-file-upload-input">Select file(s)...</span></label>'
43+
);
44+
});
45+
});
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import React, { Component } from 'react';
2+
import PropTypes from 'prop-types';
3+
import classnames from 'classnames';
4+
5+
class FileInput extends Component {
6+
constructor(props) {
7+
super(props);
8+
}
9+
10+
state = {
11+
placeholder: this.props.placeholder,
12+
};
13+
14+
static propTypes = {
15+
/**
16+
* `accept` attribute for `input="file"`.
17+
*/
18+
accept: PropTypes.string,
19+
20+
/**
21+
* Additional CSS classes.
22+
*/
23+
className: PropTypes.string,
24+
25+
/**
26+
* Make file input `disabled`.
27+
*/
28+
disabled: PropTypes.bool,
29+
30+
/**
31+
* Take up full width of parent element.
32+
*/
33+
fill: PropTypes.bool,
34+
35+
/**
36+
* Use large file input.
37+
*/
38+
large: PropTypes.bool,
39+
40+
/**
41+
* Accept multiple files.
42+
*/
43+
multiple: PropTypes.bool,
44+
45+
/**
46+
* Callback used when user selects a file.
47+
* @param {array} files - array of [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects.
48+
* @param {SyntheticEvent} e
49+
*/
50+
onChange: PropTypes.func,
51+
52+
/**
53+
* File input placeholder.
54+
*/
55+
placeholder: PropTypes.string,
56+
};
57+
58+
static defaultProps = {
59+
accept: '*',
60+
className: '',
61+
disabled: false,
62+
fill: false,
63+
large: false,
64+
multiple: false,
65+
onChange: (files, e) => {},
66+
placeholder: 'Choose file...',
67+
};
68+
69+
handleChange = e => {
70+
const files = [...e.target.files];
71+
const fileNames = files.map(f => f.name);
72+
this.setState({
73+
placeholder: fileNames.length
74+
? fileNames.join(', ')
75+
: this.props.placeholder,
76+
});
77+
this.props.onChange(files, e);
78+
};
79+
80+
render() {
81+
const { accept, className, disabled, fill, large, multiple } = this.props;
82+
return (
83+
<label
84+
className={classnames(
85+
'pt-file-upload',
86+
{ 'pt-fill': fill },
87+
{ 'pt-large': large },
88+
className
89+
)}
90+
>
91+
<input
92+
multiple={multiple}
93+
accept={accept}
94+
type="file"
95+
disabled={disabled}
96+
onChange={this.handleChange}
97+
/>
98+
<span className="pt-file-upload-input">{this.state.placeholder}</span>
99+
</label>
100+
);
101+
}
102+
}
103+
104+
export default FileInput;
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
The File Input component extends [File upload](http://blueprintjs.com/docs/#core/components/forms/file-upload) UI and adds various props and `onChange` callback.
2+
3+
```js static
4+
import { FileInput } from 'blueprint-components';
5+
```
6+
7+
```html static
8+
<FileInput />
9+
```
10+
11+
```jsx
12+
<FileInput />
13+
```
14+
15+
### File Input Large
16+
17+
To use larger size file input, add optional `large` prop:
18+
19+
```html static
20+
<FileInput large />
21+
```
22+
23+
```jsx
24+
<FileInput large />
25+
```
26+
27+
### File Input Fill
28+
29+
To take up full width of parent element, add optional `fill` prop:
30+
31+
```html static
32+
<FileInput fill />
33+
```
34+
35+
```jsx
36+
<FileInput fill />
37+
```
38+
39+
### File Input Disabled
40+
41+
To make file input disabled, add optional `disabled` prop:
42+
43+
```html static
44+
<FileInput disabled />
45+
```
46+
47+
```jsx
48+
<FileInput disabled />
49+
```
50+
51+
### File Input Mixed
52+
53+
In the example below, you can select multiple files at once and with `onChange` callback, you'll see the list of file names on `alert` popup.
54+
55+
The first argument of the `onChange` callback is `files` array, were each element is [File](https://developer.mozilla.org/en-US/docs/Web/API/File) object.
56+
57+
```html static
58+
<FileInput
59+
fill
60+
multiple
61+
onChange={(files) => { alert('Selected files: ' + files.map(f => f.name).join(', ')) }}
62+
/>
63+
```
64+
65+
```jsx
66+
<FileInput
67+
fill
68+
multiple
69+
onChange={(files) => { alert('Selected files: ' + files.map(f => f.name).join(', ')) }}
70+
/>
71+
```
72+
73+
### File Input Internalization
74+
75+
For <abbr title="Internalization">i18n</abbr> purposes, if you want to change the `placeholder` message of the file input, you need to add the corresponding prop:
76+
77+
```html static
78+
<FileInput placeholder="Custom placeholder" />
79+
```
80+
81+
```jsx
82+
<FileInput placeholder="Custom placeholder" />
83+
```
84+
85+
If you want to change the `Browse` text, you'll need to add the following code to your CSS:
86+
87+
```css static
88+
.pt-file-upload-input:after {
89+
content: "YOUR BROWSE TEXT"
90+
}
91+
```

src/components/FileInput/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default as FileInput } from './FileInput';

src/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export { ButtonGroup } from './components/ButtonGroup';
22
export { Callout } from './components/Callout';
33
export { Card } from './components/Card';
44
export { ControlGroup } from './components/ControlGroup';
5+
export { FileInput } from './components/FileInput';
56
export { FormGroup } from './components/FormGroup';
67
export { Label } from './components/Label';
78
export { Navbar } from './components/Navbar';

styleguide/styleguide.setup.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
Callout,
1919
Card,
2020
ControlGroup,
21+
FileInput,
2122
FormGroup,
2223
Label,
2324
Navbar,
@@ -29,6 +30,7 @@ global.ButtonGroup = ButtonGroup;
2930
global.Callout = Callout;
3031
global.Card = Card;
3132
global.ControlGroup = ControlGroup;
33+
global.FileInput = FileInput;
3234
global.FormGroup = FormGroup;
3335
global.Label = Label;
3436
global.Navbar = Navbar;

0 commit comments

Comments
 (0)