diff --git a/src/components/DatePicker/datepickerPropTypes.js b/src/components/DatePicker/datepickerPropTypes.js index 8f3bd03c75e0..4a681ec90c49 100644 --- a/src/components/DatePicker/datepickerPropTypes.js +++ b/src/components/DatePicker/datepickerPropTypes.js @@ -9,10 +9,16 @@ const propTypes = { /** * The datepicker supports any value that `moment` can parse. - * `onChange` would always be called with a Date (or null) + * `onInputChange` would always be called with a Date (or null) */ value: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]), + /** + * The datepicker supports any defaultValue that `moment` can parse. + * `onInputChange` would always be called with a Date (or null) + */ + defaultValue: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]), + /* Restricts for selectable max date range for the picker */ maximumDate: PropTypes.instanceOf(Date), }; diff --git a/src/components/DatePicker/index.android.js b/src/components/DatePicker/index.android.js index bc6a8ea54222..77d7bbc705a2 100644 --- a/src/components/DatePicker/index.android.js +++ b/src/components/DatePicker/index.android.js @@ -1,6 +1,7 @@ import React from 'react'; import RNDatePicker from '@react-native-community/datetimepicker'; import moment from 'moment'; +import _ from 'underscore'; import TextInput from '../TextInput'; import CONST from '../../CONST'; import {propTypes, defaultProps} from './datepickerPropTypes'; @@ -14,31 +15,31 @@ class DatePicker extends React.Component { }; this.showPicker = this.showPicker.bind(this); - this.raiseDateChange = this.raiseDateChange.bind(this); - } - - /** - * @param {Event} event - */ - showPicker(event) { - this.setState({isPickerVisible: true}); - event.preventDefault(); + this.setDate = this.setDate.bind(this); } /** * @param {Event} event * @param {Date} selectedDate */ - raiseDateChange(event, selectedDate) { + setDate(event, selectedDate) { if (event.type === 'set') { - this.props.onChange(selectedDate); + this.props.onInputChange(selectedDate); } this.setState({isPickerVisible: false}); } + /** + * @param {Event} event + */ + showPicker(event) { + this.setState({isPickerVisible: true}); + event.preventDefault(); + } + render() { - const dateAsText = this.props.value ? moment(this.props.value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; + const dateAsText = this.props.defaultValue ? moment(this.props.defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; return ( <> @@ -46,18 +47,24 @@ class DatePicker extends React.Component { label={this.props.label} value={dateAsText} placeholder={this.props.placeholder} - hasError={this.props.hasError} errorText={this.props.errorText} containerStyles={this.props.containerStyles} onPress={this.showPicker} editable={false} disabled={this.props.disabled} + onBlur={this.props.onBlur} + ref={(el) => { + if (!_.isFunction(this.props.innerRef)) { + return; + } + this.props.innerRef(el); + }} /> {this.state.isPickerVisible && ( )} @@ -69,4 +76,7 @@ class DatePicker extends React.Component { DatePicker.propTypes = propTypes; DatePicker.defaultProps = defaultProps; -export default DatePicker; +export default React.forwardRef((props, ref) => ( + /* eslint-disable-next-line react/jsx-props-no-spreading */ + +)); diff --git a/src/components/DatePicker/index.ios.js b/src/components/DatePicker/index.ios.js index 6a1bd98de987..879a102ac1ad 100644 --- a/src/components/DatePicker/index.ios.js +++ b/src/components/DatePicker/index.ios.js @@ -3,6 +3,7 @@ import React from 'react'; import {Button, View} from 'react-native'; import RNDatePicker from '@react-native-community/datetimepicker'; import moment from 'moment'; +import _ from 'underscore'; import TextInput from '../TextInput'; import withLocalize, {withLocalizePropTypes} from '../withLocalize'; import Popover from '../Popover'; @@ -16,13 +17,13 @@ const datepickerPropTypes = { ...withLocalizePropTypes, }; -class Datepicker extends React.Component { +class DatePicker extends React.Component { constructor(props) { super(props); this.state = { isPickerVisible: false, - selectedDate: props.value ? moment(props.value).toDate() : new Date(), + selectedDate: props.defaultValue ? moment(props.defaultValue).toDate() : new Date(), }; this.showPicker = this.showPicker.bind(this); @@ -49,11 +50,11 @@ class Datepicker extends React.Component { /** * Accept the current spinner changes, close the spinner and propagate the change - * to the parent component (props.onChange) + * to the parent component (props.onInputChange) */ selectDate() { this.setState({isPickerVisible: false}); - this.props.onChange(this.state.selectedDate); + this.props.onInputChange(this.state.selectedDate); } /** @@ -65,19 +66,25 @@ class Datepicker extends React.Component { } render() { - const dateAsText = this.props.value ? moment(this.props.value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; + const dateAsText = this.props.defaultValue ? moment(this.props.defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; return ( <> { + if (!_.isFunction(this.props.innerRef)) { + return; + } + this.props.innerRef(el); + }} /> ( + /* eslint-disable-next-line react/jsx-props-no-spreading */ + +))); diff --git a/src/components/DatePicker/index.js b/src/components/DatePicker/index.js index a324c25ae878..0077e4cf9add 100644 --- a/src/components/DatePicker/index.js +++ b/src/components/DatePicker/index.js @@ -1,5 +1,6 @@ import React from 'react'; import moment from 'moment'; +import _ from 'underscore'; import TextInput from '../TextInput'; import CONST from '../../CONST'; import {propTypes, defaultProps} from './datepickerPropTypes'; @@ -12,18 +13,18 @@ const datePickerPropTypes = { ...windowDimensionsPropTypes, }; -class Datepicker extends React.Component { +class DatePicker extends React.Component { constructor(props) { super(props); - this.raiseDateChange = this.raiseDateChange.bind(this); + this.setDate = this.setDate.bind(this); this.showDatepicker = this.showDatepicker.bind(this); /* We're using uncontrolled input otherwise it wont be possible to * raise change events with a date value - each change will produce a date * and make us reset the text input */ - this.defaultValue = props.value - ? moment(props.value).format(CONST.DATE.MOMENT_FORMAT_STRING) + this.defaultValue = props.defaultValue + ? moment(props.defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; } @@ -40,15 +41,15 @@ class Datepicker extends React.Component { * Trigger the `onChange` handler when the user input has a complete date or is cleared * @param {String} text */ - raiseDateChange(text) { + setDate(text) { if (!text) { - this.props.onChange(null); + this.props.onInputChange(null); return; } const asMoment = moment(text); if (asMoment.isValid()) { - this.props.onChange(asMoment.format(CONST.DATE.MOMENT_FORMAT_STRING)); + this.props.onInputChange(asMoment.format(CONST.DATE.MOMENT_FORMAT_STRING)); } } @@ -69,22 +70,31 @@ class Datepicker extends React.Component { return ( this.inputRef = input} + ref={(el) => { + this.inputRef = el; + + if (_.isFunction(this.props.innerRef)) { + this.props.innerRef(el); + } + }} onFocus={this.showDatepicker} label={this.props.label} - onChangeText={this.raiseDateChange} + onInputChange={this.setDate} defaultValue={this.defaultValue} placeholder={this.props.placeholder} - hasError={this.props.hasError} errorText={this.props.errorText} containerStyles={this.props.containerStyles} disabled={this.props.disabled} + onBlur={this.props.onBlur} /> ); } } -Datepicker.propTypes = datePickerPropTypes; -Datepicker.defaultProps = defaultProps; +DatePicker.propTypes = datePickerPropTypes; +DatePicker.defaultProps = defaultProps; -export default withWindowDimensions(Datepicker); +export default withWindowDimensions(React.forwardRef((props, ref) => ( + /* eslint-disable-next-line react/jsx-props-no-spreading */ + +))); diff --git a/src/pages/EnablePayments/AdditionalDetailsStep.js b/src/pages/EnablePayments/AdditionalDetailsStep.js index b4842ca7792a..2d679b1118f4 100644 --- a/src/pages/EnablePayments/AdditionalDetailsStep.js +++ b/src/pages/EnablePayments/AdditionalDetailsStep.js @@ -320,8 +320,8 @@ class AdditionalDetailsStep extends React.Component { this.clearErrorAndSetValue('dob', val)} - value={this.props.walletAdditionalDetailsDraft.dob || ''} + onInputChange={val => this.clearErrorAndSetValue('dob', val)} + defaultValue={this.props.walletAdditionalDetailsDraft.dob || ''} placeholder={this.props.translate('common.dob')} errorText={this.getErrorText('dob')} maximumDate={new Date()} diff --git a/src/pages/ReimbursementAccount/CompanyStep.js b/src/pages/ReimbursementAccount/CompanyStep.js index 384a7bfeba05..59dd4149abcb 100644 --- a/src/pages/ReimbursementAccount/CompanyStep.js +++ b/src/pages/ReimbursementAccount/CompanyStep.js @@ -119,8 +119,7 @@ class CompanyStep extends React.Component { * @param {String} value */ clearDateErrorsAndSetValue(value) { - this.clearError('incorporationDate'); - this.clearError('incorporationDateFuture'); + this.clearErrors(['incorporationDate', 'incorporationDateFuture']); this.setValue({incorporationDate: value}); } @@ -277,8 +276,8 @@ class CompanyStep extends React.Component { { label={`${props.translate('common.dob')}`} containerStyles={[styles.mt4]} placeholder={props.translate('common.dateFormat')} - value={props.values.dob} - onChange={value => props.onFieldChange({dob: value})} + defaultValue={props.values.dob} + onInputChange={value => props.onFieldChange({dob: value})} errorText={dobErrorText} maximumDate={new Date()} /> diff --git a/src/stories/Datepicker.stories.js b/src/stories/Datepicker.stories.js index 0fb3cd31c2b6..08f39b104c7e 100644 --- a/src/stories/Datepicker.stories.js +++ b/src/stories/Datepicker.stories.js @@ -13,7 +13,7 @@ export default { onChange: {action: 'date changed'}, }, args: { - value: '', + defaultValue: '', label: 'Select Date', placeholder: 'Date Placeholder', errorText: '', @@ -34,7 +34,7 @@ Default.args = { PreFilled.args = { label: 'Select Date', - value: new Date(2018, 7, 21), + defaultValue: new Date(2018, 7, 21), }; export { diff --git a/src/stories/Form.stories.js b/src/stories/Form.stories.js index 262a9f3a1c90..5e45076a8c7a 100644 --- a/src/stories/Form.stories.js +++ b/src/stories/Form.stories.js @@ -3,6 +3,7 @@ import {View} from 'react-native'; import TextInput from '../components/TextInput'; import Picker from '../components/Picker'; import AddressSearch from '../components/AddressSearch'; +import DatePicker from '../components/DatePicker'; import Form from '../components/Form'; import * as FormActions from '../libs/actions/FormActions'; import styles from '../styles/styles'; @@ -22,6 +23,7 @@ const story = { AddressSearch, CheckboxWithLabel, Picker, + DatePicker, }, }; @@ -54,6 +56,12 @@ const Template = (args) => { containerStyles={[styles.mt4]} isFormInput /> +