|
1 |
| -import React from 'react'; |
2 | 1 | import moment from 'moment';
|
| 2 | +import React, {forwardRef, useEffect, useRef} from 'react'; |
3 | 3 | import _ from 'underscore';
|
4 |
| -import TextInput from '../TextInput'; |
5 | 4 | import CONST from '../../CONST';
|
6 | 5 | import * as Browser from '../../libs/Browser';
|
7 |
| -import {propTypes, defaultProps} from './datepickerPropTypes'; |
8 |
| -import withWindowDimensions, {windowDimensionsPropTypes} from '../withWindowDimensions'; |
| 6 | +import TextInput from '../TextInput'; |
| 7 | +import {defaultProps, propTypes} from './datepickerPropTypes'; |
9 | 8 | import './styles.css';
|
10 | 9 |
|
11 | 10 | const datePickerPropTypes = {
|
12 | 11 | ...propTypes,
|
13 |
| - ...windowDimensionsPropTypes, |
14 | 12 | };
|
15 | 13 |
|
16 |
| -class DatePicker extends React.Component { |
17 |
| - constructor(props) { |
18 |
| - super(props); |
19 |
| - |
20 |
| - this.setDate = this.setDate.bind(this); |
21 |
| - this.showDatepicker = this.showDatepicker.bind(this); |
| 14 | +function DatePicker({defaultValue, maxDate, minDate, onInputChange, innerRef, label, value, placeholder, errorText, containerStyles, disabled, onBlur}) { |
| 15 | + const inputRef = useRef(null); |
| 16 | + const defaultDateValue = defaultValue ? moment(defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; |
22 | 17 |
|
23 |
| - this.defaultValue = props.defaultValue ? moment(props.defaultValue).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; |
24 |
| - } |
25 |
| - |
26 |
| - componentDidMount() { |
27 |
| - // Adds nice native datepicker on web/desktop. Not possible to set this through props |
28 |
| - this.inputRef.setAttribute('type', 'date'); |
29 |
| - this.inputRef.setAttribute('max', moment(this.props.maxDate).format(CONST.DATE.MOMENT_FORMAT_STRING)); |
30 |
| - this.inputRef.setAttribute('min', moment(this.props.minDate).format(CONST.DATE.MOMENT_FORMAT_STRING)); |
31 |
| - this.inputRef.classList.add('expensify-datepicker'); |
32 |
| - } |
| 18 | + useEffect(() => { |
| 19 | + inputRef.current.setAttribute('type', 'date'); |
| 20 | + inputRef.current.setAttribute('max', moment(maxDate).format(CONST.DATE.MOMENT_FORMAT_STRING)); |
| 21 | + inputRef.current.setAttribute('min', moment(minDate).format(CONST.DATE.MOMENT_FORMAT_STRING)); |
| 22 | + inputRef.current.classList.add('expensify-datepicker'); |
| 23 | + }, [maxDate, minDate]); |
33 | 24 |
|
34 | 25 | /**
|
35 | 26 | * Trigger the `onChange` handler when the user input has a complete date or is cleared
|
36 | 27 | * @param {String} text
|
37 | 28 | */
|
38 |
| - setDate(text) { |
| 29 | + const setDate = (text) => { |
39 | 30 | if (!text) {
|
40 |
| - this.props.onInputChange(''); |
| 31 | + onInputChange(''); |
41 | 32 | return;
|
42 | 33 | }
|
43 | 34 |
|
44 | 35 | const asMoment = moment(text, true);
|
45 | 36 | if (asMoment.isValid()) {
|
46 |
| - this.props.onInputChange(asMoment.format(CONST.DATE.MOMENT_FORMAT_STRING)); |
| 37 | + onInputChange(asMoment.format(CONST.DATE.MOMENT_FORMAT_STRING)); |
47 | 38 | }
|
48 |
| - } |
| 39 | + }; |
49 | 40 |
|
50 | 41 | /**
|
51 | 42 | * Pops the datepicker up when we focus this field. This only works on mWeb chrome
|
52 | 43 | * On mWeb chrome the user needs to tap on the field again in order to bring the datepicker. But our current styles
|
53 | 44 | * don't make this very obvious. To avoid confusion we open the datepicker when the user focuses the field
|
54 | 45 | */
|
55 |
| - showDatepicker() { |
56 |
| - if (!this.inputRef || !Browser.isMobileChrome()) { |
| 46 | + const showDatepicker = () => { |
| 47 | + if (!inputRef.current || !Browser.isMobileChrome()) { |
57 | 48 | return;
|
58 | 49 | }
|
59 | 50 |
|
60 |
| - this.inputRef.click(); |
61 |
| - } |
| 51 | + inputRef.current.click(); |
| 52 | + }; |
62 | 53 |
|
63 |
| - render() { |
64 |
| - return ( |
65 |
| - <TextInput |
66 |
| - forceActiveLabel |
67 |
| - ref={(el) => { |
68 |
| - this.inputRef = el; |
| 54 | + return ( |
| 55 | + <TextInput |
| 56 | + forceActiveLabel |
| 57 | + ref={(el) => { |
| 58 | + inputRef.current = el; |
69 | 59 |
|
70 |
| - if (_.isFunction(this.props.innerRef)) { |
71 |
| - this.props.innerRef(el); |
72 |
| - } |
73 |
| - }} |
74 |
| - onFocus={this.showDatepicker} |
75 |
| - label={this.props.label} |
76 |
| - accessibilityLabel={this.props.label} |
77 |
| - accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} |
78 |
| - onInputChange={this.setDate} |
79 |
| - value={this.props.value} |
80 |
| - defaultValue={this.defaultValue} |
81 |
| - placeholder={this.props.placeholder} |
82 |
| - errorText={this.props.errorText} |
83 |
| - containerStyles={this.props.containerStyles} |
84 |
| - disabled={this.props.disabled} |
85 |
| - onBlur={this.props.onBlur} |
86 |
| - /> |
87 |
| - ); |
88 |
| - } |
| 60 | + if (_.isFunction(innerRef)) { |
| 61 | + innerRef(el); |
| 62 | + } |
| 63 | + }} |
| 64 | + onFocus={showDatepicker} |
| 65 | + label={label} |
| 66 | + accessibilityLabel={label} |
| 67 | + accessibilityRole={CONST.ACCESSIBILITY_ROLE.TEXT} |
| 68 | + onInputChange={setDate} |
| 69 | + value={value} |
| 70 | + defaultValue={defaultDateValue} |
| 71 | + placeholder={placeholder} |
| 72 | + errorText={errorText} |
| 73 | + containerStyles={containerStyles} |
| 74 | + disabled={disabled} |
| 75 | + onBlur={onBlur} |
| 76 | + /> |
| 77 | + ); |
89 | 78 | }
|
90 | 79 |
|
91 | 80 | DatePicker.propTypes = datePickerPropTypes;
|
92 | 81 | DatePicker.defaultProps = defaultProps;
|
| 82 | +DatePicker.displayName = 'DatePicker'; |
93 | 83 |
|
94 |
| -export default withWindowDimensions( |
95 |
| - React.forwardRef((props, ref) => ( |
96 |
| - <DatePicker |
97 |
| - // eslint-disable-next-line react/jsx-props-no-spreading |
98 |
| - {...props} |
99 |
| - innerRef={ref} |
100 |
| - /> |
101 |
| - )), |
102 |
| -); |
| 84 | +export default forwardRef((props, ref) => ( |
| 85 | + <DatePicker |
| 86 | + // eslint-disable-next-line react/jsx-props-no-spreading |
| 87 | + {...props} |
| 88 | + innerRef={ref} |
| 89 | + /> |
| 90 | +)); |
0 commit comments