Skip to content

Commit 065c51d

Browse files
committed
add uploader w/ test files
1 parent aeca971 commit 065c51d

File tree

14 files changed

+325
-4
lines changed

14 files changed

+325
-4
lines changed

.DS_Store

0 Bytes
Binary file not shown.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
coverage
33
lib
44
node_modules
5-
npm-debug.log
5+
npm-debug.log
6+
.DS_Store

example/.DS_Store

6 KB
Binary file not shown.

example/pages/.DS_Store

6 KB
Binary file not shown.

example/pages/cell/.DS_Store

6 KB
Binary file not shown.

example/pages/cell/images/.DS_Store

6 KB
Binary file not shown.

example/pages/cell/images/avatar.jpg

41.3 KB
Loading

example/pages/cell/index.js

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,37 @@ import { ButtonArea,
2323
Switch,
2424
Radio,
2525
Checkbox,
26-
Select
26+
Select,
27+
Uploader
2728
} from '../../../src/index';
2829
import Page from '../../component/page';
2930
import iconSrc from './images/icon.png';
3031
import vcodeSrc from './images/vcode.jpg';
32+
import avatarSrc from './images/avatar.jpg';
3133

3234
export default class CellDemo extends React.Component {
35+
state = {
36+
demoFiles : [
37+
{
38+
url: avatarSrc,
39+
onClick: e=>alert('事件测试')
40+
},
41+
{
42+
url: avatarSrc
43+
},
44+
{
45+
url: avatarSrc
46+
},
47+
{
48+
url: avatarSrc,
49+
error: true
50+
},
51+
{
52+
url: avatarSrc,
53+
status: '50%'
54+
}
55+
]
56+
};
3357

3458
render() {
3559
return (
@@ -207,6 +231,26 @@ export default class CellDemo extends React.Component {
207231
<Button type="default">取消</Button>
208232
</ButtonArea>
209233

234+
<CellsTitle>上传</CellsTitle>
235+
<Form>
236+
<FormCell>
237+
<CellBody>
238+
<Uploader
239+
title="图片上传"
240+
maxCount={6}
241+
files={this.state.demoFiles}
242+
onError={msg => alert(msg)}
243+
onChange={file => {
244+
let newFiles = [...this.state.demoFiles, {url:file.data}];
245+
this.setState({
246+
demoFiles: newFiles
247+
});
248+
}}
249+
/>
250+
</CellBody>
251+
</FormCell>
252+
</Form>
253+
210254
<CellsTitle>文本域</CellsTitle>
211255
<Form>
212256
<FormCell>

src/.DS_Store

6 KB
Binary file not shown.

src/components/.DS_Store

6 KB
Binary file not shown.

src/components/form/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Switch from './switch';
1212
import Radio from './radio';
1313
import Checkbox from './checkbox';
1414
import Select from './select';
15+
import Uploader from './uploader';
1516

1617
export default {
1718
Form,
@@ -21,5 +22,6 @@ export default {
2122
Switch,
2223
Radio,
2324
Checkbox,
24-
Select
25+
Select,
26+
Uploader
2527
};

src/components/form/uploader.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* Created by n7best
3+
*/
4+
5+
6+
import React, { Component, PropTypes } from 'react';
7+
import classNames from 'classnames';
8+
9+
export default class Uploader extends React.Component {
10+
static propTypes = {
11+
title: PropTypes.string,
12+
maxCount: PropTypes.number,
13+
maxWidth: PropTypes.number,
14+
onChange: PropTypes.func,
15+
onError: PropTypes.func,
16+
files: PropTypes.array,
17+
lang: PropTypes.object
18+
};
19+
20+
static defaultProps = {
21+
title: '图片上传',
22+
maxCount: 4,
23+
maxWidth: 500,
24+
files: [],
25+
onChange: undefined,
26+
onError: undefined,
27+
lang:{
28+
maxError: maxCount => `最多只能上传${maxCount}张图片`
29+
}
30+
};
31+
32+
handleFile(file,cb) {
33+
let reader = new FileReader();
34+
reader.onload = e => {
35+
let img = new Image();
36+
img.onload = ()=>{
37+
let w = Math.min(this.props.maxWidth, img.width);
38+
let h = img.height * (w / img.width);
39+
let canvas = document.createElement('canvas');
40+
let ctx = canvas.getContext('2d');
41+
canvas.width = w;
42+
canvas.height = h;
43+
ctx.drawImage(img, 0, 0, w, h);
44+
let base64 = canvas.toDataURL('image/png');
45+
cb({
46+
lastModified: file.lastModified,
47+
lastModifiedDate: file.lastModifiedDate,
48+
name: file.name,
49+
size: file.size,
50+
type: file.type,
51+
data: base64
52+
});
53+
};
54+
img.src = e.target.result;
55+
}
56+
reader.readAsDataURL(file);
57+
}
58+
59+
handleChange(e) {
60+
const langs = this.props.lang;
61+
let _files = e.target.files;
62+
63+
if(_files.length === 0) return;
64+
65+
if(this.props.files.length >= this.props.maxCount) {
66+
this.props.onError(langs.maxError(this.props.maxCount));
67+
return;
68+
}
69+
70+
for(let key in _files) {
71+
if (!_files.hasOwnProperty(key)) continue;
72+
let file = _files[key];
73+
this.handleFile(file, _file=>{
74+
if(this.props.onChange) this.props.onChange(_file, e);
75+
});
76+
}
77+
}
78+
79+
renderFiles(){
80+
return this.props.files.map((file, idx)=>{
81+
let {url, error, status, ...others} = file;
82+
let fileStyle = {
83+
backgroundImage: `url(${url})`
84+
};
85+
let cls = classNames({
86+
weui_uploader_file: true,
87+
weui_uploader_status: error || status
88+
});
89+
90+
return (
91+
<li className={cls} key={idx} style={fileStyle} {...others}>
92+
{
93+
error || status ?
94+
<div className="weui_uploader_status_content">
95+
{ error ? <i className="weui_icon_warn"></i> : status }
96+
</div>
97+
: false
98+
}
99+
</li>
100+
);
101+
});
102+
}
103+
104+
render(){
105+
const { className, title, maxCount, files, onChange, ...others } = this.props;
106+
const cls = classNames({
107+
weui_uploader: true,
108+
[className]: className
109+
});
110+
111+
return (
112+
<div className={cls}>
113+
<div className="weui_uploader_hd weui_cell">
114+
<div className="weui_cell_bd weui_cell_primary">{title}</div>
115+
<div className="weui_cell_ft">{files.length}/{maxCount}</div>
116+
</div>
117+
<div className="weui_uploader_bd">
118+
<ul className="weui_uploader_files">
119+
{this.renderFiles()}
120+
</ul>
121+
<div className="weui_uploader_input_wrp">
122+
<input
123+
className="weui_uploader_input"
124+
type="file"
125+
accept="image/jpg,image/jpeg,image/png,image/gif"
126+
onChange={this.handleChange.bind(this)}
127+
{...others}
128+
/>
129+
</div>
130+
</div>
131+
</div>
132+
);
133+
}
134+
};

src/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import {Button, ButtonArea} from './components/button/index';
66
import {Cells, CellsTitle, CellsTips, Cell, CellHeader, CellBody, CellFooter} from './components/cell/index';
77
import Mask from './components/mask/index';
8-
import {Form, FormCell, TextArea, Input, Switch, Radio, Checkbox, Select} from './components/form/index';
8+
import {Form, FormCell, TextArea, Input, Switch, Radio, Checkbox, Select, Uploader} from './components/form/index';
99
import Label from './components/label/index';
1010
import Toast from './components/toast/index';
1111
import Progress from './components/progress/index';
@@ -35,6 +35,7 @@ export default {
3535
TextArea,
3636
Switch,
3737
Select,
38+
Uploader,
3839
Label,
3940
Toast,
4041
Progress,

test/uploader.js

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/**
2+
* Created by n7best.
3+
*/
4+
5+
"use strict";
6+
7+
import React from 'react';
8+
import { shallow } from 'enzyme';
9+
import assert from 'assert';
10+
import WeUI from '../src/index';
11+
12+
const {Uploader} = WeUI;
13+
const files = [
14+
{
15+
data: 'http://mmrmb.github.io/avatar/bear.jpg',
16+
onClick: e=>alert('事件测试')
17+
},
18+
{
19+
data: 'http://mmrmb.github.io/avatar/bear.jpg'
20+
},
21+
{
22+
data: 'http://mmrmb.github.io/avatar/bear.jpg'
23+
},
24+
{
25+
data: 'http://mmrmb.github.io/avatar/bear.jpg',
26+
error: true
27+
},
28+
{
29+
data: 'http://mmrmb.github.io/avatar/bear.jpg',
30+
status: '50%'
31+
}
32+
];
33+
const title = '提示';
34+
const enlang = {
35+
maxError: maxCount => `Max ${maxCount} photos allowed`
36+
};
37+
describe('<Uploader></Uploader>', ()=> {
38+
[enlang, undefined].map(lang=> {
39+
[undefined, 5, 30].map(maxCount=> {
40+
[files, []].map(files=> {
41+
describe(`<Uploader lang=${lang} title=${title} files=${files} maxCount=${maxCount}></Uploader>`, ()=> {
42+
let verifyFiles = {};
43+
let verifyErrors;
44+
const wrapper = shallow(
45+
<Uploader lang={lang} title={title} files={files} maxCount={maxCount} onError={msg=>verifyErrors=msg} onChange={_files=>verifyFiles=_files}/>
46+
);
47+
48+
it('should render <Uploader></Uploader> component', () => {
49+
assert(wrapper.instance() instanceof Uploader);
50+
assert(wrapper.hasClass('weui_uploader'));
51+
});
52+
53+
it('should render header with class weui_uploader_hd, weui_cell', () => {
54+
assert(wrapper.children().first().hasClass('weui_uploader_hd'));
55+
assert(wrapper.children().first().hasClass('weui_cell'));
56+
});
57+
58+
it('should render body with class weui_uploader_bd', () => {
59+
assert(wrapper.children().last().hasClass('weui_uploader_bd'));
60+
});
61+
62+
let bd = wrapper.children().last().children();
63+
64+
it('should render lists container with weui_uploader_files', () => {
65+
assert(bd.find('ul').hasClass('weui_uploader_files'));
66+
});
67+
68+
it('should render input wrapper with weui_uploader_input_wrp', () => {
69+
assert(bd.last().hasClass('weui_uploader_input_wrp'));
70+
});
71+
72+
it('should render input with weui_uploader_input', () => {
73+
assert(bd.last().find('input').hasClass('weui_uploader_input'));
74+
});
75+
76+
it('should render file preview with weui_uploader_file', () => {
77+
if(files.length > 0){
78+
assert(bd.first().find('ul').children().first().hasClass('weui_uploader_file'));
79+
}
80+
});
81+
82+
it('should render file preview with weui_uploader_status when error is true or status is set', () => {
83+
if(files.length > 0){
84+
assert(bd.first().find('ul').children().at(3).hasClass('weui_uploader_status'));
85+
assert(bd.first().find('ul').children().at(4).hasClass('weui_uploader_status'));
86+
}
87+
});
88+
89+
it('should render div with weui_uploader_status_content when error is true or status is set', () => {
90+
if(files.length > 0){
91+
assert(bd.first().find('ul').children().at(3).find('div').hasClass('weui_uploader_status_content'));
92+
assert(bd.first().find('ul').children().at(4).find('div').hasClass('weui_uploader_status_content'));
93+
}
94+
});
95+
96+
it('should render icon with weui_icon_warn when error is true', () => {
97+
if(files.length > 0){
98+
assert(bd.first().find('ul').children().at(3).find('div').find('i').hasClass('weui_icon_warn'));
99+
}
100+
});
101+
102+
it('should render status when status is set', () => {
103+
if(files.length > 0){
104+
assert.equal('50%',bd.first().find('ul').children().at(4).find('div').text());
105+
}
106+
});
107+
108+
it('should render chinese error message', () => {
109+
let _maxCount = maxCount ? maxCount : 4;
110+
if(files.length > _maxCount && !lang){
111+
verifyErrors = '';
112+
let expectedmsg = `最多只能上传${_maxCount}张图片`;
113+
let input = assert(bd.last().find('input'));
114+
let changefiles = { 0: {name:'foo', size: 500001} };
115+
bd.last().find('input').simulate('change',{target:{files:changefiles}});
116+
assert.equal(expectedmsg,verifyErrors);
117+
}
118+
});
119+
120+
it('should render english error message', () => {
121+
let _maxCount = maxCount ? maxCount : 4;
122+
if(files.length > _maxCount && lang){
123+
verifyErrors = '';
124+
let expectedmsg = `Max ${_maxCount} photos allowed`;
125+
let input = assert(bd.last().find('input'));
126+
let changefiles = { 0: {name:'foo', size: 500001} };
127+
bd.last().find('input').simulate('change',{target:{files:changefiles}});
128+
assert.equal(expectedmsg,verifyErrors);
129+
}
130+
});
131+
132+
it('should render files', () => {
133+
//currently can't test filereaders
134+
});
135+
});
136+
});
137+
});
138+
});
139+
});

0 commit comments

Comments
 (0)