diff --git a/README.md b/README.md
index 2f45e2e1e..4230d9c7c 100644
--- a/README.md
+++ b/README.md
@@ -132,6 +132,9 @@ render(, mountNode);
| onCalendarChange | Function(value:[moment], formatString: [string, string]) | | a callback function, can be executed when the start time or the end time of the range is changing |
| direction | String: ltr or rtl | | Layout direction of picker component, it supports RTL direction too. |
| order | Boolean | true | (TimeRangePicker only) `false` to disable auto order |
+| order | Boolean | true | (TimeRangePicker only) `false` to disable auto order |
+| disabledPickerStartDate | Function(startDate: moment):boolean | | disable start date |
+| disabledPickerEndDate | Function(endDate: moment):boolean | | disable end date |
### showTime-options
diff --git a/examples/range.tsx b/examples/range.tsx
index 53a774d2f..843900070 100644
--- a/examples/range.tsx
+++ b/examples/range.tsx
@@ -14,14 +14,12 @@ function formatDate(date: Moment | null) {
}
export default () => {
- const [value, setValue] = React.useState<
- [Moment | null, Moment | null] | null
- >([defaultStartValue, defaultEndValue]);
+ const [value, setValue] = React.useState<[Moment | null, Moment | null] | null>([
+ defaultStartValue,
+ defaultEndValue,
+ ]);
- const onChange = (
- newValue: [Moment | null, Moment | null] | null,
- formatStrings?: string[],
- ) => {
+ const onChange = (newValue: [Moment | null, Moment | null] | null, formatStrings?: string[]) => {
console.log('Change:', newValue, formatStrings);
setValue(newValue);
};
@@ -33,6 +31,12 @@ export default () => {
console.log('Calendar Change:', newValue, formatStrings);
};
+ const onChangeWithAllDatesEnabled = (newValue: [Moment | null, Moment | null] | null) => {
+ if (newValue[0] && newValue[1] && newValue[1].isBefore(newValue[0])) {
+ setValue([newValue[1], newValue[0]]);
+ }
+ };
+
const sharedProps = {
generateConfig: momentGenerateConfig,
value,
@@ -44,10 +48,7 @@ export default () => {
return (
-
- Value:{' '}
- {value ? `${formatDate(value[0])} ~ ${formatDate(value[1])}` : 'null'}
-
+
Value: {value ? `${formatDate(value[0])} ~ ${formatDate(value[1])}` : 'null'}
@@ -132,21 +133,11 @@ export default () => {
Start disabled
-
- {...sharedProps}
- locale={zhCN}
- allowClear
- disabled={[true, false]}
- />
+ {...sharedProps} locale={zhCN} allowClear disabled={[true, false]} />
End disabled
-
- {...sharedProps}
- locale={zhCN}
- allowClear
- disabled={[false, true]}
- />
+ {...sharedProps} locale={zhCN} allowClear disabled={[false, true]} />
@@ -161,6 +152,17 @@ export default () => {
renderExtraFooter={() =>
extra footer
}
/>
+
+
All dates enabled
+
+ {...sharedProps}
+ onChange={onChangeWithAllDatesEnabled}
+ value={undefined}
+ locale={zhCN}
+ disabledPickerEndDate={() => false}
+ disabledPickerStartDate={() => false}
+ />
+
);
diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx
index 09b9f17e2..27b667a47 100644
--- a/src/RangePicker.tsx
+++ b/src/RangePicker.tsx
@@ -78,6 +78,8 @@ export interface RangePickerSharedProps
{
direction?: 'ltr' | 'rtl';
/** @private Internal control of active picker. Do not use since it's private usage */
activePickerIndex?: 0 | 1;
+ disabledPickerStartDate?: (startDate: DateType) => boolean;
+ disabledPickerEndDate?: (endDate: DateType) => boolean;
}
type OmitPickerProps = Omit<
@@ -182,6 +184,8 @@ function InnerRangePicker(props: RangePickerProps) {
order,
direction,
activePickerIndex,
+ disabledPickerStartDate,
+ disabledPickerEndDate,
} = props as MergedRangePickerProps;
const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time';
@@ -288,6 +292,8 @@ function InnerRangePicker(props: RangePickerProps) {
disabled: mergedDisabled,
disabledDate,
generateConfig,
+ disabledPickerStartDate,
+ disabledPickerEndDate,
});
// ============================= Open ==============================
@@ -332,13 +338,13 @@ function InnerRangePicker(props: RangePickerProps) {
let values = newValue;
const startValue = getValue(values, 0);
- let endValue = getValue(values, 1);
+ const endValue = getValue(values, 1);
if (startValue && endValue && generateConfig.isAfter(startValue, endValue)) {
if (!isSameDate(generateConfig, startValue, endValue)) {
- // Clean up end date when start date is after end date
- values = [startValue, null];
- endValue = null;
+ // if user selects start date while end date picker is opened
+ // then set the start date at proper position
+ values = [startValue, endValue];
} else if (picker !== 'time' || order !== false) {
// Reorder when in same date
values = reorderValues(values, generateConfig);
diff --git a/src/hooks/useRangeDisabled.ts b/src/hooks/useRangeDisabled.ts
index 302b9342f..e61031975 100644
--- a/src/hooks/useRangeDisabled.ts
+++ b/src/hooks/useRangeDisabled.ts
@@ -12,6 +12,8 @@ export default function useRangeDisabled({
disabledDate,
disabled,
generateConfig,
+ disabledPickerStartDate,
+ disabledPickerEndDate,
}: {
picker: PickerMode;
selectedValue: RangeValue;
@@ -19,30 +21,35 @@ export default function useRangeDisabled({
disabled: [boolean, boolean];
locale: Locale;
generateConfig: GenerateConfig;
+ disabledPickerStartDate?: (startDateToDisable: DateType) => boolean;
+ disabledPickerEndDate?: (endDateToDisable: DateType) => boolean;
}) {
const startDate = getValue(selectedValue, 0);
const endDate = getValue(selectedValue, 1);
const disabledStartDate = React.useCallback(
(date: DateType) => {
+ if (disabledPickerStartDate) {
+ return disabledPickerStartDate(date);
+ }
if (disabledDate && disabledDate(date)) {
return true;
}
if (disabled[1] && endDate) {
- return (
- !isSameDate(generateConfig, date, endDate) &&
- generateConfig.isAfter(date, endDate)
- );
+ return !isSameDate(generateConfig, date, endDate) && generateConfig.isAfter(date, endDate);
}
return false;
},
- [disabledDate, disabled[1], endDate],
+ [disabledDate, disabled[1], endDate, disabledPickerStartDate],
);
const disableEndDate = React.useCallback(
(date: DateType) => {
+ if (disabledPickerEndDate) {
+ return disabledPickerEndDate(date);
+ }
if (disabledDate && disabledDate(date)) {
return true;
}
@@ -59,7 +66,7 @@ export default function useRangeDisabled({
return false;
},
- [disabledDate, startDate, picker],
+ [disabledDate, startDate, picker, disabledPickerEndDate],
);
// Handle week date disabled
diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx
index e72ef9c5b..256af1661 100644
--- a/tests/range.spec.tsx
+++ b/tests/range.spec.tsx
@@ -173,7 +173,7 @@ describe('Picker.Range', () => {
expect(wrapper.findCell(11).hasClass('rc-picker-cell-disabled')).toBeFalsy();
});
- it('Reset when startDate is after endDate', () => {
+ it('Set correct start and end date', () => {
const onChange = jest.fn();
const wrapper = mount();
@@ -182,8 +182,8 @@ describe('Picker.Range', () => {
wrapper.openPicker(0);
wrapper.selectCell(23);
- expect(onChange).not.toHaveBeenCalled();
- matchValues(wrapper, '1990-09-23', '');
+ expect(onChange).toHaveBeenCalled();
+ matchValues(wrapper, '1990-09-07', '1990-09-23');
});
it('allowEmpty', () => {
@@ -1063,4 +1063,32 @@ describe('Picker.Range', () => {
.props().id,
).toEqual('bamboo');
});
+
+ it('all dates inside endDate picker should be clickable', () => {
+ const onChange = jest.fn();
+
+ const wrapper = mount(
+ false}
+ disabledPickerStartDate={() => false}
+ />,
+ );
+
+ let cellNode: Wrapper;
+
+ // Start date
+ wrapper.openPicker();
+ wrapper.selectCell(23);
+
+ // End date
+ cellNode = wrapper.selectCell(11);
+ expect(cellNode.hasClass('rc-picker-cell-disabled')).toBeFalsy();
+ expect(onChange).toHaveBeenCalled();
+
+ // Click origin disabled date
+ cellNode = wrapper.selectCell(28);
+ expect(cellNode.hasClass('rc-picker-cell-disabled')).toBeFalsy();
+ expect(onChange).toHaveBeenCalled();
+ });
});