diff --git a/examples/DayPickerSingleDateControllerWrapper.jsx b/examples/DayPickerSingleDateControllerWrapper.jsx
index 02d94e931b..44a0a5e1fb 100644
--- a/examples/DayPickerSingleDateControllerWrapper.jsx
+++ b/examples/DayPickerSingleDateControllerWrapper.jsx
@@ -19,6 +19,7 @@ const propTypes = forbidExtraProps({
initialDate: momentPropTypes.momentObj,
showInput: PropTypes.bool,
+ allowUnselect: PropTypes.bool,
keepOpenOnDateSelect: PropTypes.bool,
isOutsideRange: PropTypes.func,
isDayBlocked: PropTypes.func,
@@ -56,6 +57,7 @@ const defaultProps = {
showInput: false,
// day presentation and interaction related props
+ allowUnselect: false,
renderCalendarDay: undefined,
renderDayContents: null,
isDayBlocked: () => false,
diff --git a/src/components/DayPickerSingleDateController.jsx b/src/components/DayPickerSingleDateController.jsx
index 89be9f6ed8..de492c3a24 100644
--- a/src/components/DayPickerSingleDateController.jsx
+++ b/src/components/DayPickerSingleDateController.jsx
@@ -34,12 +34,18 @@ import {
import DayPicker from './DayPicker';
import getPooledMoment from '../utils/getPooledMoment';
+// Default value of the date property. Represents the state
+// when there is no date selected.
+// TODO: use null
+const DATE_UNSET_VALUE = undefined;
+
const propTypes = forbidExtraProps({
date: momentPropTypes.momentObj,
minDate: momentPropTypes.momentObj,
maxDate: momentPropTypes.momentObj,
onDateChange: PropTypes.func,
+ allowUnselect: PropTypes.bool,
focused: PropTypes.bool,
onFocusChange: PropTypes.func,
onClose: PropTypes.func,
@@ -102,11 +108,12 @@ const propTypes = forbidExtraProps({
});
const defaultProps = {
- date: undefined, // TODO: use null
+ date: DATE_UNSET_VALUE,
minDate: null,
maxDate: null,
onDateChange() {},
+ allowUnselect: false,
focused: false,
onFocusChange() {},
onClose() {},
@@ -352,16 +359,19 @@ export default class DayPickerSingleDateController extends React.PureComponent {
if (e) e.preventDefault();
if (this.isBlocked(day)) return;
const {
+ allowUnselect,
onDateChange,
keepOpenOnDateSelect,
onFocusChange,
onClose,
} = this.props;
- onDateChange(day);
+ const clickedDay = allowUnselect && this.isSelected(day) ? DATE_UNSET_VALUE : day;
+
+ onDateChange(clickedDay);
if (!keepOpenOnDateSelect) {
onFocusChange({ focused: false });
- onClose({ date: day });
+ onClose({ date: clickedDay });
}
}
diff --git a/stories/DayPickerSingleDateController.js b/stories/DayPickerSingleDateController.js
index e7fd81a5f0..ef05c08305 100644
--- a/stories/DayPickerSingleDateController.js
+++ b/stories/DayPickerSingleDateController.js
@@ -145,6 +145,14 @@ storiesOf('DayPickerSingleDateController', module)
onNextMonthClick={action('DayPickerSingleDateController::onNextMonthClick')}
/>
)))
+ .add('with day unselection', withInfo()(() => (
+
+ )))
.add('with custom input', withInfo()(() => (
{
expect(onDateChangeStub.callCount).to.equal(1);
});
+ it('props.onDateChange receives undefined when day selected', () => {
+ const date = moment();
+ const onDateChangeStub = sinon.stub();
+ const wrapper = shallow((
+ {}}
+ date={date}
+ allowUnselect
+ />
+ ));
+ // Click same day as the provided date.
+ wrapper.instance().onDayClick(date);
+ expect(onDateChangeStub.callCount).to.equal(1);
+ expect(onDateChangeStub.getCall(0).args[0]).to.equal(undefined);
+ });
+
+ it('props.onDateChange receives day when allowUnselect is disabled', () => {
+ const date = moment();
+ const onDateChangeStub = sinon.stub();
+ const wrapper = shallow((
+ {}}
+ date={date}
+ allowUnselect={false}
+ />
+ ));
+ // Click same day as the provided date.
+ wrapper.instance().onDayClick(date);
+ expect(onDateChangeStub.callCount).to.equal(1);
+ expect(onDateChangeStub.getCall(0).args[0]).to.equal(date);
+ });
+
describe('props.keepOpenOnDateSelect is false', () => {
it('props.onFocusChange is called', () => {
const onFocusChangeStub = sinon.stub();