Consistent file uploads (#4699)
* FileSelect component * Use FileSelect component in Actionbar * Convert CreateAccount/Import to FileSelect
This commit is contained in:
24
js/src/ui/Actionbar/Import/import.css
vendored
24
js/src/ui/Actionbar/Import/import.css
vendored
@@ -15,30 +15,6 @@
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
.importZone {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
border-width: 2px;
|
||||
border-color: #666;
|
||||
border-style: dashed;
|
||||
border-radius: 10px;
|
||||
|
||||
background-color: rgba(50, 50, 50, 0.2);
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2em;
|
||||
|
||||
transition: all 0.5s ease;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
border-radius: 0;
|
||||
background-color: rgba(50, 50, 50, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin-top: 0;
|
||||
color: #ccc;
|
||||
|
||||
@@ -15,12 +15,12 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
|
||||
import Button from '../../Button';
|
||||
import FileSelect from '../../Form/FileSelect';
|
||||
import { CancelIcon, DoneIcon, FileUploadIcon } from '../../Icons';
|
||||
import Portal from '../../Portal';
|
||||
|
||||
@@ -184,25 +184,8 @@ export default class ActionbarImport extends Component {
|
||||
return this.renderValidation();
|
||||
}
|
||||
|
||||
return this.renderFileSelect();
|
||||
}
|
||||
|
||||
renderFileSelect () {
|
||||
return (
|
||||
<div>
|
||||
<Dropzone
|
||||
onDrop={ this.onDrop }
|
||||
multiple={ false }
|
||||
className={ styles.importZone }
|
||||
>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id='ui.actiobar.import.dropzone'
|
||||
defaultMessage='Drop a file here, or click to select a file to upload.'
|
||||
/>
|
||||
</div>
|
||||
</Dropzone>
|
||||
</div>
|
||||
<FileSelect onSelect={ this.onFileSelect } />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -224,39 +207,30 @@ export default class ActionbarImport extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
onDrop = (files) => {
|
||||
onFileSelect = (file, content) => {
|
||||
const { renderValidation } = this.props;
|
||||
|
||||
const file = files[0];
|
||||
const reader = new FileReader();
|
||||
if (typeof renderValidation !== 'function') {
|
||||
this.props.onConfirm(content);
|
||||
return this.onCloseModal();
|
||||
}
|
||||
|
||||
reader.onload = (e) => {
|
||||
const content = e.target.result;
|
||||
const validationBody = renderValidation(content);
|
||||
|
||||
if (typeof renderValidation !== 'function') {
|
||||
this.props.onConfirm(content);
|
||||
return this.onCloseModal();
|
||||
}
|
||||
|
||||
const validationBody = renderValidation(content);
|
||||
|
||||
if (validationBody && validationBody.error) {
|
||||
return this.setState({
|
||||
step: 1,
|
||||
error: true,
|
||||
errorText: validationBody.error
|
||||
});
|
||||
}
|
||||
|
||||
this.setState({
|
||||
if (validationBody && validationBody.error) {
|
||||
return this.setState({
|
||||
step: 1,
|
||||
validate: true,
|
||||
validationBody,
|
||||
content
|
||||
error: true,
|
||||
errorText: validationBody.error
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
reader.readAsText(file);
|
||||
this.setState({
|
||||
step: 1,
|
||||
validate: true,
|
||||
validationBody,
|
||||
content
|
||||
});
|
||||
}
|
||||
|
||||
onConfirm = () => {
|
||||
|
||||
52
js/src/ui/Form/FileSelect/fileSelect.css
Normal file
52
js/src/ui/Form/FileSelect/fileSelect.css
Normal file
@@ -0,0 +1,52 @@
|
||||
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
/* This file is part of Parity.
|
||||
/*
|
||||
/* Parity is free software: you can redistribute it and/or modify
|
||||
/* it under the terms of the GNU General Public License as published by
|
||||
/* the Free Software Foundation, either version 3 of the License, or
|
||||
/* (at your option) any later version.
|
||||
/*
|
||||
/* Parity is distributed in the hope that it will be useful,
|
||||
/* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
/* GNU General Public License for more details.
|
||||
/*
|
||||
/* You should have received a copy of the GNU General Public License
|
||||
/* along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
$backgroundNormal: rgba(0, 0, 0, 0.2);
|
||||
$backgroundNormalHover: rgba(0, 0, 0, 0.5);
|
||||
$backgroundError: rgba(255, 0, 0, 0.1);
|
||||
$backgroundErrorHover: rgba(255, 0, 0, 0.2);
|
||||
|
||||
.dropzone {
|
||||
align-items: center;
|
||||
background: $backgroundNormal;
|
||||
border: 2px dashed #666;
|
||||
border-radius: 0.5em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
font-size: 1.2em;
|
||||
height: 12em;
|
||||
transition: all 0.5s ease;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
background: $backgroundNormalHover;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.error {
|
||||
background: $backgroundError;
|
||||
|
||||
&:hover {
|
||||
background: $backgroundErrorHover;
|
||||
}
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #aaa;
|
||||
}
|
||||
}
|
||||
76
js/src/ui/Form/FileSelect/fileSelect.js
Normal file
76
js/src/ui/Form/FileSelect/fileSelect.js
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import Dropzone from 'react-dropzone';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { nodeOrStringProptype } from '~/util/proptypes';
|
||||
|
||||
import styles from './fileSelect.css';
|
||||
|
||||
export default class FileSelect extends Component {
|
||||
static propTypes = {
|
||||
className: PropTypes.string,
|
||||
error: nodeOrStringProptype(),
|
||||
label: nodeOrStringProptype(),
|
||||
onSelect: PropTypes.func.isRequired
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
label: (
|
||||
<FormattedMessage
|
||||
id='ui.fileSelect.defaultLabel'
|
||||
defaultMessage='Drop a file here, or click to select a file to upload'
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, error, label } = this.props;
|
||||
|
||||
return (
|
||||
<Dropzone
|
||||
onDrop={ this.onDrop }
|
||||
multiple={ false }
|
||||
className={
|
||||
[
|
||||
styles.dropzone,
|
||||
error
|
||||
? styles.error
|
||||
: '',
|
||||
className
|
||||
].join(' ') }
|
||||
>
|
||||
<div className={ styles.label }>
|
||||
{ error || label }
|
||||
</div>
|
||||
</Dropzone>
|
||||
);
|
||||
}
|
||||
|
||||
onDrop = (files) => {
|
||||
const { onSelect } = this.props;
|
||||
const reader = new FileReader();
|
||||
const file = files[0];
|
||||
|
||||
reader.onload = (event) => {
|
||||
onSelect(file.name, event.target.result);
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
}
|
||||
118
js/src/ui/Form/FileSelect/fileSelect.spec.js
Normal file
118
js/src/ui/Form/FileSelect/fileSelect.spec.js
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import FileSelect from './';
|
||||
|
||||
const FILE = {
|
||||
content: 'some test content',
|
||||
name: 'someName'
|
||||
};
|
||||
|
||||
let component;
|
||||
let globalFileReader;
|
||||
let instance;
|
||||
let onSelect;
|
||||
let processedFile;
|
||||
|
||||
function stubReader () {
|
||||
globalFileReader = global.FileReader;
|
||||
|
||||
global.FileReader = class {
|
||||
readAsText (file) {
|
||||
processedFile = file;
|
||||
|
||||
this.onload({
|
||||
target: {
|
||||
result: file.content
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function restoreReader () {
|
||||
global.FileReader = globalFileReader;
|
||||
}
|
||||
|
||||
function render (props = {}) {
|
||||
onSelect = sinon.stub();
|
||||
component = shallow(
|
||||
<FileSelect
|
||||
onSelect={ onSelect }
|
||||
{ ...props }
|
||||
/>
|
||||
);
|
||||
instance = component.instance();
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
describe('ui/Form/FileSelect', () => {
|
||||
beforeEach(() => {
|
||||
stubReader();
|
||||
render();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
restoreReader();
|
||||
});
|
||||
|
||||
it('renders defaults', () => {
|
||||
expect(component).to.be.ok;
|
||||
});
|
||||
|
||||
describe('DropZone', () => {
|
||||
let label;
|
||||
let zone;
|
||||
|
||||
beforeEach(() => {
|
||||
label = component.find('FormattedMessage');
|
||||
zone = component.find('Dropzone');
|
||||
});
|
||||
|
||||
it('renders the label', () => {
|
||||
expect(label.props().id).to.equal('ui.fileSelect.defaultLabel');
|
||||
});
|
||||
|
||||
it('attaches the onDrop event', () => {
|
||||
expect(zone.props().onDrop).to.equal(instance.onDrop);
|
||||
});
|
||||
|
||||
it('does not allow multiples', () => {
|
||||
expect(zone.props().multiple).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('event methods', () => {
|
||||
describe('onDrop', () => {
|
||||
beforeEach(() => {
|
||||
instance.onDrop([ FILE ]);
|
||||
});
|
||||
|
||||
it('reads the file as dropped', () => {
|
||||
expect(processedFile).to.deep.equal(FILE);
|
||||
});
|
||||
|
||||
it('calls prop onSelect with file & content', () => {
|
||||
expect(onSelect).to.have.been.calledWith(FILE.name, FILE.content);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/ui/Form/FileSelect/index.js
Normal file
17
js/src/ui/Form/FileSelect/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './fileSelect';
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
export AddressSelect from './AddressSelect';
|
||||
export DappUrlInput from './DappUrlInput';
|
||||
export FileSelect from './FileSelect';
|
||||
export FormWrap from './FormWrap';
|
||||
export Input from './Input';
|
||||
export InputAddress from './InputAddress';
|
||||
|
||||
@@ -30,7 +30,7 @@ export DappCard from './DappCard';
|
||||
export DappIcon from './DappIcon';
|
||||
export Errors from './Errors';
|
||||
export Features, { FEATURES, FeaturesStore } from './Features';
|
||||
export Form, { AddressSelect, DappUrlInput, FormWrap, Input, InputAddress, InputAddressSelect, InputChip, InputDate, InputInline, InputTime, Label, RadioButtons, Select, TypedInput } from './Form';
|
||||
export Form, { AddressSelect, DappUrlInput, FileSelect, FormWrap, Input, InputAddress, InputAddressSelect, InputChip, InputDate, InputInline, InputTime, Label, RadioButtons, Select, TypedInput } from './Form';
|
||||
export GasPriceEditor from './GasPriceEditor';
|
||||
export GasPriceSelector from './GasPriceSelector';
|
||||
export Icons from './Icons';
|
||||
|
||||
Reference in New Issue
Block a user