-
Notifications
You must be signed in to change notification settings - Fork 832
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add file-picker component * Remove unused files * Add disabled styles and use t.true() * Tweak disabled style
- Loading branch information
Showing
15 changed files
with
536 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"name": "evergreen-file-picker", | ||
"version": "1.0.0", | ||
"description": "React components: FilePicker", | ||
"main": "lib/index.js", | ||
"keywords": ["evergreen", "segment", "ui", "react", "FilePicker"], | ||
"author": "Segment", | ||
"license": "MIT", | ||
"dependencies": { | ||
"evergreen-buttons": "^2.19.6", | ||
"evergreen-text-input": "^2.19.6", | ||
"prop-types": "^15.0.0", | ||
"ui-box": "^0.5.4" | ||
}, | ||
"peerDependencies": { | ||
"react": "^16.0.0" | ||
}, | ||
"xo": false | ||
} |
127 changes: 127 additions & 0 deletions
127
packages/evergreen-file-picker/src/components/FilePicker.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
import React, { PureComponent } from 'react' | ||
import PropTypes from 'prop-types' | ||
import Box from 'ui-box' | ||
import { Button } from 'evergreen-buttons' | ||
import { TextInput } from 'evergreen-text-input' | ||
|
||
export const CLASS_PREFIX = 'evergreen-file-picker' | ||
|
||
export default class FilePicker extends PureComponent { | ||
static propTypes = { | ||
name: PropTypes.string, | ||
accept: PropTypes.oneOfType([ | ||
PropTypes.string, | ||
PropTypes.arrayOf(PropTypes.string) | ||
]), | ||
required: PropTypes.bool, | ||
multiple: PropTypes.bool, | ||
disabled: PropTypes.bool, | ||
capture: PropTypes.bool, | ||
height: PropTypes.number, | ||
onChange: PropTypes.func | ||
} | ||
|
||
constructor() { | ||
super() | ||
|
||
this.state = { | ||
files: [] | ||
} | ||
} | ||
|
||
render() { | ||
const { | ||
name, | ||
accept, | ||
required, | ||
multiple, | ||
disabled, | ||
capture, | ||
height, | ||
...props | ||
} = this.props | ||
const { files } = this.state | ||
|
||
let inputValue | ||
if (files.length === 0) { | ||
inputValue = '' | ||
} else if (files.length === 1) { | ||
inputValue = files[0].name | ||
} else { | ||
inputValue = `${files.length} files` | ||
} | ||
|
||
let buttonText | ||
if (files.length === 0) { | ||
buttonText = 'Select file' | ||
} else if (files.length === 1) { | ||
buttonText = 'Replace file' | ||
} else { | ||
buttonText = 'Replace files' | ||
} | ||
|
||
return ( | ||
<Box display="flex" className={`${CLASS_PREFIX}-root`} {...props}> | ||
<Box | ||
innerRef={this.fileInputRef} | ||
className={`${CLASS_PREFIX}-file-input`} | ||
is="input" | ||
type="file" | ||
name={name} | ||
accept={accept} | ||
required={required} | ||
multiple={multiple} | ||
disabled={disabled} | ||
capture={capture} | ||
onChange={this.handleFileChange} | ||
display="none" | ||
/> | ||
|
||
<TextInput | ||
className={`${CLASS_PREFIX}-text-input`} | ||
readOnly | ||
value={inputValue} | ||
placeholder="Select a file to upload…" | ||
// There's a weird specifity issue when there's two differently sized inputs on the page | ||
borderTopRightRadius="0 !important" | ||
borderBottomRightRadius="0 !important" | ||
height={height} | ||
flex={1} | ||
textOverflow="ellipsis" | ||
/> | ||
|
||
<Button | ||
className={`${CLASS_PREFIX}-button`} | ||
onClick={this.handleButtonClick} | ||
disabled={disabled} | ||
borderTopLeftRadius={0} | ||
borderBottomLeftRadius={0} | ||
height={height} | ||
flexShrink={0} | ||
> | ||
{buttonText} | ||
</Button> | ||
</Box> | ||
) | ||
} | ||
|
||
fileInputRef = node => { | ||
this.fileInput = node | ||
} | ||
|
||
handleFileChange = e => { | ||
const { onChange } = this.props | ||
const files = e.target.files | ||
|
||
// Firefox returns the same array instance each time for some reason | ||
this.setState({ files: [...files] }) | ||
|
||
if (onChange) { | ||
onChange(files) | ||
} | ||
} | ||
|
||
handleButtonClick = () => { | ||
this.fileInput.click() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import FilePicker, { CLASS_PREFIX } from './components/FilePicker' | ||
|
||
export default FilePicker | ||
export { FilePicker, CLASS_PREFIX } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { storiesOf } from '@storybook/react' // eslint-disable-line import/no-extraneous-dependencies | ||
import React from 'react' | ||
import Box from 'ui-box' | ||
import { FilePicker } from '../src/' | ||
|
||
storiesOf('file-picker', module).add('FilePicker', () => ( | ||
<Box padding={40}> | ||
{(() => { | ||
document.body.style.margin = '0' | ||
document.body.style.height = '100vh' | ||
})()} | ||
|
||
<FilePicker multiple width={250} marginBottom={32} /> | ||
|
||
<FilePicker multiple width={350} height={24} marginBottom={32} /> | ||
|
||
<FilePicker disabled width={250} marginBottom={32} /> | ||
</Box> | ||
)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
/* eslint-disable import/no-extraneous-dependencies */ | ||
import React from 'react' | ||
import test from 'ava' | ||
import render from 'react-test-renderer' | ||
import { shallow } from 'enzyme' | ||
import { FilePicker, CLASS_PREFIX } from '../src' | ||
|
||
test('snapshot', t => { | ||
const component = <FilePicker /> | ||
const tree = render.create(component).toJSON() | ||
t.snapshot(tree) | ||
}) | ||
|
||
test('sets name', t => { | ||
const component = shallow(<FilePicker name="hi" />) | ||
t.is(component.find(`.${CLASS_PREFIX}-file-input`).prop('name'), 'hi') | ||
}) | ||
|
||
test('sets accept', t => { | ||
const component = shallow(<FilePicker accept="application/json" />) | ||
t.is( | ||
component.find(`.${CLASS_PREFIX}-file-input`).prop('accept'), | ||
'application/json' | ||
) | ||
}) | ||
|
||
test('sets required', t => { | ||
const component = shallow(<FilePicker required />) | ||
t.true(component.find(`.${CLASS_PREFIX}-file-input`).prop('required')) | ||
}) | ||
|
||
test('sets multiple', t => { | ||
const component = shallow(<FilePicker multiple />) | ||
t.true(component.find(`.${CLASS_PREFIX}-file-input`).prop('multiple')) | ||
}) | ||
|
||
test('sets disabled', t => { | ||
const component = shallow(<FilePicker disabled />) | ||
t.true(component.find(`.${CLASS_PREFIX}-file-input`).prop('disabled')) | ||
t.true(component.find(`.${CLASS_PREFIX}-button`).prop('disabled')) | ||
}) | ||
|
||
test('sets capture', t => { | ||
const component = shallow(<FilePicker capture />) | ||
t.true(component.find(`.${CLASS_PREFIX}-file-input`).prop('capture')) | ||
}) | ||
|
||
test('passes through height', t => { | ||
const component = shallow(<FilePicker height={20} />) | ||
t.is(component.find(`.${CLASS_PREFIX}-text-input`).prop('height'), 20) | ||
t.is(component.find(`.${CLASS_PREFIX}-button`).prop('height'), 20) | ||
}) | ||
|
||
test('passes through props', t => { | ||
const component = shallow(<FilePicker width={20} />) | ||
t.is(component.find(`.${CLASS_PREFIX}-root`).prop('width'), 20) | ||
}) | ||
|
||
test('handles 1 file selected', t => { | ||
const component = shallow(<FilePicker />) | ||
const e = { | ||
target: { | ||
files: [{ name: 'data.json' }] | ||
} | ||
} | ||
component.find(`.${CLASS_PREFIX}-file-input`).simulate('change', e) | ||
t.deepEqual(component.state('files'), e.target.files) | ||
t.is(component.find(`.${CLASS_PREFIX}-text-input`).prop('value'), 'data.json') | ||
t.true(component.find(`.${CLASS_PREFIX}-button`).contains('Replace file')) | ||
}) | ||
|
||
test('handles 2 files selected', t => { | ||
const component = shallow(<FilePicker />) | ||
const e = { | ||
target: { | ||
files: [{ name: 'data1.json' }, { name: 'data2.json' }] | ||
} | ||
} | ||
component.find(`.${CLASS_PREFIX}-file-input`).simulate('change', e) | ||
t.deepEqual(component.state('files'), e.target.files) | ||
t.is(component.find(`.${CLASS_PREFIX}-text-input`).prop('value'), '2 files') | ||
t.true(component.find(`.${CLASS_PREFIX}-button`).contains('Replace files')) | ||
}) | ||
|
||
// Firefox returns the same array instance in each change event for some reason | ||
test('clones files array', t => { | ||
const component = shallow(<FilePicker />) | ||
const e = { | ||
target: { | ||
files: [{ name: 'data.json' }] | ||
} | ||
} | ||
component.find(`.${CLASS_PREFIX}-file-input`).simulate('change', e) | ||
t.deepEqual(component.state('files'), e.target.files) | ||
t.not(component.state('files'), e.target.files) | ||
}) |
Oops, something went wrong.