diff --git a/README.md b/README.md index c55c6060ce..94b2bdbd0b 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Check [index.js](https://github.com/xgfe/react-native-datepicker/blob/master/exa style={{width: 200}} date={this.state.date} mode="date" + placeholder="select date" format="YYYY-MM-DD" minDate="2016-05-01" maxDate="2016-06-01" @@ -59,6 +60,7 @@ You can check [index.js](https://github.com/xgfe/react-native-datepicker/blob/ma | duration | 300 | `number` | Specify the animation duration of datepicker.| | customStyles | - | `number` | The hook of customize datepicker style, same as the native style. `dateTouchBody`, `dateInput`...| | showIcon | true | `boolean` | Controller whether or not show the icon | +| placeholder | '' | `string` | The placeholder show when this.props.date is falsy | ## Methods diff --git a/example/index.android.js b/example/index.android.js index 73fa08f094..14bdf655a8 100644 --- a/example/index.android.js +++ b/example/index.android.js @@ -18,7 +18,7 @@ class example extends Component { super(props); this.state = { - date: '2016-05-11', + date: '', time: '20:00', datetime: '2016-05-05 20:00', datetime1: '2016-05-05 20:00' @@ -35,6 +35,7 @@ class example extends Component { style={{width: 200}} date={this.state.date} mode="date" + placeholder="placeholder" format="YYYY-MM-DD" minDate="2016-05-01" maxDate="2016-06-01" diff --git a/index.js b/index.js index 4cd41ee649..c608d364ab 100644 --- a/index.js +++ b/index.js @@ -24,26 +24,7 @@ class DatePicker extends Component { constructor(props) { super(props); - this.mode = this.props.mode || 'date'; - - this.format = this.props.format || FORMATS[this.mode]; - // component height: 216(DatePickerIOS) + 1(borderTop) + 42(marginTop), IOS only - this.height = 259; - // slide animation duration time, default to 300ms, IOS only - this.duration = this.props.duration || 300; - - this.confirmBtnText = this.props.confirmBtnText || '确定'; - this.cancelBtnText = this.props.cancelBtnText || '取消'; - - this.iconSource = this.props.iconSource || require('./date_icon.png'); - this.customStyles = this.props.customStyles || {}; - - // whether or not show the icon - if (typeof this.props.showIcon === 'boolean') { - this.showIcon = this.props.showIcon; - } else { - this.showIcon = true; - } + this.format = this.props.format || FORMATS[this.props.mode]; this.state = { date: this.getDate(), @@ -79,8 +60,8 @@ class DatePicker extends Component { Animated.timing( this.state.animatedHeight, { - toValue: this.height, - duration: this.duration + toValue: this.props.height, + duration: this.props.duration } ).start(); } else { @@ -120,15 +101,15 @@ class DatePicker extends Component { this.props.onDateChange(this.getDateStr(this.state.date), this.state.date); } } - + getTitleElement() { const {date, placeholder} = this.props; if (!date && placeholder) { - return ({placeholder}); + return ({placeholder}); } - return ({this.getDateStr()}); + return ({this.getDateStr()}); } - + onDatePicked({action, year, month, day}) { if (action !== DatePickerAndroid.dismissedAction) { this.setState({ @@ -183,13 +164,13 @@ class DatePicker extends Component { } else { // 选日期 - if (this.mode === 'date') { + if (this.props.mode === 'date') { DatePickerAndroid.open({ date: this.state.date, minDate: this.props.minDate && this.getDate(this.props.minDate), maxDate: this.props.maxDate && this.getDate(this.props.maxDate) }).then(this.onDatePicked); - } else if (this.mode === 'time') { + } else if (this.props.mode === 'time') { // 选时间 let timeMoment = Moment(this.state.date); @@ -199,7 +180,7 @@ class DatePicker extends Component { minute: timeMoment.minutes(), is24Hour: !this.format.match(/h|a/) }).then(this.onTimePicked); - } else if (this.mode === 'datetime') { + } else if (this.props.mode === 'datetime') { // 选日期和时间 DatePickerAndroid.open({ @@ -214,6 +195,8 @@ class DatePicker extends Component { } render() { + let customStyles = this.props.customStyles; + this.format = this.props.format || FORMATS[this.props.mode]; return ( - - + + {this.getTitleElement()} - {this.showIcon && } {Platform.OS === 'ios' && this.setState({date: date})} - style={[Style.datePicker, this.customStyles.datePicker]} + style={[Style.datePicker, customStyles.datePicker]} /> - {this.cancelBtnText} + {this.props.cancelBtnText} - {this.confirmBtnText} + {this.props.confirmBtnText} @@ -283,4 +266,42 @@ class DatePicker extends Component { } } +DatePicker.defaultProps = { + mode: 'date', + date: '', + // component height: 216(DatePickerIOS) + 1(borderTop) + 42(marginTop), IOS only + height: 259, + + // slide animation duration time, default to 300ms, IOS only + duration: 300, + confirmBtnText: '确定', + cancelBtnText: '取消', + iconSource: require('./date_icon.png'), + customStyles: {}, + + // whether or not show the icon + showIcon: true, + disabled: false, + placeholder: '' +}; + +DatePicker.propTypes = { + mode: React.PropTypes.oneOf(['date', 'datetime', 'time']), + date: React.PropTypes.oneOfType([React.PropTypes.string, function(props, propName) { + if (!(props[propName] instanceof Date)) { + return new Error('Should be string or Date'); + } + }]), + height: React.PropTypes.number, + duration: React.PropTypes.number, + confirmBtnText: React.PropTypes.string, + cancelBtnText: React.PropTypes.string, + iconSource: React.PropTypes.oneOfType([React.PropTypes.number, React.PropTypes.object]), + customStyles: React.PropTypes.object, + showIcon: React.PropTypes.bool, + disabled: React.PropTypes.bool, + onDateChange: React.PropTypes.func, + placeholder: React.PropTypes.string +}; + export default DatePicker; diff --git a/package.json b/package.json index ec88c22217..71c3a457d8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-native-datepicker", - "version": "1.3.0", + "version": "1.3.1", "description": "react native datePicker component for both Android and IOS, useing DatePikcerAndroid, TimePickerAndroid and DatePickerIOS", "main": "index.js", "scripts": { @@ -33,14 +33,15 @@ "chai": "^3.5.0", "coveralls": "^2.11.9", "cz-conventional-changelog": "^1.1.6", - "enzyme": "^2.3.0", + "enzyme": "^2.4.0", "istanbul": "^1.0.0-alpha.2", + "jsdom": "^9.4.1", "mocha": "^2.5.2", "pre-commit": "^1.1.3", - "react": "15.0.2", - "react-addons-test-utils": "15.0.2", - "react-dom": "15.0.2", - "react-native": "^0.26.0", + "react": "^15.1.0", + "react-addons-test-utils": "^15.1.0", + "react-dom": "^15.1.0", + "react-native": "^0.28.0", "react-native-mock": "^0.2.3", "sinon": "^1.17.4" }, diff --git a/test/index.test.js b/test/index.test.js index 4930563723..78121f823e 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -1,11 +1,13 @@ -import React from 'react'; +import React, {Component} from 'react'; import { Animated, - Platform + Platform, + Image, + Text, + View } from 'react-native'; -import {shallow} from 'enzyme'; +import {shallow, mount} from 'enzyme'; import Moment from 'moment'; -import DatePicker from '../index'; import {expect} from 'chai'; import sinon from 'sinon'; @@ -21,29 +23,52 @@ m._load = function (request, parent, isMain) { return originalLoader(request, parent, isMain); }; +/*---------------- mock DOM ----------------*/ +import {jsdom} from 'jsdom'; +var exposedProperties = ['window', 'navigator', 'document']; + +global.document = jsdom(''); +global.window = document.defaultView; +Object.keys(document.defaultView).forEach((property) => { + if (typeof global[property] === 'undefined') { + exposedProperties.push(property); + global[property] = document.defaultView[property]; + } +}); + +global.navigator = { + userAgent: 'node.js' +}; + +global.ErrorUtils = { + setGlobalHandler: () => {} +}; + +var DatePicker = require('../index').default; + describe('DatePicker:', () => { it('initialize', () => { - const wrapper = shallow(); + + const wrapper = mount(); const datePicker = wrapper.instance(); - expect(datePicker.mode).to.equal('date'); + expect(wrapper.prop('mode')).to.equal('date'); expect(datePicker.format).to.equal('YYYY-MM-DD'); - expect(datePicker.duration).to.equal(300); - expect(datePicker.height).to.above(200); - expect(datePicker.confirmBtnText).to.equal('确定'); - expect(datePicker.cancelBtnText).to.equal('取消'); - expect(datePicker.iconSource).to.deep.equal(require('../date_icon.png')); - expect(datePicker.customStyles).to.deep.equal({}); - expect(datePicker.showIcon).to.equal(true); - expect(wrapper.find('Image').length).to.equal(1); + expect(wrapper.prop('duration')).to.equal(300); + expect(wrapper.prop('height')).to.above(200); + expect(wrapper.prop('confirmBtnText')).to.equal('确定'); + expect(wrapper.prop('cancelBtnText')).to.equal('取消'); + expect(wrapper.prop('iconSource')).to.deep.equal(require('../date_icon.png')); + expect(wrapper.prop('customStyles')).to.deep.equal({}); + expect(wrapper.prop('showIcon')).to.equal(true); expect(wrapper.state('date')).to.be.a('date'); - expect(wrapper.state('disabled')).to.equal(undefined); + expect(wrapper.state('disabled')).to.equal(false); expect(wrapper.state('modalVisible')).to.equal(false); expect(wrapper.state('animatedHeight')).to.deep.equal(new Animated.Value(0)); - const wrapper1 = shallow( + const wrapper1 = mount( { ); const datePicker1 = wrapper1.instance(); - - expect(datePicker1.mode).to.equal('datetime'); + expect(wrapper1.prop('mode')).to.equal('datetime'); expect(datePicker1.format).to.equal('YYYY/MM/DD'); - expect(datePicker1.duration).to.equal(400); - expect(datePicker1.confirmBtnText).to.equal('Confirm'); - expect(datePicker1.cancelBtnText).to.equal('Cancel'); - expect(datePicker1.iconSource).to.deep.equal({}); - expect(datePicker1.customStyles).to.deep.equal({testStyle: 123}); - expect(datePicker1.showIcon).to.equal(false); - expect(wrapper1.find('Image').length).to.equal(0); + expect(wrapper1.prop('duration')).to.equal(400); + expect(wrapper1.prop('confirmBtnText')).to.equal('Confirm'); + expect(wrapper1.prop('cancelBtnText')).to.equal('Cancel'); + expect(wrapper1.prop('iconSource')).to.deep.equal({}); + expect(wrapper1.prop('customStyles')).to.deep.equal({testStyle: 123}); + expect(wrapper1.prop('showIcon')).to.equal(false); expect(wrapper1.state('date')).to.deep.equal(Moment('2016-05-11', 'YYYY-MM-DD').toDate()); expect(wrapper1.state('disabled')).to.equal(true); + + // find not work with mount, and defaultProps not work with shallow... + const wrapper2 = shallow(); + expect(wrapper2.find('Image')).to.have.length(1); + expect(wrapper2.instance().getDateStr()).to.equal('2016-09-09'); + + const wrapper3 = shallow(); + expect(wrapper3.find('Image')).to.have.length(0); + expect(wrapper3.instance().getDateStr()).to.equal('Invalid date'); }); it('setModalVisible', () => { @@ -212,26 +244,26 @@ describe('DatePicker:', () => { Platform.OS = 'android'; expect(datePicker.onPressDate).to.not.throw(Error); - datePicker.mode = 'datetime'; + wrapper.setProps({mode: 'datetime'}); expect(datePicker.onPressDate).to.not.throw(Error); - datePicker.mode = 'time'; + wrapper.setProps({mode: 'time'}); expect(datePicker.onPressDate).to.not.throw(Error); - datePicker.mode = 'tttt'; + wrapper.setProps({mode: 'tttt'}); expect(datePicker.onPressDate).to.throw(Error); }); - + it('getTitleElement - with placeholder', () => { const placeholder = 'Please pick a date'; - const wrapper = shallow(); + const wrapper = mount(); const datePicker = wrapper.instance(); expect(datePicker.getTitleElement().props.children).to.equal(placeholder); }); it('getTitleElement - without placeholder', () => { - const wrapper = shallow(); + const wrapper = mount(); const datePicker = wrapper.instance(); expect(datePicker.getTitleElement().props.children).to.equal(datePicker.getDateStr());