Skip to content

Commit 37441d7

Browse files
[added] 添加注释, 测试用例
1 parent 4994536 commit 37441d7

8 files changed

+310
-50
lines changed

lib/DatePicker.js

+47-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* @module DatePicker组件
33
*/
4-
import './index.css';
4+
55
import React, { Component, PropTypes } from 'react';
66
import { nextTime, getTimeName } from './time.js';
77
import {
@@ -26,9 +26,9 @@ class DatePicker extends Component {
2626
};
2727
});
2828

29-
this.animating = false;
30-
this.touchY = 0;
31-
this.angle = 0;
29+
this.animating = false; // 判断是否在transition过渡动画之中
30+
this.touchY = 0; // 保存touchstart的pageY
31+
this.angle = 0; // 容器转过的角度
3232
this.state = {
3333
angle: 0,
3434
dates,
@@ -41,16 +41,31 @@ class DatePicker extends Component {
4141
this._moveToNext = this._moveToNext.bind(this);
4242
}
4343

44+
/**
45+
* 根据角度返回透明度(0-1之间)
46+
* @param {number} angle 角度
47+
* @return
48+
*/
4449
_setOpacity(angle) {
4550
return angle > 0
4651
? ((40 - angle) / 40 * 100 | 0) / 100
4752
: ((40 + angle) / 40 * 100 | 0) / 100;
4853
}
4954

55+
/**
56+
* 清除对象的transition样式
57+
* @param {Dom} obj 指定的对象
58+
* @return {undefined}
59+
*/
5060
_clearTransition(obj) {
5161
obj.style[TRANSITION] = ''; // eslint-disable-line
5262
}
5363

64+
/**
65+
* 滑动到下一日期
66+
* @param {number} direction 滑动方向
67+
* @return {undefined}
68+
*/
5469
_moveToNext(direction) {
5570
const scroll = this.refs.scroll;
5671
const angle = this.angle;
@@ -70,6 +85,12 @@ class DatePicker extends Component {
7085
}
7186
}
7287

88+
/**
89+
* 添加滑动动画
90+
* @param {DOM} obj DOM对象
91+
* @param {number} angle 角度
92+
* @return {undefined}
93+
*/
7394
_moveTo(obj, angle) {
7495
this.animating = true;
7596
obj.style[TRANSITION] = `${TRANSFORM_CSS} .2s ease-out`; // eslint-disable-line
@@ -78,11 +99,20 @@ class DatePicker extends Component {
7899
});
79100
}
80101

102+
/**
103+
* 点击完成按钮事件
104+
* @return {undefined}
105+
*/
81106
handleFinishBtnClick() {
82107
const date = this.state.dates.find(value => value.angle + this.state.angle === 0);
83108
this.props.onSelect(date.value);
84109
}
85110

111+
/**
112+
* 滑动日期选择器事件
113+
* @param {Object} event 事件对象
114+
* @return {undefined}
115+
*/
86116
handleContentTouch(event) {
87117
event.preventDefault();
88118
if (this.animating) return;
@@ -107,6 +137,10 @@ class DatePicker extends Component {
107137
}
108138
}
109139

140+
/**
141+
* transition过渡完成事件
142+
* @return {undefined}
143+
*/
110144
handleContentTransitionEnd() {
111145
const { dates, angle } = this.state;
112146
const date = dates.find(value => value.angle + angle === 0);
@@ -126,6 +160,11 @@ class DatePicker extends Component {
126160
});
127161
}
128162

