From cf6b6146c0e032b19db0e2fcb1e002aa9bd40480 Mon Sep 17 00:00:00 2001 From: demir-selmanovich Date: Fri, 8 May 2020 22:47:48 +0200 Subject: [PATCH 1/4] Add optional props for disabling start or end date --- README.md | 3 +++ examples/range.tsx | 48 ++++++++++++++++++----------------- src/RangePicker.tsx | 14 +++++++--- src/hooks/useRangeDisabled.ts | 15 ++++++++--- 4 files changed, 49 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index da9812ac1..965a4c4a6 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..9bb7f651d 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,21 +21,23 @@ 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; @@ -43,6 +47,9 @@ export default function useRangeDisabled({ const disableEndDate = React.useCallback( (date: DateType) => { + if (disabledPickerEndDate) { + return disabledPickerEndDate(date); + } if (disabledDate && disabledDate(date)) { return true; } From 112d313ca481f487c4dde08a4d0dcd689e6d9709 Mon Sep 17 00:00:00 2001 From: demir-selmanovich Date: Fri, 8 May 2020 23:07:14 +0200 Subject: [PATCH 2/4] Fix test --- tests/range.spec.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index e72ef9c5b..41220d405 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', () => { From 162ad0422ec95757bf316629427580ef44795746 Mon Sep 17 00:00:00 2001 From: demir-selmanovich Date: Tue, 12 May 2020 00:18:27 +0200 Subject: [PATCH 3/4] Update useCallback dependencies --- src/hooks/useRangeDisabled.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useRangeDisabled.ts b/src/hooks/useRangeDisabled.ts index 9bb7f651d..e61031975 100644 --- a/src/hooks/useRangeDisabled.ts +++ b/src/hooks/useRangeDisabled.ts @@ -42,7 +42,7 @@ export default function useRangeDisabled({ return false; }, - [disabledDate, disabled[1], endDate], + [disabledDate, disabled[1], endDate, disabledPickerStartDate], ); const disableEndDate = React.useCallback( @@ -66,7 +66,7 @@ export default function useRangeDisabled({ return false; }, - [disabledDate, startDate, picker], + [disabledDate, startDate, picker, disabledPickerEndDate], ); // Handle week date disabled From 7f7941a3fba3f2712d04c2e466485a1ce4147a45 Mon Sep 17 00:00:00 2001 From: demir-selmanovich Date: Tue, 12 May 2020 00:35:35 +0200 Subject: [PATCH 4/4] Add test with new props included --- tests/range.spec.tsx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index 41220d405..256af1661 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -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(); + }); });