Skip to content

Commit

Permalink
✨ [feature] Add characteristics of automatic configuration Year, Mont…
Browse files Browse the repository at this point in the history
…h, Day, Hour, Minute, Second.
  • Loading branch information
lanjingling0510 committed Jul 8, 2017
1 parent 89e1b78 commit d2c3372
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 47 deletions.
2 changes: 2 additions & 0 deletions examples/basic/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ window.Perf = require('react-addons-perf');
</a>
</div>
<DatePicker
dateFormat={['hh', 'mm', 'ss']}
showFormat="hh时:mm分:ss秒"
value={this.state.time}
theme={this.state.theme}
isOpen={this.state.isOpen}
Expand Down
35 changes: 12 additions & 23 deletions lib/DatePicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class DatePicker extends Component {
* @return {Object} JSX对象
*/
render() {
const { min, max, theme, dateFormat, confirmText, cancelText } = this.props;
const { min, max, theme, dateFormat, confirmText, cancelText, showFormat } = this.props;
const value = this.state.value;
const themeClassName =
['default', 'dark', 'ios', 'android', 'android-dark'].indexOf(theme) === -1 ?
Expand All @@ -74,29 +74,17 @@ class DatePicker extends Component {
return (
<div
className={`datepicker ${themeClassName}`}>
<div className="datepicker-header">{convertDate(value, 'YYYY/MM/DD')}</div>
<div className="datepicker-header">{convertDate(value, showFormat)}</div>
<div className="datepicker-content">
<DatePickerItem
value={value}
min={min}
max={max}
typeName="Year"
format={dateFormat[0]}
onSelect={this.handleDateSelect} />
<DatePickerItem
value={value}
min={min}
max={max}
typeName="Month"
format={dateFormat[1]}
onSelect={this.handleDateSelect} />
<DatePickerItem
value={value}
min={min}
max={max}
typeName="Date"
format={dateFormat[2]}
onSelect={this.handleDateSelect} />
{dateFormat.map((format, index) => (
<DatePickerItem
key={index}
value={value}
min={min}
max={max}
format={format}
onSelect={this.handleDateSelect} />
))}
</div>
<div className="datepicker-navbar">
<a
Expand All @@ -117,6 +105,7 @@ DatePicker.propTypes = {
min: PropTypes.object,
max: PropTypes.object,
dateFormat: PropTypes.array,
showFormat: PropTypes.string,
confirmText: PropTypes.string,
cancelText: PropTypes.string,
onSelect: PropTypes.func,
Expand Down
34 changes: 31 additions & 3 deletions lib/DatePickerItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,33 @@ const MIDDLE_Y = - DATE_HEIGHT * MIDDLE_INDEX; // translateY值

const isUndefined = val => typeof val === 'undefined';

/**
* 根据格式获取时间滑动的类别
* @param {String} format 格式
* @return {string} 类别名称
*/
const getTimeType = format => {
const typeMap = {
Y: 'Year',
M: 'Month',
D: 'Date',
h: 'Hour',
m: 'Minute',
s: 'Second',
};

for (const key in typeMap) {
if (typeMap.hasOwnProperty(key)) {
if (~format.indexOf(key)) {
return typeMap[key]
}
}
}

throw new Error('时间格式必须包含 Y, M, D, h, m 或 s字母');
}


/**
* Class Date组件类
* @extends Component
Expand All @@ -31,6 +58,8 @@ class DatePickerItem extends Component {
marginTop: (this.currentIndex - MIDDLE_INDEX) * DATE_HEIGHT,
};

// 设置时间选择器单元的类别
this.typeName = getTimeType(props.format);
this.renderDatepickerItem = this.renderDatepickerItem.bind(this);
this.handleContentTouch = this.handleContentTouch.bind(this);
this.handleContentMouseDown = this.handleContentMouseDown.bind(this);
Expand Down Expand Up @@ -84,15 +113,15 @@ class DatePickerItem extends Component {
}

_iniDates(date) {
const { typeName } = this.props;
const typeName = this.typeName;
const dates = Array(...Array(DATE_LENGTH))
.map((value, index) =>
TimeUtil[`next${typeName}`](date, index - MIDDLE_INDEX));
this.setState({ dates });
}

_updateDates(direction) {
const { typeName } = this.props;
const typeName = this.typeName;
const { dates } = this.state;
if (direction === 1) {
this.currentIndex ++;
Expand Down Expand Up @@ -305,7 +334,6 @@ DatePickerItem.propTypes = {
min: PropTypes.object,
max: PropTypes.object,
format: PropTypes.string,
typeName: PropTypes.string,
onSelect: PropTypes.func,
};

Expand Down
1 change: 1 addition & 0 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ ModalDatePicker.defaultProps = {
min: new Date(1970, 0, 1),
max: new Date(2050, 0, 1),
dateFormat: ['YYYY', 'M', 'D'],
showFormat: 'YYYY/MM/DD',
confirmText: '完成',
cancelText: '取消',
onSelect: () => {},
Expand Down
52 changes: 42 additions & 10 deletions lib/time.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
* @module time工具
*/


function throwIfInvalidDate(date) {
if (Object.prototype.toString.call(date, null) !== '[object Date]') {
throw new Error('参数类型不对');
Expand All @@ -28,15 +27,16 @@ export function convertDate(date, format) {
's+': date.getSeconds(),
};
if (/(Y+)/.test(format)) {
str = str.replace(RegExp.$1,
(date.getFullYear().toString()).substr(4 - RegExp.$1.length));
str = str.replace(RegExp.$1, date.getFullYear().toString().substr(4 - RegExp.$1.length));
}

for (const k in o) { // eslint-disable-line
for (const k in o) {
// eslint-disable-line
if (new RegExp(`(${k})`).test(format)) {
str = str.replace(RegExp.$1,
(RegExp.$1.length === 1) ?
o[k] : (`00${o[k]}`.substr((o[k].toString()).length)));
str = str.replace(
RegExp.$1,
RegExp.$1.length === 1 ? o[k] : `00${o[k]}`.substr(o[k].toString().length),
);
}
}

Expand All @@ -50,7 +50,14 @@ export function convertDate(date, format) {
*/
export function nextYear(now, index = 0) {
throwIfInvalidDate(now);
const date = new Date(now.getFullYear() + index, now.getMonth(), now.getDate());
const date = new Date(
now.getFullYear() + index,
now.getMonth(),
now.getDate(),
now.getHours(),
now.getMinutes(),
now.getSeconds(),
);
return date;
}

Expand All @@ -59,12 +66,37 @@ export function nextMonth(now, index = 0) {
const year = now.getFullYear();
const month = now.getMonth() + index;
const dayOfMonth = Math.min(now.getDate(), daysInMonth(year, month));
const date = new Date(year, month, dayOfMonth);
const date = new Date(
year,
month,
dayOfMonth,
now.getHours(),
now.getMinutes(),
now.getSeconds(),
);
return date;
}

export function nextDate(now, index = 0) {
throwIfInvalidDate(now);
const date = new Date(now.getFullYear(), now.getMonth(), now.getDate() + index);
const date = new Date(now.getTime() + index * 24 * 60 * 60 * 1000);
return date;
}

export function nextHour(now, index = 0) {
throwIfInvalidDate(now);
const date = new Date(now.getTime() + index * 60 * 60 * 1000);
return date;
}

export function nextMinute(now, index = 0) {
throwIfInvalidDate(now);
const date = new Date(now.getTime() + index * 60 * 1000);
return date;
}

export function nextSecond(now, index = 0) {
throwIfInvalidDate(now);
const date = new Date(now.getTime() + index * 1000);
return date;
}
17 changes: 8 additions & 9 deletions test/functional/DatePickerItem_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ const DEFAULT_PROPS = {
value: new Date(2010, 3, 7),
min: new Date(2010, 2, 6),
max: new Date(2010, 4, 8),
format: 'M',
onSelect: () => {},
}

Expand All @@ -20,7 +19,7 @@ describe('DatePickerItem.js', () => {
it('should call componentWillMount and initialize dates of state', () => {
const spyFunction = sinon.spy(DatePickerItem.prototype, 'componentWillMount');
const datePicker = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);
const dates = datePicker.state('dates');
sinon.assert.calledOnce(spyFunction);
Expand All @@ -35,7 +34,7 @@ describe('DatePickerItem.js', () => {
it('componentWillReceiveProps', () => {
const spyFunction = sinon.spy(DatePickerItem.prototype, 'componentWillReceiveProps');
const datePicker = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);
datePicker.setProps({ date: new Date(2010, 3, 10) });
const dates = datePicker.state('dates');
Expand All @@ -51,7 +50,7 @@ describe('DatePickerItem.js', () => {
it('shouldComponentUpdate', () => {
const spyFunction = sinon.spy(DatePickerItem.prototype, 'shouldComponentUpdate');
const datePicker = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);

datePicker.setProps({ value: new Date(2010, 3, 10) });
Expand All @@ -65,7 +64,7 @@ describe('DatePickerItem.js', () => {
it('should call handleContent three times after touching', () => {
const spyFunction = sinon.spy(DatePickerItem.prototype, 'handleContentTouch');
const datePicker = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);

const touchstartEvent = {
Expand Down Expand Up @@ -93,7 +92,7 @@ describe('DatePickerItem.js', () => {
it('should analyzing the right direction', () => {
const spyFunction = sinon.spy(DatePickerItem.prototype, '_moveToNext');
const datePicker = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);
const touchstartEvent = {
targetTouches: [{ pageY: 0 }],
Expand All @@ -110,7 +109,7 @@ describe('DatePickerItem.js', () => {
expect(spyFunction.getCall(0).args[0]).to.equal(-1);

const datePicker2 = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);
const touchstartEvent2 = {
targetTouches: [{ pageY: 0 }],
Expand All @@ -132,7 +131,7 @@ describe('DatePickerItem.js', () => {
it('should update dates of state, When the sliding more than 20', () => {
const spyFunction = sinon.spy(DatePickerItem.prototype, '_updateDates');
const datePicker = mount(
<DatePickerItem {...DEFAULT_PROPS} typeName="Date" />
<DatePickerItem {...DEFAULT_PROPS} format="D" />
);
const touchstartEvent = {
targetTouches: [{ pageY: 0 }],
Expand Down Expand Up @@ -161,7 +160,7 @@ describe('DatePickerItem.js', () => {

const spyFunction = sinon.spy(DatePickerItem.prototype, '_moveTo');
const datePicker = mount(
<DatePickerItem {...props} typeName="Date" />
<DatePickerItem {...props} format="D" />
);

const touchstartEvent = {
Expand Down
52 changes: 52 additions & 0 deletions test/functional/DatePicker_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,55 @@ describe('DatePicker.js', () => {
})
});
});

describe('渲染正确的DatepicketItem子组件', () => {
let props;
let mountedDatepicker;

const datePicker = () => {
if (!mountedDatepicker) {
mountedDatepicker = mount(
<DatePicker {...props} />
);
}

return mountedDatepicker;
}

beforeEach(() => {
props = {
value: new Date(2016, 8, 16),
};
mountedDatepicker = undefined;
});

it('当dateFormat等于[YYYY, MM, DD]', () => {
props.dateFormat = ['YYYY', 'MM', 'DD'];
const datePickerItems = datePicker().find(DatePickerItem);
expect(datePickerItems.length).to.equals(3);
expect(datePickerItems.at(0).props().format).to.equals('YYYY');
expect(datePickerItems.at(1).props().format).to.equals('MM');
expect(datePickerItems.at(2).props().format).to.equals('DD');
});

it('当dateFormat等于[YYYY, MM, DD, hh, mm, ss]', () => {
props.dateFormat = ['YYYY', 'MM', 'DD', 'hh', 'mm', 'ss'];
const datePickerItems = datePicker().find(DatePickerItem);
expect(datePickerItems.length).to.equals(6);
expect(datePickerItems.at(0).props().format).to.equals('YYYY');
expect(datePickerItems.at(1).props().format).to.equals('MM');
expect(datePickerItems.at(2).props().format).to.equals('DD');
expect(datePickerItems.at(3).props().format).to.equals('hh');
expect(datePickerItems.at(4).props().format).to.equals('mm');
expect(datePickerItems.at(5).props().format).to.equals('ss');
});

it('当dateFormat等于[hh, mm, ss]', () => {
props.dateFormat = ['hh', 'mm', 'ss'];
const datePickerItems = datePicker().find(DatePickerItem);
expect(datePickerItems.length).to.equals(3);
expect(datePickerItems.at(0).props().format).to.equals('hh');
expect(datePickerItems.at(1).props().format).to.equals('mm');
expect(datePickerItems.at(2).props().format).to.equals('ss');
});
});
Loading

0 comments on commit d2c3372

Please sign in to comment.