From 2b4186547fc7efb8877c37f9a03f0682f10f8bfe Mon Sep 17 00:00:00 2001 From: raul_ascencio_ Date: Fri, 12 Feb 2016 13:39:14 -0600 Subject: [PATCH] [TimePicker] Added value prop. - TimePicker#getValue is now deprecated. - TimePicker#setValue is now deprecated. - Added value prop. - defaultTime is only used if value is not defined on props. - Added tests for TimePicker initialization behavior. - Adding utils/DateTime#formatTime Fixes #3094 Signed-off-by: rscnt --- .../components/TimePicker/ExampleComplex.jsx | 28 +++---- src/time-picker/clock.jsx | 4 +- src/time-picker/time-picker.jsx | 84 ++++++++++++------- src/utils/date-time.js | 66 +++++++++++++++ test/time-picker/time-picker-spec.js | 53 ++++++++++++ 5 files changed, 187 insertions(+), 48 deletions(-) create mode 100644 test/time-picker/time-picker-spec.js diff --git a/docs/src/app/components/pages/components/TimePicker/ExampleComplex.jsx b/docs/src/app/components/pages/components/TimePicker/ExampleComplex.jsx index e9502cbc2a18c2..23cc6912499d1e 100644 --- a/docs/src/app/components/pages/components/TimePicker/ExampleComplex.jsx +++ b/docs/src/app/components/pages/components/TimePicker/ExampleComplex.jsx @@ -3,30 +3,30 @@ import TimePicker from 'material-ui/lib/time-picker/time-picker'; export default class TimePickerExampleComplex extends React.Component { + constructor(props) { + super(props); + this.state = {value24: null, value12: null}; + } - handleChangeTimePicker12 = (err, time) => { - this.refs.picker12hr.setTime(time); + handleChangeTimePicker24 = (e, date) => { + this.setState({value24: date}); }; - handleChangeTimePicker24 = (err, time) => { - this.refs.picker24hr.setTime(time); + handleChangeTimePicker12 = (e, date) => { + this.setState({value12: date}); }; render() { return (
- - +
); } diff --git a/src/time-picker/clock.jsx b/src/time-picker/clock.jsx index f5384a27a9a388..e56e00a24333a9 100644 --- a/src/time-picker/clock.jsx +++ b/src/time-picker/clock.jsx @@ -28,7 +28,7 @@ const Clock = React.createClass({ getInitialState() { return { muiTheme: this.context.muiTheme || getMuiTheme(), - selectedTime: this.props.initialTime, + selectedTime: this.props.initialTime || new Date(), mode: 'hour', }; }, @@ -36,7 +36,7 @@ const Clock = React.createClass({ componentWillReceiveProps(nextProps, nextContext) { this.setState({ muiTheme: nextContext.muiTheme || this.state.muiTheme, - selectedTime: nextProps.initialTime, + selectedTime: nextProps.initialTime || new Date(), }); }, diff --git a/src/time-picker/time-picker.jsx b/src/time-picker/time-picker.jsx index 7ec7d557259cf9..34c2cb960d5a2d 100644 --- a/src/time-picker/time-picker.jsx +++ b/src/time-picker/time-picker.jsx @@ -1,4 +1,6 @@ import React from 'react'; +import warning from 'warning'; +import DateTime from '../utils/date-time.js'; import TimePickerDialog from './time-picker-dialog'; import TextField from '../text-field'; import getMuiTheme from '../styles/getMuiTheme'; @@ -76,6 +78,16 @@ const TimePicker = React.createClass({ * Override the inline-styles of TimePicker's TextField element. */ textFieldStyle: React.PropTypes.object, + + /** + * Sets the time for the Time Picker programmatically. + */ + value: React.PropTypes.object, + + /** + * Wordings used inside the button of the dialog. + */ + wordings: React.PropTypes.object, }, contextTypes: { @@ -94,47 +106,41 @@ const TimePicker = React.createClass({ getInitialState() { return { - time: this.props.defaultTime || emptyTime, + time: this._isControlled() ? this._getControlledTime() : this.props.defaultTime, dialogTime: new Date(), muiTheme: this.context.muiTheme || getMuiTheme(), }; }, - formatTime(date) { - let hours = date.getHours(); - let mins = date.getMinutes().toString(); - - if (this.props.format === 'ampm') { - const isAM = hours < 12; - hours = hours % 12; - const additional = isAM ? ' am' : ' pm'; - hours = (hours || 12).toString(); - - if (mins.length < 2 ) mins = `0${mins}`; - - if (this.props.pedantic) { - // Treat midday/midnight specially http://www.nist.gov/pml/div688/times.cfm - if (hours === '12' && mins === '00') { - return additional === ' pm' ? '12 noon' : '12 midnight'; - } - } - - return hours + (mins === '00' ? '' : `:${mins}`) + additional; + componentWillReceiveProps(nextProps, nextContext) { + const newState = this.state; + if (nextContext.muiTheme) { + newState.muiTheme = nextContext.muiTheme; } - - hours = hours.toString(); - - if (hours.length < 2) hours = `0${hours}`; - if (mins.length < 2) mins = `0${mins}`; - - return `${hours}:${mins}`; + newState.time = this._getControlledTime(nextProps); + this.setState(newState); }, + + /** + * Deprecated. + * returns timepicker value. + **/ getTime() { + warning(false, `getTime() method is deprecated. Use the defaultTime property + instead. Or use the TimePicker as a controlled component with the value + property.`); return this.state.time; }, + /** + * Deprecated + * sets timepicker value. + **/ setTime(time) { + warning(false, `setTime() method is deprecated. Use the defaultTime property + instead. Or use the TimePicker as a controlled component with the value + property.`); this.setState({time: time ? time : emptyTime}); }, @@ -147,14 +153,15 @@ const TimePicker = React.createClass({ openDialog() { this.setState({ - dialogTime: this.getTime(), + dialogTime: this.state.time, }); - this.refs.dialogWindow.show(); }, _handleDialogAccept(t) { - this.setTime(t); + this.setState({ + time: t, + }); if (this.props.onChange) this.props.onChange(null, t); }, @@ -171,6 +178,18 @@ const TimePicker = React.createClass({ if (this.props.onTouchTap) this.props.onTouchTap(e); }, + _isControlled() { + return this.props.value !== null; + }, + + _getControlledTime(props = this.props) { + let result = null; + if (DateTime.isDateObject(props.value)) { + result = props.value; + } + return result; + }, + render() { const { autoOk, @@ -181,6 +200,7 @@ const TimePicker = React.createClass({ onDismiss, style, textFieldStyle, + pedantic, ...other, } = this.props; @@ -197,7 +217,7 @@ const TimePicker = React.createClass({ {...other} style={textFieldStyle} ref="input" - value={time === emptyTime ? null : this.formatTime(time)} + value={time === emptyTime ? null : DateTime.formatTime(time, format, pedantic)} onFocus={this._handleInputFocus} onTouchTap={this._handleInputTouchTap} /> diff --git a/src/utils/date-time.js b/src/utils/date-time.js index f814f4b43a8e37..0758711b466564 100644 --- a/src/utils/date-time.js +++ b/src/utils/date-time.js @@ -49,6 +49,24 @@ export default { return newDate; }, + addHours(d, hours) { + const newDate = this.clone(d); + newDate.setHours(d.getHours() + hours); + return newDate; + }, + + addMinutes(d, minutes) { + const newDate = this.clone(d); + newDate.setMinutes(d.getMinutes() + minutes); + return newDate; + }, + + addSeconds(d, seconds) { + const newDate = this.clone(d); + newDate.setSeconds(d.getMinutes() + seconds); + return newDate; + }, + clone(d) { return new Date(d.getTime()); }, @@ -123,6 +141,46 @@ export default { return `${m}/${d}/${y}`; }, + /** + * formatTime, extracted from date-picker/date-picker. + * + * @param date [Date] A Date object. + * @param format [String] One of 'ampm', '24hr', defaults to 'ampm'. + * @param pedantic [Boolean] Check time-picker/time-picker.jsx file. + * + * @return String A string representing the formatted time. + */ + formatTime(date, format = 'ampm', pedantic = false) { + if (!date) return ''; + let hours = date.getHours(); + let mins = date.getMinutes().toString(); + + if (format === 'ampm') { + let isAM = hours < 12; + hours = hours % 12; + let additional = isAM ? ' am' : ' pm'; + hours = (hours || 12).toString(); + + if (mins.length < 2 ) mins = `0${mins}`; + + if (pedantic) { + // Treat midday/midnight specially http://www.nist.gov/pml/div688/times.cfm + if (hours === '12' && mins === '00') { + return additional === ' pm' ? '12 noon' : '12 midnight'; + } + } + + return hours + (mins === '00' ? '' : `:${mins}`) + additional; + } + + hours = hours.toString(); + + if (hours.length < 2) hours = `0${hours}`; + if (mins.length < 2) mins = `0${mins}`; + + return `${hours}:${mins}`; + }, + isEqualDate(d1, d2) { return d1 && d2 && (d1.getFullYear() === d2.getFullYear()) && @@ -165,4 +223,12 @@ export default { return ~~(this.monthDiff(d1, d2) / 12); }, + isEqualTime(d1, d2) { + return d1 && d2 && + (d1.getHours() === d2.getHours()) && + (d1.getMinutes() === d2.getMinutes()) && + (d1.getSeconds() === d2.getSeconds()) && + (d1.getMilliseconds() === d2.getMilliseconds()); + }, + }; diff --git a/test/time-picker/time-picker-spec.js b/test/time-picker/time-picker-spec.js new file mode 100644 index 00000000000000..aa3d9c864a3645 --- /dev/null +++ b/test/time-picker/time-picker-spec.js @@ -0,0 +1,53 @@ +import React from 'react'; +import TextField from 'text-field'; +import TimePicker from 'time-picker/time-picker'; +import DateTime from 'utils/date-time'; +import TestUtils from 'react-addons-test-utils'; + +describe('TimePicker', () => { + + it('has to give value prop precedence over defaultTime', () => { + let initialTime = new Date(1448967059892); // Tue, 01 Dec 2015 10:50:59 GMT + let valueTime = DateTime.addHours(initialTime, 2); + + let render = TestUtils.renderIntoDocument( + + ); + + let timeTextField = TestUtils.findRenderedComponentWithType(render, TextField); + + expect(timeTextField.props.value, DateTime.formatTime(valueTime)); + }); + + it('takes defaulTime prop to set first value when value prop is missing', () => { + let initialTime = new Date(1448967059892); // Tue, 01 Dec 2015 10:50:59 GMT + + let render = TestUtils.renderIntoDocument( + + ); + + let timeTextField = TestUtils.findRenderedComponentWithType(render, TextField); + + expect(timeTextField.props.value, DateTime.formatTime(initialTime)); + }); + + it('shows value prop if defaultTime is missing', () => { + let initialTime = null; + let valueTime = new Date(1448967059892); // Tue, 01 Dec 2015 10:50:59 GM + + let render = TestUtils.renderIntoDocument( + + ); + + let timeTextField = TestUtils.findRenderedComponentWithType(render, TextField); + + expect(timeTextField.props.value, DateTime.formatTime(valueTime)); + }); + +});