163+
/**
164+
* 渲染一个日期DOM对象
165+
* @param {Object} date date数据
166+
* @return {Object} JSX对象
167+
*/
129168
renderDatepickerItem(date) {
130169
const itemStyle = {
131170
[TRANSFORM]: `rotateX(${date.angle}deg) translate3d(0,0,100px)`,
@@ -141,6 +180,10 @@ class DatePicker extends Component {
141180
);
142181
}
143182

183+
/**
184+
* render函数
185+
* @return {Object} JSX对象
186+
*/
144187
render() {
145188
const { layerBackground, btnColor } = this.props;
146189
const scrollStyle = {

lib/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
import './index.css';
12
import DatePicker from './DatePicker.js';
23
export default DatePicker;

lib/time.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
/**
2+
* @module time工具
3+
*/
4+
15
function convertDate(timestamp, formate) {
26
const date = new Date(timestamp);
37
const year = date.getFullYear();
@@ -16,7 +20,11 @@ function convertDate(timestamp, formate) {
1620
.replace(/s+/, second);
1721
}
1822

19-
23+
/**
24+
* 获取相对日期的偏移日期
25+
* @param {Date} 日期
26+
* @return {number} 相对的天数
27+
*/
2028
export function nextTime(now = new Date(), index = 1) {
2129
if (Object.prototype.toString.call(now, null) !== '[object Date]') {
2230
throw new Error('参数类型不对');
@@ -27,6 +35,11 @@ export function nextTime(now = new Date(), index = 1) {
2735
return date;
2836
}
2937

38+
/**
39+
* 获取指定日期的格式化日期名称
40+
* @param {Date} 日期
41+
* @return {String} 格式化日期名称
42+
*/
3043
export function getTimeName(now) {
3144
if (Object.prototype.toString.call(now, null) !== '[object Date]') {
3245
throw new Error('参数类型不对');

lib/transition.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ if (typeof document.body.style.transition === 'string') {
66
TRANSITIONEND = 'transitionend';
77
TRANSITION_CSS = 'transition';
88
} else if (typeof document.body.style.webkitTransition === 'string') {
9-
TRANSITION = 'webkitTransition';
9+
TRANSITION = 'WebkitTransition';
1010
TRANSITION_CSS = '-webkit-transition';
1111
TRANSITIONEND = 'webkitTransitionEnd';
1212
}
@@ -17,7 +17,7 @@ if (typeof document.body.style.transform === 'string') {
1717
TRANSFORM = 'transform';
1818
TRANSFORM_CSS = 'transform';
1919
} else if (typeof document.body.style.webkitTransform === 'string') {
20-
TRANSFORM = 'webkitTransform';
20+
TRANSFORM = 'WebkitTransform';
2121
TRANSFORM_CSS = '-webkit-transform';
2222
}
2323

package.json

+6-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"scripts": {
99
"build": "webpack",
1010
"start": "webpack-dev-server",
11-
"release": "scripts/release",
11+
"release": "npm install build;scripts/release",
1212
"preview-release": "scripts/preview-release",
1313
"mocha": "./node_modules/.bin/mocha --compilers js:babel-core/register --require ./test/helper.js --recursive",
1414
"test": "./node_modules/.bin/nyc npm run mocha",
@@ -20,13 +20,15 @@
2020
"babel-core": "^6.9.0",
2121
"babel-eslint": "^6.0.4",
2222
"babel-loader": "^6.2.4",
23+
"babel-polyfill": "^6.9.1",
2324
"babel-preset-es2015": "^6.9.0",
2425
"babel-preset-react": "^6.5.0",
2526
"babel-preset-stage-0": "^6.5.0",
2627
"chai": "^3.5.0",
2728
"coveralls": "^2.11.9",
2829
"css-loader": "^0.23.1",
2930
"cssnext": "^1.8.4",
31+
"enzyme": "^2.3.0",
3032
"eslint": "^2.10.2",
3133
"eslint-config-airbnb": "9.0.1",
3234
"eslint-plugin-import": "^1.8.0",
@@ -41,7 +43,9 @@
4143
"postcss-cssnext": "^2.5.2",
4244
"postcss-loader": "^0.9.1",
4345
"postcss-nested": "^1.0.0",
46+
"react-addons-test-utils": "^15.1.0",
4447
"rf-release": "^0.4.0",
48+
"sinon": "^1.17.4",
4549
"style-loader": "^0.13.1",
4650
"webpack": "^1.13.1",
4751
"webpack-dev-server": "^1.14.1",
@@ -52,4 +56,4 @@
5256
"react-dom": "^15.1.0"
5357
},
5458
"license": "ISC"
55-
}
59+
}

test/functional/DatePicker_spec.js

+177
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
2+
import React from 'react';
3+
import { assert, expect } from 'chai';
4+
import sinon from 'sinon';
5+
import { mount, shallow } from 'enzyme';
6+
import DatePicker from '../../lib/DatePicker';
7+
import {nextTime} from '../../lib/time';
8+
9+
describe('时间选择器组件DatePicker', () => {
10+
describe('测试初始化的过程:', () => {
11+
it('测试传入_setOpacity的参数', () => {
12+
const spyFunction = sinon.spy(DatePicker.prototype, '_setOpacity');
13+
const datePicker = mount(
14+
<DatePicker />
15+
);
16+
17+
const list = [
18+
spyFunction.getCall(0).args[0],
19+
spyFunction.getCall(1).args[0],
20+
spyFunction.getCall(2).args[0],
21+
spyFunction.getCall(3).args[0],
22+
spyFunction.getCall(4).args[0],
23+
];
24+
25+
const expectList = [45, 22.5, 0, -22.5, -45];
26+
expect(list).to.deep.equals(expectList);
27+
spyFunction.restore();
28+
})
29+
})
30+
31+
32+
describe('测试方法:', () => {
33+
it('_setOpacity应该返回相应的值,当传入不同的临界值', () => {
34+
const datePicker = mount(
35+
<DatePicker />
36+
);
37+
const inst = datePicker.instance();
38+
39+
const test1 = [0, 1];
40+
const test2 = [40, 0];
41+
const test3 = [-40, 0];
42+
43+
expect(inst._setOpacity(test1[0])).to.equals(test1[1]);
44+
expect(inst._setOpacity(test2[0])).to.equals(test2[1]);
45+
expect(inst._setOpacity(test3[0])).to.equals(test3[1]);
46+
})
47+
48+
it('应该滑动到上一个日期,当调用_moveToNext方法', () => {
49+
const spyFunction = sinon.spy();
50+
const datePicker = mount(
51+
<DatePicker
52+
onSelect={spyFunction}/>
53+
);
54+
datePicker.instance()._moveToNext(-1);
55+
datePicker.find('.datepicker-finish-btn').simulate('click');
56+
sinon.assert.calledWith(spyFunction, nextTime(new Date(), -1));
57+
})
58+
59+
60+
it('应该向_moveToNext传入-1, 当滑动到上一个日期', () => {
61+
const spyFunction = sinon.spy(DatePicker.prototype, '_moveToNext');
62+
const datePicker = mount(
63+
<DatePicker />
64+
);
65+
const touchstartEvent = {
66+
type: 'touchstart',
67+
targetTouches: [{ pageY: 0 }],
68+
};
69+
const touchendEvent = {
70+
type: 'touchend',
71+
changedTouches: [{ pageY: 50 }],
72+
};
73+
74+
75+
datePicker.find('.datepicker-content').simulate('touchStart', touchstartEvent);
76+
datePicker.find('.datepicker-content').simulate('touchEnd', touchendEvent);
77+
sinon.assert.calledWith(spyFunction, -1);
78+
spyFunction.restore();
79+
})
80+
81+
})
82+
83+
describe('测试交互事件:', () => {
84+
it('应该触发handleContent3次, 在触摸屏幕之后', () => {
85+
const spyFunction = sinon.spy(DatePicker.prototype, 'handleContentTouch');
86+
const datePicker = mount(
87+
<DatePicker />
88+
);
89+
90+
datePicker.find('.datepicker-content').simulate('touchStart');
91+
datePicker.find('.datepicker-content').simulate('touchMove');
92+
datePicker.find('.datepicker-content').simulate('touchEnd');
93+
sinon.assert.callCount(spyFunction, 3);
94+
spyFunction.restore();
95+
})
96+
97+
it('应该触发onSelect, 在点击完成按钮之后', () => {
98+
const spyFunction = sinon.spy();
99+
const datePicker = mount(
100+
<DatePicker
101+
onSelect={spyFunction}/>
102+
);
103+
datePicker.find('.datepicker-finish-btn').simulate('click');
104+
sinon.assert.calledOnce(spyFunction);
105+
})
106+
107+
108+
it('应该向onSelect传入上一个日期, 当向下滑动超过touchLen', () => {
109+
const spyFunction = sinon.spy();
110+
const datePicker = mount(
111+
<DatePicker
112+
onSelect={spyFunction}/>
113+
);
114+
const touchstartEvent = {
115+
type: 'touchstart',
116+
targetTouches: [{ pageY: 0 }],
117+
};
118+
const touchendEvent = {
119+
type: 'touchend',
120+
changedTouches: [{ pageY: 50 }],
121+
};
122+
123+
datePicker.find('.datepicker-content').simulate('touchStart', touchstartEvent);
124+
datePicker.find('.datepicker-content').simulate('touchEnd', touchendEvent);
125+
datePicker.find('.datepicker-content').simulate('transitionEnd');
126+
datePicker.find('.datepicker-finish-btn').simulate('click');
127+
sinon.assert.calledWith(spyFunction, nextTime(new Date(), -1));
128+
})
129+
130+
it('应该向onSelect传入下一个日期, 当向上滑动超过touchLen', () => {
131+
const spyFunction = sinon.spy();
132+
const datePicker = mount(
133+
<DatePicker
134+
maxDate={nextTime(new Date(), 1)}
135+
onSelect={spyFunction}/>
136+
);
137+
const touchstartEvent = {
138+
type: 'touchstart',
139+
targetTouches: [{ pageY: 0 }],
140+
};
141+
const touchendEvent = {
142+
type: 'touchend',
143+
changedTouches: [{ pageY: -50 }],
144+
};
145+
146+
datePicker.find('.datepicker-content').simulate('touchStart', touchstartEvent);
147+
datePicker.find('.datepicker-content').simulate('touchEnd', touchendEvent);
148+
datePicker.find('.datepicker-content').simulate('transitionEnd');
149+
datePicker.find('.datepicker-finish-btn').simulate('click');
150+
sinon.assert.calledWith(spyFunction, nextTime(new Date(), 1));
151+
})
152+
153+
it('应该恢复当前日期, 但滑动超过touchLen但上一个日期小于minDate', () => {
154+
const spyFunction = sinon.spy();
155+
const datePicker = mount(
156+
<DatePicker
157+
minDate={nextTime(new Date())}
158+
onSelect={spyFunction}/>
159+
);
160+
const touchstartEvent = {
161+
type: 'touchstart',
162+
targetTouches: [{ pageY: 0 }],
163+
};
164+
const touchendEvent = {
165+
type: 'touchend',
166+
changedTouches: [{ pageY: 50 }],
167+
};
168+
169+
datePicker.find('.datepicker-content').simulate('touchStart', touchstartEvent);
170+
datePicker.find('.datepicker-content').simulate('touchEnd', touchendEvent);
171+
datePicker.find('.datepicker-content').simulate('transitionEnd');
172+
datePicker.find('.datepicker-finish-btn').simulate('click');
173+
sinon.assert.calledWith(spyFunction, nextTime(new Date(), 0));
174+
})
175+
})
176+
177+
});

0 commit comments

Comments
 (0)