Skip to content

Commit

Permalink
Allow clear dates to clear partially entered dates
Browse files Browse the repository at this point in the history
Addresses #525.

Move user input state out of `<DateInput/>` and into either `<DateRangePickerInputController/>` or `<SingleDatePicker/>`, passing down the value and a setter as props.
  • Loading branch information
timhwang21 committed Jun 30, 2017
1 parent c96a237 commit b6d1408
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 25 deletions.
19 changes: 10 additions & 9 deletions src/components/DateInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const propTypes = forbidExtraProps({
placeholder: PropTypes.string, // also used as label
displayValue: PropTypes.string,
inputValue: PropTypes.string,
userInputValue: PropTypes.string,
screenReaderMessage: PropTypes.string,
focused: PropTypes.bool,
disabled: PropTypes.bool,
Expand All @@ -18,6 +19,7 @@ const propTypes = forbidExtraProps({
showCaret: PropTypes.bool,

onChange: PropTypes.func,
onUserInputChange: PropTypes.func,
onFocus: PropTypes.func,
onKeyDownShiftTab: PropTypes.func,
onKeyDownTab: PropTypes.func,
Expand All @@ -33,6 +35,7 @@ const defaultProps = {
placeholder: 'Select Date',
displayValue: '',
inputValue: '',
userInputValue: '',
screenReaderMessage: '',
focused: false,
disabled: false,
Expand All @@ -41,6 +44,7 @@ const defaultProps = {
showCaret: false,

onChange() {},
onUserInputChange() {},
onFocus() {},
onKeyDownShiftTab() {},
onKeyDownTab() {},
Expand All @@ -56,7 +60,6 @@ export default class DateInput extends React.Component {
constructor(props) {
super(props);
this.state = {
dateString: '',
isTouchDevice: false,
};

Expand All @@ -70,9 +73,7 @@ export default class DateInput extends React.Component {

componentWillReceiveProps(nextProps) {
if (!this.props.displayValue && nextProps.displayValue) {
this.setState({
dateString: '',
});
nextProps.onUserInputChange('');
}
}

Expand All @@ -89,7 +90,7 @@ export default class DateInput extends React.Component {
}

onChange(e) {
const { onChange, onKeyDownQuestionMark } = this.props;
const { onChange, onKeyDownQuestionMark, onUserInputChange } = this.props;
const dateString = e.target.value;

// In Safari, onKeyDown does not consistently fire ahead of onChange. As a result, we need to
Expand All @@ -98,7 +99,7 @@ export default class DateInput extends React.Component {
if (dateString[dateString.length - 1] === '?') {
onKeyDownQuestionMark(e);
} else {
this.setState({ dateString });
onUserInputChange(dateString);
onChange(dateString);
}
}
Expand Down Expand Up @@ -130,14 +131,14 @@ export default class DateInput extends React.Component {

render() {
const {
dateString,
isTouchDevice: isTouch,
} = this.state;
const {
id,
placeholder,
displayValue,
inputValue,
userInputValue,
screenReaderMessage,
focused,
showCaret,
Expand All @@ -147,8 +148,8 @@ export default class DateInput extends React.Component {
readOnly,
} = this.props;

const displayText = displayValue || inputValue || dateString || placeholder || '';
const value = inputValue || displayValue || dateString || '';
const displayText = displayValue || inputValue || userInputValue || placeholder || '';
const value = inputValue || displayValue || userInputValue || '';
const screenReaderMessageId = `DateInput__screen-reader-message-${id}`;

return (
Expand Down
20 changes: 19 additions & 1 deletion src/components/DateRangePickerInput.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const propTypes = forbidExtraProps({
onEndDateFocus: PropTypes.func,
onStartDateChange: PropTypes.func,
onEndDateChange: PropTypes.func,
onStartDateUserInputChange: PropTypes.func,
onEndDateUserInputChange: PropTypes.func,
onStartDateShiftTab: PropTypes.func,
onEndDateTab: PropTypes.func,
onClearDates: PropTypes.func,
Expand All @@ -34,8 +36,10 @@ const propTypes = forbidExtraProps({

startDate: PropTypes.string,
startDateValue: PropTypes.string,
startDateUserInputValue: PropTypes.string,
endDate: PropTypes.string,
endDateValue: PropTypes.string,
endDateUserInputValue: PropTypes.string,

isStartDateFocused: PropTypes.bool,
isEndDateFocused: PropTypes.bool,
Expand Down Expand Up @@ -68,6 +72,8 @@ const defaultProps = {
onEndDateFocus() {},
onStartDateChange() {},
onEndDateChange() {},
onStartDateUserInputChange() {},
onEndDateUserInputChange() {},
onStartDateShiftTab() {},
onEndDateTab() {},
onClearDates() {},
Expand All @@ -76,8 +82,10 @@ const defaultProps = {

startDate: '',
startDateValue: '',
startDateUserInputValue: '',
endDate: '',
endDateValue: '',
endDateUserInputValue: '',

isStartDateFocused: false,
isEndDateFocused: false,
Expand Down Expand Up @@ -128,19 +136,23 @@ export default class DateRangePickerInput extends React.Component {
const {
startDate,
startDateValue,
startDateUserInputValue,
startDateId,
startDatePlaceholderText,
screenReaderMessage,
isStartDateFocused,
onStartDateChange,
onStartDateUserInputChange,
onStartDateFocus,
onStartDateShiftTab,
endDate,
endDateValue,
endDateUserInputValue,
endDateId,
endDatePlaceholderText,
isEndDateFocused,
onEndDateChange,
onEndDateUserInputChange,
onEndDateFocus,
onEndDateTab,
onArrowDown,
Expand All @@ -164,6 +176,8 @@ export default class DateRangePickerInput extends React.Component {
const arrowIcon = customArrowIcon || (isRTL ? <LeftArrow /> : <RightArrow />);
const closeIcon = customCloseIcon || (<CloseButton />);
const screenReaderText = screenReaderMessage || phrases.keyboardNavigationInstructions;
const isClearDatesHidden =
!(startDate || endDate || startDateUserInputValue || endDateUserInputValue);

return (
<div
Expand All @@ -189,6 +203,7 @@ export default class DateRangePickerInput extends React.Component {
placeholder={startDatePlaceholderText}
displayValue={startDate}
inputValue={startDateValue}
userInputValue={startDateUserInputValue}
screenReaderMessage={screenReaderText}
focused={isStartDateFocused}
isFocused={isFocused}
Expand All @@ -198,6 +213,7 @@ export default class DateRangePickerInput extends React.Component {
showCaret={showCaret}

onChange={onStartDateChange}
onUserInputChange={onStartDateUserInputChange}
onFocus={onStartDateFocus}
onKeyDownShiftTab={onStartDateShiftTab}
onKeyDownArrowDown={onArrowDown}
Expand All @@ -217,6 +233,7 @@ export default class DateRangePickerInput extends React.Component {
placeholder={endDatePlaceholderText}
displayValue={endDate}
inputValue={endDateValue}
userInputValue={endDateUserInputValue}
screenReaderMessage={screenReaderText}
focused={isEndDateFocused}
isFocused={isFocused}
Expand All @@ -226,6 +243,7 @@ export default class DateRangePickerInput extends React.Component {
showCaret={showCaret}

onChange={onEndDateChange}
onUserInputChange={onEndDateUserInputChange}
onFocus={onEndDateFocus}
onKeyDownTab={onEndDateTab}
onKeyDownArrowDown={onArrowDown}
Expand All @@ -237,7 +255,7 @@ export default class DateRangePickerInput extends React.Component {
type="button"
aria-label={phrases.clearDates}
className={cx('DateRangePickerInput__clear-dates', {
'DateRangePickerInput__clear-dates--hide': !(startDate || endDate),
'DateRangePickerInput__clear-dates--hide': isClearDatesHidden,
'DateRangePickerInput__clear-dates--hover': isClearDatesHovered,
})}
onMouseEnter={this.onClearDatesMouseEnter}
Expand Down
29 changes: 29 additions & 0 deletions src/components/DateRangePickerInputController.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,17 @@ const defaultProps = {
export default class DateRangePickerInputController extends React.Component {
constructor(props) {
super(props);
this.state = {
startDateUserInputValue: '',
endDateUserInputValue: '',
};

this.onClearFocus = this.onClearFocus.bind(this);
this.onStartDateChange = this.onStartDateChange.bind(this);
this.onStartDateUserInputChange = this.onStartDateUserInputChange.bind(this);
this.onStartDateFocus = this.onStartDateFocus.bind(this);
this.onEndDateChange = this.onEndDateChange.bind(this);
this.onEndDateUserInputChange = this.onEndDateUserInputChange.bind(this);
this.onEndDateFocus = this.onEndDateFocus.bind(this);
this.clearDates = this.clearDates.bind(this);
}
Expand Down Expand Up @@ -162,6 +168,12 @@ export default class DateRangePickerInputController extends React.Component {
}
}

onEndDateUserInputChange(endDateUserInputValue) {
this.setState({
endDateUserInputValue,
});
}

onStartDateChange(startDateString) {
const startDate = toMomentObject(startDateString, this.getDisplayFormat());

Expand Down Expand Up @@ -189,6 +201,12 @@ export default class DateRangePickerInputController extends React.Component {
}
}

onStartDateUserInputChange(startDateUserInputValue) {
this.setState({
startDateUserInputValue,
});
}

getDisplayFormat() {
const { displayFormat } = this.props;
return typeof displayFormat === 'string' ? displayFormat : displayFormat();
Expand All @@ -204,6 +222,8 @@ export default class DateRangePickerInputController extends React.Component {

clearDates() {
const { onDatesChange, reopenPickerOnClearDates, onFocusChange } = this.props;
this.onStartDateUserInputChange('');
this.onEndDateUserInputChange('');
onDatesChange({ startDate: null, endDate: null });
if (reopenPickerOnClearDates) {
onFocusChange(START_DATE);
Expand Down Expand Up @@ -237,6 +257,11 @@ export default class DateRangePickerInputController extends React.Component {
isRTL,
} = this.props;

const {
startDateUserInputValue,
endDateUserInputValue,
} = this.state;

const startDateString = this.getDateString(startDate);
const startDateValue = toISODateString(startDate);
const endDateString = this.getDateString(endDate);
Expand All @@ -246,11 +271,13 @@ export default class DateRangePickerInputController extends React.Component {
<DateRangePickerInput
startDate={startDateString}
startDateValue={startDateValue}
startDateUserInputValue={startDateUserInputValue}
startDateId={startDateId}
startDatePlaceholderText={startDatePlaceholderText}
isStartDateFocused={isStartDateFocused}
endDate={endDateString}
endDateValue={endDateValue}
endDateUserInputValue={endDateUserInputValue}
endDateId={endDateId}
endDatePlaceholderText={endDatePlaceholderText}
isEndDateFocused={isEndDateFocused}
Expand All @@ -265,9 +292,11 @@ export default class DateRangePickerInputController extends React.Component {
customCloseIcon={customCloseIcon}
phrases={phrases}
onStartDateChange={this.onStartDateChange}
onStartDateUserInputChange={this.onStartDateUserInputChange}
onStartDateFocus={this.onStartDateFocus}
onStartDateShiftTab={this.onClearFocus}
onEndDateChange={this.onEndDateChange}
onEndDateUserInputChange={this.onEndDateUserInputChange}
onEndDateFocus={this.onEndDateFocus}
onEndDateTab={this.onClearFocus}
showClearDates={showClearDates}
Expand Down
14 changes: 13 additions & 1 deletion src/components/SingleDatePicker.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const defaultProps = {
onPrevMonthClick() {},
onNextMonthClick() {},
onClose() {},
onDateChange() {},

// month presentation and interaction related props
renderMonth: null,
Expand All @@ -96,6 +97,7 @@ export default class SingleDatePicker extends React.Component {
this.isTouchDevice = false;

this.state = {
userInputValue: '',
dayPickerContainerStyles: {},
isDayPickerFocused: false,
isInputFocused: false,
Expand All @@ -105,6 +107,7 @@ export default class SingleDatePicker extends React.Component {
this.onDayPickerBlur = this.onDayPickerBlur.bind(this);

this.onChange = this.onChange.bind(this);
this.onUserInputChange = this.onUserInputChange.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onClearFocus = this.onClearFocus.bind(this);
this.clearDate = this.clearDate.bind(this);
Expand Down Expand Up @@ -179,6 +182,12 @@ export default class SingleDatePicker extends React.Component {
}
}

onUserInputChange(userInputValue) {
this.setState({
userInputValue,
});
}

onClearFocus() {
const { startDate, endDate, focused, onFocusChange, onClose } = this.props;
if (!focused) return;
Expand Down Expand Up @@ -237,6 +246,7 @@ export default class SingleDatePicker extends React.Component {

clearDate() {
const { onDateChange, reopenPickerOnClearDate, onFocusChange } = this.props;
this.onUserInputChange('');
onDateChange(null);
if (reopenPickerOnClearDate) {
onFocusChange({ focused: true });
Expand Down Expand Up @@ -395,7 +405,7 @@ export default class SingleDatePicker extends React.Component {
isRTL,
} = this.props;

const { isInputFocused } = this.state;
const { userInputValue, isInputFocused } = this.state;

const displayValue = this.getDateString(date);
const inputValue = toISODateString(date);
Expand All @@ -420,7 +430,9 @@ export default class SingleDatePicker extends React.Component {
customInputIcon={customInputIcon}
displayValue={displayValue}
inputValue={inputValue}
userInputValue={userInputValue}
onChange={this.onChange}
onUserInputChange={this.onUserInputChange}
onFocus={this.onFocus}
onKeyDownShiftTab={this.onClearFocus}
onKeyDownTab={this.onClearFocus}
Expand Down
Loading

0 comments on commit b6d1408

Please sign in to comment.