Skip to content

Commit e1e0d09

Browse files
Yuiai01dujiaqi
andauthored
fix: dateTime mode switch should trigger onCalendarChange (#669)
* fix: disabledDate not work when using both showTime and changeOnBlur * chore: add test case * feat: dateTime mode switch trigger onCalendarChange * chore: separate the onCalendarChange * chore: change test case * fix: resolve test case error * chore: trigger setSelectedValue inside the change function * chore: restore methods and add param * chore: split the process content --------- Co-authored-by: dujiaqi <[email protected]>
1 parent c23b2b6 commit e1e0d09

File tree

3 files changed

+102
-71
lines changed

3 files changed

+102
-71
lines changed

src/RangePicker.tsx

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,11 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
279279
// ============================= Misc ==============================
280280
const formatList = toArray(getDefaultFormat<DateType>(format, picker, showTime, use12Hours));
281281

282+
const formatDateValue = (values: RangeValue<DateType>, index: 0 | 1) =>
283+
values && values[index]
284+
? formatValue(values[index], { generateConfig, locale, format: formatList[0] })
285+
: '';
286+
282287
// Operation ref
283288
const operationRef: React.MutableRefObject<ContextOperationRefProps | null> =
284289
useRef<ContextOperationRefProps>(null);
@@ -398,7 +403,11 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
398403
}, 0);
399404
}
400405

