-
Notifications
You must be signed in to change notification settings - Fork 3.9k
Add DatePicker component #5509
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add DatePicker component #5509
Changes from all commits
37135b9
125894a
f8ac6b6
c1922b8
a2c9fa5
5226357
03ad0e5
c149599
9f647d5
b29b1c6
215a925
3173150
014e4f8
52beb8d
e0259b6
374f3a3
73fbf05
0b2e111
6eb8946
3d15dd8
8019668
fcb1687
0a6174c
4325871
0f710a2
9a3e83b
9db7aa9
71a8c32
f635517
dfb3d17
61f0f61
859e196
00799de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,6 @@ | ||
| <resources> | ||
| <color name="bootsplash_background">#FFFFFF</color> | ||
| <color name="accent">#0185ff</color> | ||
| <color name="dark">#0b1b34</color> | ||
| <color name="gray4">#7D8B8F</color> | ||
| </resources> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import PropTypes from 'prop-types'; | ||
| import { | ||
| propTypes as fieldPropTypes, | ||
| defaultProps as defaultFieldPropTypes, | ||
| } from '../ExpensiTextInput/baseExpensiTextInputPropTypes'; | ||
|
|
||
| const propTypes = { | ||
| ...fieldPropTypes, | ||
|
|
||
| /** | ||
| * The datepicker supports any value that `moment` can parse. | ||
| * `onChange` would always be called with a Date (or null) | ||
| */ | ||
| value: PropTypes.oneOfType([PropTypes.instanceOf(Date), PropTypes.string]), | ||
| }; | ||
|
|
||
| const defaultProps = { | ||
| ...defaultFieldPropTypes, | ||
| value: undefined, | ||
| }; | ||
|
|
||
| export {propTypes, defaultProps}; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| import React from 'react'; | ||
| import RNDatePicker from '@react-native-community/datetimepicker'; | ||
| import moment from 'moment'; | ||
| import ExpensiTextInput from '../ExpensiTextInput'; | ||
| import CONST from '../../CONST'; | ||
| import {propTypes, defaultProps} from './datepickerPropTypes'; | ||
|
|
||
|
kidroca marked this conversation as resolved.
|
||
| class DatePicker extends React.Component { | ||
| constructor(props) { | ||
| super(props); | ||
|
|
||
| this.state = { | ||
| isPickerVisible: false, | ||
| }; | ||
|
|
||
| this.showPicker = this.showPicker.bind(this); | ||
| this.raiseDateChange = this.raiseDateChange.bind(this); | ||
| } | ||
|
|
||
| /** | ||
| * @param {Event} event | ||
| */ | ||
| showPicker(event) { | ||
| this.setState({isPickerVisible: true}); | ||
| event.preventDefault(); | ||
| } | ||
|
|
||
| /** | ||
| * @param {Event} event | ||
| * @param {Date} selectedDate | ||
| */ | ||
| raiseDateChange(event, selectedDate) { | ||
| if (event.type === 'set') { | ||
| this.props.onChange(selectedDate); | ||
| } | ||
|
|
||
| this.setState({isPickerVisible: false}); | ||
| } | ||
|
|
||
| render() { | ||
| const { | ||
| value, | ||
| label, | ||
| placeholder, | ||
| hasError, | ||
| errorText, | ||
| translateX, | ||
| containerStyles, | ||
| disabled, | ||
| } = this.props; | ||
|
|
||
| const dateAsText = value ? moment(value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; | ||
|
|
||
| return ( | ||
| <> | ||
| <ExpensiTextInput | ||
| label={label} | ||
| value={dateAsText} | ||
| placeholder={placeholder} | ||
| hasError={hasError} | ||
| errorText={errorText} | ||
| containerStyles={containerStyles} | ||
| translateX={translateX} | ||
| onPress={this.showPicker} | ||
| editable={false} | ||
| disabled={disabled} | ||
| /> | ||
| {this.state.isPickerVisible && ( | ||
| <RNDatePicker | ||
| value={value ? moment(value).toDate() : new Date()} | ||
| mode="date" | ||
| onChange={this.raiseDateChange} | ||
| /> | ||
| )} | ||
| </> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| DatePicker.propTypes = propTypes; | ||
| DatePicker.defaultProps = defaultProps; | ||
|
|
||
| export default DatePicker; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| import React from 'react'; | ||
| import {Button, View} from 'react-native'; | ||
| import RNDatePicker from '@react-native-community/datetimepicker'; | ||
| import moment from 'moment'; | ||
| import ExpensiTextInput from '../ExpensiTextInput'; | ||
| import withLocalize, {withLocalizePropTypes} from '../withLocalize'; | ||
| import Popover from '../Popover'; | ||
| import CONST from '../../CONST'; | ||
| import styles from '../../styles/styles'; | ||
| import themeColors from '../../styles/themes/default'; | ||
| import {propTypes, defaultProps} from './datepickerPropTypes'; | ||
|
|
||
| const datepickerPropTypes = { | ||
| ...propTypes, | ||
| ...withLocalizePropTypes, | ||
| }; | ||
|
|
||
| class Datepicker extends React.Component { | ||
| constructor(props) { | ||
| super(props); | ||
|
|
||
| this.state = { | ||
| isPickerVisible: false, | ||
| selectedDate: props.value ? moment(props.value).toDate() : new Date(), | ||
| }; | ||
|
|
||
| this.showPicker = this.showPicker.bind(this); | ||
| this.reset = this.reset.bind(this); | ||
| this.selectDate = this.selectDate.bind(this); | ||
| this.updateLocalDate = this.updateLocalDate.bind(this); | ||
| } | ||
|
|
||
| /** | ||
| * @param {Event} event | ||
| */ | ||
| showPicker(event) { | ||
| this.initialValue = this.state.selectedDate; | ||
| this.setState({isPickerVisible: true}); | ||
| event.preventDefault(); | ||
| } | ||
|
|
||
| /** | ||
| * Reset the date spinner to the initial value | ||
| */ | ||
| reset() { | ||
| this.setState({selectedDate: this.initialValue}); | ||
| } | ||
|
|
||
| /** | ||
| * Accept the current spinner changes, close the spinner and propagate the change | ||
| * to the parent component (props.onChange) | ||
| */ | ||
| selectDate() { | ||
| this.setState({isPickerVisible: false}); | ||
| this.props.onChange(this.state.selectedDate); | ||
| } | ||
|
|
||
| /** | ||
| * @param {Event} event | ||
| * @param {Date} selectedDate | ||
| */ | ||
| updateLocalDate(event, selectedDate) { | ||
| this.setState({selectedDate}); | ||
| } | ||
|
|
||
| render() { | ||
| const { | ||
| value, | ||
| label, | ||
| placeholder, | ||
| hasError, | ||
| errorText, | ||
| translateX, | ||
| containerStyles, | ||
| disabled, | ||
| } = this.props; | ||
|
|
||
| const dateAsText = value ? moment(value).format(CONST.DATE.MOMENT_FORMAT_STRING) : ''; | ||
|
|
||
| return ( | ||
| <> | ||
| <ExpensiTextInput | ||
| label={label} | ||
| value={dateAsText} | ||
| placeholder={placeholder} | ||
| hasError={hasError} | ||
| errorText={errorText} | ||
| containerStyles={containerStyles} | ||
| translateX={translateX} | ||
| onPress={this.showPicker} | ||
| editable={false} | ||
| disabled={disabled} | ||
| /> | ||
| <Popover | ||
| isVisible={this.state.isPickerVisible} | ||
| onClose={this.selectDate} | ||
| > | ||
| <View style={[ | ||
| styles.flexRow, | ||
| styles.justifyContentBetween, | ||
| styles.borderBottom, | ||
| styles.pb1, | ||
| styles.ph4, | ||
| ]} | ||
| > | ||
| <Button | ||
| title={this.props.translate('common.reset')} | ||
| color={themeColors.textError} | ||
| onPress={this.reset} | ||
| /> | ||
| <Button | ||
| title={this.props.translate('common.done')} | ||
| color={themeColors.link} | ||
| onPress={this.selectDate} | ||
| /> | ||
| </View> | ||
| <RNDatePicker | ||
| value={this.state.selectedDate} | ||
| mode="date" | ||
| display="spinner" | ||
| onChange={this.updateLocalDate} | ||
| locale={this.props.preferredLocale} | ||
| /> | ||
| </Popover> | ||
| </> | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| Datepicker.propTypes = datepickerPropTypes; | ||
| Datepicker.defaultProps = defaultProps; | ||
|
|
||
| export default withLocalize(Datepicker); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NAB but maybe useful to mention somewhere that iOS is the only platform where we have to manage localization ourselves, and on all others it's controlled by the OS
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll open a separate request to put a note about this We're localizing the "Reset" and "Done" button that we put on our own popup. On Android the buttons and the modal are handled by the system so they are always in the system locale |
||
Uh oh!
There was an error while loading. Please reload this page.