401-
function triggerChange(newValue: RangeValue<DateType>, sourceIndex: 0 | 1) {
406+
function triggerChange(
407+
newValue: RangeValue<DateType>,
408+
sourceIndex: 0 | 1,
409+
triggerCalendarChangeOnly?: boolean,
410+
) {
402411
let values = newValue;
403412
let startValue = getValue(values, 0);
404413
let endValue = getValue(values, 1);
@@ -432,37 +441,30 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
432441

433442
setSelectedValue(values);
434443

435-
const startStr =
436-
values && values[0]
437-
? formatValue(values[0], { generateConfig, locale, format: formatList[0] })
438-
: '';
439-
const endStr =
440-
values && values[1]
441-
? formatValue(values[1], { generateConfig, locale, format: formatList[0] })
442-
: '';
444+
const startStr = formatDateValue(values, 0);
445+
const endStr = formatDateValue(values, 1);
443446

444447
if (onCalendarChange) {
445448
const info: RangeInfo = { range: sourceIndex === 0 ? 'start' : 'end' };
446449

447450
onCalendarChange(values, [startStr, endStr], info);
448451
}
449452

450-
// >>>>> Trigger `onChange` event
451-
const canStartValueTrigger = canValueTrigger(startValue, 0, mergedDisabled, allowEmpty);
452-
const canEndValueTrigger = canValueTrigger(endValue, 1, mergedDisabled, allowEmpty);
453-
454-
const canTrigger = values === null || (canStartValueTrigger && canEndValueTrigger);
455-
456-
if (canTrigger) {
457-
// Trigger onChange only when value is validate
458-
setInnerValue(values);
459-
460-
if (
461-
onChange &&
462-
(!isEqual(generateConfig, getValue(mergedValue, 0), startValue) ||
463-
!isEqual(generateConfig, getValue(mergedValue, 1), endValue))
464-
) {
465-
onChange(values, [startStr, endStr]);
453+
if (!triggerCalendarChangeOnly) {
454+
// >>>>> Trigger `onChange` event
455+
const canStartValueTrigger = canValueTrigger(startValue, 0, mergedDisabled, allowEmpty);
456+
const canEndValueTrigger = canValueTrigger(endValue, 1, mergedDisabled, allowEmpty);
457+
const canTrigger = values === null || (canStartValueTrigger && canEndValueTrigger);
458+
if (canTrigger) {
459+
// Trigger onChange only when value is validate
460+
setInnerValue(values);
461+
if (
462+
onChange &&
463+
(!isEqual(generateConfig, getValue(mergedValue, 0), startValue) ||
464+
!isEqual(generateConfig, getValue(mergedValue, 1), endValue))
465+
) {
466+
onChange(values, [startStr, endStr]);
467+
}
466468
}
467469
}
468470
}
@@ -570,29 +572,44 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
570572
}, [mergedOpen]);
571573

572574
const onInternalBlur: React.FocusEventHandler<HTMLInputElement> = (e) => {
573-
if (changeOnBlur && delayOpen) {
574-
const selectedIndexValue = getValue(selectedValue, mergedActivePickerIndex);
575-
if (selectedIndexValue) {
576-
triggerChange(selectedValue, mergedActivePickerIndex);
575+
if (delayOpen) {
576+
if (changeOnBlur) {
577+
const selectedIndexValue = getValue(selectedValue, mergedActivePickerIndex);
578+
579+
if (selectedIndexValue) {
580+
triggerChange(selectedValue, mergedActivePickerIndex);
581+
}
582+
} else if (needConfirmButton) {
583+
// when in dateTime mode, switching between two date input fields will trigger onCalendarChange.
584+
// when onBlur is triggered, the input field has already switched,
585+
// so it's necessary to obtain the value of the previous input field here.
586+
const needTriggerIndex = mergedActivePickerIndex ? 0 : 1;
587+
const selectedIndexValue = getValue(selectedValue, needTriggerIndex);
588+
589+
if (selectedIndexValue) {
590+
triggerChange(selectedValue, needTriggerIndex, true);
591+
}
577592
}
578593
}
594+
579595
return onBlur?.(e);
580596
};
581597

582598
const getSharedInputHookProps = (index: 0 | 1, resetText: () => void) => ({
583599
blurToCancel: !changeOnBlur && needConfirmButton,
584600
forwardKeyDown,
585601
onBlur: onInternalBlur,
586-
isClickOutside: (target: EventTarget | null) =>
587-
!elementsContains(
602+
isClickOutside: (target: EventTarget | null) => {
603+
const elementsRefs = [startInputDivRef.current, endInputDivRef.current, containerRef.current];
604+
return !elementsContains(
588605
[
606+
// Filter the ref of the currently selected input to trigger the onBlur event of another input.
607+
...(needConfirmButton ? [elementsRefs[mergedActivePickerIndex]] : elementsRefs),
589608
panelDivRef.current,
590-
startInputDivRef.current,
591-
endInputDivRef.current,
592-
containerRef.current,
593609
],
594610
target as HTMLElement,
595-
),
611+
);
612+
},
596613
onFocus: (e: React.FocusEvent<HTMLInputElement>) => {
597614
if (onFocus) {
598615
onFocus(e);
@@ -637,7 +654,6 @@ function InnerRangePicker<DateType>(props: RangePickerProps<DateType>) {
637654
onKeyDown: (e, preventDefault) => {
638655
onKeyDown?.(e, preventDefault);
639656
},
640-
changeOnBlur,
641657
};
642658

643659
const [startInputProps, { focused: startFocused, typing: startTyping }] = usePickerInput({

src/hooks/usePickerInput.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ export default function usePickerInput({
1616
onCancel,
1717
onFocus,
1818
onBlur,
19-
changeOnBlur,
2019
}: {
2120
open: boolean;
2221
value: string;
@@ -29,7 +28,6 @@ export default function usePickerInput({
2928
onCancel: () => void;
3029
onFocus?: React.FocusEventHandler<HTMLInputElement>;
3130
onBlur?: React.FocusEventHandler<HTMLInputElement>;
32-
changeOnBlur?: boolean;
3331
}): [React.DOMAttributes<HTMLInputElement>, { focused: boolean; typing: boolean }] {
3432
const [typing, setTyping] = useState(false);
3533
const [focused, setFocused] = useState(false);
@@ -160,7 +158,7 @@ export default function usePickerInput({
160158
raf(() => {
161159
preventBlurRef.current = false;
162160
});
163-
} else if (!changeOnBlur && (!focused || clickedOutside)) {
161+
} else if (!blurToCancel && (!focused || clickedOutside)) {
164162
triggerOpen(false);
165163
}
166164
}

tests/range.spec.tsx

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import type { Moment } from 'moment';
33
import moment from 'moment';
44
import KeyCode from 'rc-util/lib/KeyCode';
55
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
6+
import { resetWarned } from 'rc-util/lib/warning';
67
import React from 'react';
78
import type { PickerMode } from '../src/interface';
89
import zhCN from '../src/locale/zh_CN';
10+
import type { RangePickerProps } from '../src/RangePicker';
911
import {
1012
clearValue,
1113
clickButton,
@@ -19,8 +21,6 @@ import {
1921
openPicker,
2022
selectCell,
2123
} from './util/commonUtil';
22-
import type { RangePickerProps } from '../src/RangePicker';
23-
import { resetWarned } from 'rc-util/lib/warning';
2424

2525
describe('Picker.Range', () => {
2626
let errorSpy;
@@ -286,7 +286,9 @@ describe('Picker.Range', () => {
286286
expect(baseElement.querySelectorAll('.rc-picker-input')).toHaveLength(2);
287287
fireEvent.click(baseElement.querySelectorAll('.rc-picker-input')[1]);
288288
expect(baseElement.querySelector('.rc-picker-dropdown-hidden')).toBeFalsy();
289-
fireEvent.click(baseElement.querySelector('.rc-picker-cell-range-start .rc-picker-cell-inner'));
289+
fireEvent.click(
290+
baseElement.querySelector('.rc-picker-cell-range-start .rc-picker-cell-inner'),
291+
);
290292
fireEvent.click(baseElement.querySelector('.rc-picker-ok button'));
291293
expect(baseElement.querySelector('.rc-picker-dropdown-hidden')).toBeTruthy();
292294
});
@@ -361,7 +363,6 @@ describe('Picker.Range', () => {
361363
});
362364

363365
function testRangePickerPresetRange(propsType: 'ranges' | 'presets') {
364-
365366
const genProps = (ranges: Record<string, any>) => {
366367
const props: Partial<RangePickerProps<Moment>> = {};
367368
if (propsType === 'ranges') {
@@ -371,21 +372,19 @@ describe('Picker.Range', () => {
371372
props.presets = [];
372373
Object.entries(ranges).forEach(([label, value]) => {
373374
props.presets.push({ label, value });
374-
})
375+
});
375376
}
376377
return props as RangePickerProps<Moment>;
377-
}
378+
};
378379

379380
it(`${propsType} work`, () => {
380381
const onChange = jest.fn();
381382
const { container } = render(
382383
<MomentRangePicker
383-
{...genProps(
384-
{
385-
test: [getMoment('1989-11-28'), getMoment('1990-09-03')],
386-
func: () => [getMoment('2000-01-01'), getMoment('2010-11-11')],
387-
}
388-
)}
384+
{...genProps({
385+
test: [getMoment('1989-11-28'), getMoment('1990-09-03')],
386+
func: () => [getMoment('2000-01-01'), getMoment('2010-11-11')],
387+
})}
389388
onChange={onChange}
390389
/>,
391390
);
@@ -420,11 +419,9 @@ describe('Picker.Range', () => {
420419
it(`${propsType} hover className`, () => {
421420
const { container } = render(
422421
<MomentRangePicker
423-
{...genProps(
424-
{
425-
now: [getMoment('1990-09-11'), getMoment('1990-09-13')],
426-
}
427-
)}
422+
{...genProps({
423+
now: [getMoment('1990-09-11'), getMoment('1990-09-13')],
424+
})}
428425
/>,
429426
);
430427

@@ -439,7 +436,6 @@ describe('Picker.Range', () => {
439436
expect(findCell(12)).not.toHaveClass('rc-picker-cell-in-range');
440437
expect(findCell(13)).not.toHaveClass('rc-picker-cell-range-end');
441438
});
442-
443439
}
444440

445441
describe('ranges or presets', () => {
@@ -755,7 +751,7 @@ describe('Picker.Range', () => {
755751

756752
expect(container).toMatchSnapshot();
757753
expect(errorSpy).toHaveBeenCalledWith(
758-
'Warning: `clearIcon` will be removed in future. Please use `allowClear` instead.'
754+
'Warning: `clearIcon` will be removed in future. Please use `allowClear` instead.',
759755
);
760756
});
761757

@@ -1111,19 +1107,19 @@ describe('Picker.Range', () => {
11111107
targetCell: string;
11121108
match: string[];
11131109
}[] = [
1114-
{
1115-
picker: 'week',
1116-
defaultValue: ['2020-06-13'],
1117-
targetCell: '9',
1118-
match: ['2020-24th'],
1119-
},
1120-
{
1121-
picker: 'quarter',
1122-
defaultValue: ['2020-03-30', '2020-05-20'],
1123-
targetCell: 'Q1',
1124-
match: ['2020-Q1'],
1125-
},
1126-
];
1110+
{
1111+
picker: 'week',
1112+
defaultValue: ['2020-06-13'],
1113+
targetCell: '9',
1114+
match: ['2020-24th'],
1115+
},
1116+
{
1117+
picker: 'quarter',
1118+
defaultValue: ['2020-03-30', '2020-05-20'],
1119+
targetCell: 'Q1',
1120+
match: ['2020-Q1'],
1121+
},
1122+
];
11271123

11281124
list.forEach(({ picker, defaultValue, match, targetCell }) => {
11291125
it(picker, () => {
@@ -1927,4 +1923,25 @@ describe('Picker.Range', () => {
19271923
fireEvent.click(document.querySelector('.rc-picker-cell'));
19281924
expect(document.querySelectorAll('.rc-picker-input')[1]).toHaveClass('rc-picker-input-active');
19291925
});
1926+
1927+
it('dateTime mode switch should trigger onCalendarChange', () => {
1928+
const onCalendarChange = jest.fn();
1929+
const { container } = render(
1930+
<MomentRangePicker
1931+
showTime
1932+
onCalendarChange={onCalendarChange}
1933+
/>,
1934+
);
1935+
1936+
openPicker(container, 0);
1937+
1938+
selectCell(8, 0);
1939+
1940+
openPicker(container, 1);
1941+
1942+
// onBlur is triggered when the switch is complete
1943+
closePicker(container, 0);
1944+
1945+
expect(onCalendarChange).toHaveBeenCalled();
1946+
});
19301947
});

0 commit comments

Comments
 (0)