diff --git a/docs/examples/basic.tsx b/docs/examples/basic.tsx index f9012e1e6..e1357e44d 100644 --- a/docs/examples/basic.tsx +++ b/docs/examples/basic.tsx @@ -47,7 +47,7 @@ export default () => {

Basic

- {...sharedProps} locale={zhCN} /> + {...sharedProps} locale={zhCN} suffixIcon="SUFFIX" /> {...sharedProps} locale={enUS} />
@@ -79,6 +79,7 @@ export default () => { } return {}; }} + changeOnBlur />
diff --git a/docs/examples/range.tsx b/docs/examples/range.tsx index 7e93df40e..3cb68d413 100644 --- a/docs/examples/range.tsx +++ b/docs/examples/range.tsx @@ -1,10 +1,10 @@ -import React from 'react'; import type { Moment } from 'moment'; import moment from 'moment'; -import RangePicker from '../../src/RangePicker'; +import React from 'react'; +import '../../assets/index.less'; import momentGenerateConfig from '../../src/generate/moment'; import zhCN from '../../src/locale/zh_CN'; -import '../../assets/index.less'; +import RangePicker from '../../src/RangePicker'; import './common.less'; const defaultStartValue = moment('2019-09-03 05:02:03'); @@ -70,13 +70,18 @@ export default () => { ref={rangePickerRef} showTime style={{ width: 580 }} - cellRender={(current, info) =>
{typeof current === "number" ? current : current.get("date")}
} + cellRender={(current, info) => ( +
+ {typeof current === 'number' ? current : current.get('date')} +
+ )} ranges={{ ranges: [moment(), moment().add(10, 'day')], }} onOk={(dates) => { console.log('OK!!!', dates); }} + changeOnBlur /> {...sharedProps} diff --git a/package.json b/package.json index 161a6e99b..c3ebeda81 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^1.5.0", "classnames": "^2.2.1", - "rc-util": "^5.27.0" + "rc-util": "^5.30.0" }, "engines": { "node": ">=8.x" diff --git a/src/Picker.tsx b/src/Picker.tsx index d4a63b610..0a26e9b9f 100644 --- a/src/Picker.tsx +++ b/src/Picker.tsx @@ -11,8 +11,8 @@ * Tips: Should add faq about `datetime` mode with `defaultValue` */ -import classNames from 'classnames'; import type { AlignType } from '@rc-component/trigger/lib/interface'; +import classNames from 'classnames'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; import warning from 'rc-util/lib/warning'; import * as React from 'react'; @@ -87,6 +87,12 @@ export type PickerSharedProps = { onContextMenu?: React.MouseEventHandler; onKeyDown?: (event: React.KeyboardEvent, preventDefault: () => void) => void; + /** + * Trigger `onChange` event when blur. + * If you don't want to user click `confirm` to trigger change, can use this. + */ + changeOnBlur?: boolean; + // Internal /** @private Internal usage, do not use in production mode!!! */ pickerRef?: React.MutableRefObject; @@ -182,6 +188,7 @@ function InnerPicker(props: PickerProps) { direction, autoComplete = 'off', inputRender, + changeOnBlur, } = props as MergedPickerProps; const inputRef = React.useRef(null); @@ -301,6 +308,14 @@ function InnerPicker(props: PickerProps) { }; // ============================= Input ============================= + const onInternalBlur: React.FocusEventHandler = (e) => { + if (changeOnBlur) { + triggerChange(selectedValue); + } + + onBlur?.(e); + }; + const [inputProps, { focused, typing }] = usePickerInput({ blurToCancel: needConfirmButton, open: mergedOpen, @@ -336,7 +351,8 @@ function InnerPicker(props: PickerProps) { onKeyDown?.(e, preventDefault); }, onFocus, - onBlur, + onBlur: onInternalBlur, + changeOnBlur, }); // ============================= Sync ============================== @@ -450,7 +466,17 @@ function InnerPicker(props: PickerProps) { let suffixNode: React.ReactNode; if (suffixIcon) { - suffixNode = {suffixIcon}; + suffixNode = ( + { + // Not lost focus + e.preventDefault(); + }} + > + {suffixIcon} + + ); } let clearNode: React.ReactNode; diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx index fff3cfa91..5fbf0984d 100644 --- a/src/RangePicker.tsx +++ b/src/RangePicker.tsx @@ -1,5 +1,6 @@ import classNames from 'classnames'; import useMergedState from 'rc-util/lib/hooks/useMergedState'; +import raf from 'rc-util/lib/raf'; import warning from 'rc-util/lib/warning'; import * as React from 'react'; import { useEffect, useRef, useState } from 'react'; @@ -10,6 +11,7 @@ import useHoverValue from './hooks/useHoverValue'; import usePickerInput from './hooks/usePickerInput'; import usePresets from './hooks/usePresets'; import useRangeDisabled from './hooks/useRangeDisabled'; +import useRangeOpen from './hooks/useRangeOpen'; import useRangeViewDates from './hooks/useRangeViewDates'; import useTextValueMapping from './hooks/useTextValueMapping'; import useValueTexts from './hooks/useValueTexts'; @@ -130,6 +132,11 @@ export type RangePickerSharedProps = { dateRender?: RangeDateRender; cellRender?: CellRender; panelRender?: (originPanel: React.ReactNode) => React.ReactNode; + /** + * Trigger `onChange` event when blur. + * If you don't want to user click `confirm` to trigger change, can use this. + */ + changeOnBlur?: boolean; }; type OmitPickerProps = Omit< @@ -244,13 +251,11 @@ function InnerRangePicker(props: RangePickerProps) { direction, activePickerIndex, autoComplete = 'off', + changeOnBlur, } = props as MergedRangePickerProps; const needConfirmButton: boolean = (picker === 'date' && !!showTime) || picker === 'time'; - // We record opened status here in case repeat open with picker - const openRecordsRef = useRef>({}); - const containerRef = useRef(null); const panelDivRef = useRef(null); const startInputDivRef = useRef(null); @@ -268,11 +273,6 @@ function InnerRangePicker(props: RangePickerProps) { // ============================= Misc ============================== const formatList = toArray(getDefaultFormat(format, picker, showTime, use12Hours)); - // Active picker - const [mergedActivePickerIndex, setMergedActivePickerIndex] = useMergedState<0 | 1>(0, { - value: activePickerIndex, - }); - // Operation ref const operationRef: React.MutableRefObject = useRef(null); @@ -343,6 +343,22 @@ function InnerRangePicker(props: RangePickerProps) { } }; + // ============================= Open ============================== + const [mergedOpen, mergedActivePickerIndex, firstTimeOpen, triggerOpen] = useRangeOpen( + defaultOpen, + open, + activePickerIndex, + changeOnBlur, + startInputRef, + endInputRef, + getValue(selectedValue, 0), + getValue(selectedValue, 1), + onOpenChange, + ); + + const startOpen = mergedOpen && mergedActivePickerIndex === 0; + const endOpen = mergedOpen && mergedActivePickerIndex === 1; + // ========================= Disable Date ========================== const [disabledStartDate, disabledEndDate] = useRangeDisabled( { @@ -353,29 +369,9 @@ function InnerRangePicker(props: RangePickerProps) { disabledDate, generateConfig, }, - openRecordsRef.current[1], - openRecordsRef.current[0], + firstTimeOpen, ); - // ============================= Open ============================== - const [mergedOpen, triggerInnerOpen] = useMergedState(false, { - value: open, - defaultValue: defaultOpen, - postState: (postOpen) => (mergedDisabled[mergedActivePickerIndex] ? false : postOpen), - onChange: (newOpen) => { - if (onOpenChange) { - onOpenChange(newOpen); - } - - if (!newOpen && operationRef.current && operationRef.current.onClose) { - operationRef.current.onClose(); - } - }, - }); - - const startOpen = mergedOpen && mergedActivePickerIndex === 0; - const endOpen = mergedOpen && mergedActivePickerIndex === 1; - // ============================= Popup ============================= // Popup min width const [popupMinWidth, setPopupMinWidth] = useState(0); @@ -386,38 +382,10 @@ function InnerRangePicker(props: RangePickerProps) { }, [mergedOpen]); // ============================ Trigger ============================ - const triggerRef = React.useRef(); - - function triggerOpen(newOpen: boolean, index: 0 | 1) { - if (newOpen) { - clearTimeout(triggerRef.current); - openRecordsRef.current[index] = true; - - setMergedActivePickerIndex(index); - triggerInnerOpen(newOpen); - - // Open to reset view date - if (!mergedOpen) { - setViewDate(null, index); - } - } else if (mergedActivePickerIndex === index) { - triggerInnerOpen(newOpen); - - // Clean up async - // This makes ref not quick refresh in case user open another input with blur trigger - const openRecords = openRecordsRef.current; - triggerRef.current = setTimeout(() => { - if (openRecords === openRecordsRef.current) { - openRecordsRef.current = {}; - } - }); - } - } - function triggerOpenAndFocus(index: 0 | 1) { - triggerOpen(true, index); + triggerOpen(true, index, 'open'); // Use setTimeout to make sure panel DOM exists - setTimeout(() => { + raf(() => { const inputRef = [startInputRef, endInputRef][index]; if (inputRef.current) { inputRef.current.focus(); @@ -451,11 +419,6 @@ function InnerRangePicker(props: RangePickerProps) { startValue = null; values = [null, endValue]; } - - // Clean up cache since invalidate - openRecordsRef.current = { - [sourceIndex]: true, - }; } else if (picker !== 'time' || order !== false) { // Reorder when in same date values = reorderValues(values, generateConfig); @@ -497,28 +460,6 @@ function InnerRangePicker(props: RangePickerProps) { onChange(values, [startStr, endStr]); } } - - // >>>>> Open picker when - - // Always open another picker if possible - let nextOpenIndex: 0 | 1 = null; - if (sourceIndex === 0 && !mergedDisabled[1]) { - nextOpenIndex = 1; - } else if (sourceIndex === 1 && !mergedDisabled[0]) { - nextOpenIndex = 0; - } - - if ( - nextOpenIndex !== null && - nextOpenIndex !== mergedActivePickerIndex && - (!openRecordsRef.current[nextOpenIndex] || !getValue(values, nextOpenIndex)) && - getValue(values, sourceIndex) - ) { - // Delay to focus to avoid input blur trigger expired selectedValues - triggerOpenAndFocus(nextOpenIndex); - } else { - triggerOpen(false, sourceIndex); - } } const forwardKeyDown = (e: React.KeyboardEvent) => { @@ -616,10 +557,20 @@ function InnerRangePicker(props: RangePickerProps) { }; // ============================= Input ============================= + const onInternalBlur: React.FocusEventHandler = (e) => { + if (changeOnBlur) { + const selectedIndexValue = getValue(selectedValue, mergedActivePickerIndex); + if (selectedIndexValue) { + triggerChange(selectedValue, mergedActivePickerIndex); + } + } + return onBlur?.(e); + }; + const getSharedInputHookProps = (index: 0 | 1, resetText: () => void) => ({ - blurToCancel: needConfirmButton, + blurToCancel: !changeOnBlur && needConfirmButton, forwardKeyDown, - onBlur, + onBlur: onInternalBlur, isClickOutside: (target: EventTarget | null) => !elementsContains( [ @@ -631,13 +582,21 @@ function InnerRangePicker(props: RangePickerProps) { target as HTMLElement, ), onFocus: (e: React.FocusEvent) => { - setMergedActivePickerIndex(index); if (onFocus) { onFocus(e); } }, triggerOpen: (newOpen: boolean) => { - triggerOpen(newOpen, index); + if (newOpen) { + triggerOpen(newOpen, index, 'open'); + } else { + triggerOpen( + newOpen, + // Close directly if no selected value provided + getValue(selectedValue, index) ? index : false, + 'blur', + ); + } }, onSubmit: () => { if ( @@ -651,30 +610,36 @@ function InnerRangePicker(props: RangePickerProps) { triggerChange(selectedValue, index); resetText(); + + // Switch + triggerOpen(false, mergedActivePickerIndex, 'confirm'); }, onCancel: () => { - triggerOpen(false, index); + triggerOpen(false, index, 'cancel'); setSelectedValue(mergedValue); resetText(); }, }); + const sharedPickerInput = { + onKeyDown: (e, preventDefault) => { + onKeyDown?.(e, preventDefault); + }, + changeOnBlur, + }; + const [startInputProps, { focused: startFocused, typing: startTyping }] = usePickerInput({ ...getSharedInputHookProps(0, resetStartText), open: startOpen, value: startText, - onKeyDown: (e, preventDefault) => { - onKeyDown?.(e, preventDefault); - }, + ...sharedPickerInput, }); const [endInputProps, { focused: endFocused, typing: endTyping }] = usePickerInput({ ...getSharedInputHookProps(1, resetEndText), open: endOpen, value: endText, - onKeyDown: (e, preventDefault) => { - onKeyDown?.(e, preventDefault); - }, + ...sharedPickerInput, }); // ========================== Click Picker ========================== @@ -758,7 +723,7 @@ function InnerRangePicker(props: RangePickerProps) { monthCellRender, dateRender, }); - + const panelDateRender = React.useMemo(() => { if (!mergedCellRender) return undefined; return (date: DateType, info: CellRenderInfo) => @@ -943,12 +908,13 @@ function InnerRangePicker(props: RangePickerProps) { locale, // rangeList, onOk: () => { - if (getValue(selectedValue, mergedActivePickerIndex)) { - // triggerChangeOld(selectedValue); + const selectedIndexValue = getValue(selectedValue, mergedActivePickerIndex); + if (selectedIndexValue) { triggerChange(selectedValue, mergedActivePickerIndex); - if (onOk) { - onOk(selectedValue); - } + onOk?.(selectedValue); + + // Switch + triggerOpen(false, mergedActivePickerIndex, 'confirm'); } }, }); @@ -1001,7 +967,7 @@ function InnerRangePicker(props: RangePickerProps) { presets={presetList} onClick={(nextValue) => { triggerChange(nextValue, null); - triggerOpen(false, mergedActivePickerIndex); + triggerOpen(false, mergedActivePickerIndex, 'preset'); }} onHover={(hoverValue) => { setRangeHoverValue(hoverValue); @@ -1051,7 +1017,17 @@ function InnerRangePicker(props: RangePickerProps) { // ============================= Icons ============================= let suffixNode: React.ReactNode; if (suffixIcon) { - suffixNode = {suffixIcon}; + suffixNode = ( + { + // Not lost focus + e.preventDefault(); + }} + > + {suffixIcon} + + ); } let clearNode: React.ReactNode; @@ -1079,7 +1055,7 @@ function InnerRangePicker(props: RangePickerProps) { } triggerChange(values, null); - triggerOpen(false, mergedActivePickerIndex); + triggerOpen(false, mergedActivePickerIndex, 'clear'); }} className={`${prefixCls}-clear`} > @@ -1117,6 +1093,9 @@ function InnerRangePicker(props: RangePickerProps) { } else { onEndLeave(); } + + // Switch + triggerOpen(false, mergedActivePickerIndex, 'confirm'); } else { setSelectedValue(values); } diff --git a/src/hooks/usePickerInput.ts b/src/hooks/usePickerInput.ts index d2663daec..5e0ffac65 100644 --- a/src/hooks/usePickerInput.ts +++ b/src/hooks/usePickerInput.ts @@ -1,4 +1,5 @@ import KeyCode from 'rc-util/lib/KeyCode'; +import raf from 'rc-util/lib/raf'; import type * as React from 'react'; import { useEffect, useRef, useState } from 'react'; import { addGlobalMouseDownEvent, getTargetFromEvent } from '../utils/uiUtil'; @@ -15,6 +16,7 @@ export default function usePickerInput({ onCancel, onFocus, onBlur, + changeOnBlur, }: { open: boolean; value: string; @@ -27,6 +29,7 @@ export default function usePickerInput({ onCancel: () => void; onFocus?: React.FocusEventHandler; onBlur?: React.FocusEventHandler; + changeOnBlur?: boolean; }): [React.DOMAttributes, { focused: boolean; typing: boolean }] { const [typing, setTyping] = useState(false); const [focused, setFocused] = useState(false); @@ -130,9 +133,7 @@ export default function usePickerInput({ } setFocused(false); - if (onBlur) { - onBlur(e); - } + onBlur?.(e); }, }; @@ -156,14 +157,12 @@ export default function usePickerInput({ preventBlurRef.current = true; // Always set back in case `onBlur` prevented by user - requestAnimationFrame(() => { + raf(() => { preventBlurRef.current = false; }); - } else if (!focused || clickedOutside) { + } else if (!changeOnBlur && (!focused || clickedOutside)) { triggerOpen(false); } - } else if (focused && !clickedOutside) { - preventBlurRef.current = true; } }), ); diff --git a/src/hooks/useRangeDisabled.ts b/src/hooks/useRangeDisabled.ts index c01b81673..a201da5d7 100644 --- a/src/hooks/useRangeDisabled.ts +++ b/src/hooks/useRangeDisabled.ts @@ -1,8 +1,8 @@ import * as React from 'react'; -import type { RangeValue, PickerMode, Locale } from '../interface'; -import { getValue } from '../utils/miscUtil'; import type { GenerateConfig } from '../generate'; -import { isSameDate, getQuarter } from '../utils/dateUtil'; +import type { Locale, PickerMode, RangeValue } from '../interface'; +import { getQuarter, isSameDate } from '../utils/dateUtil'; +import { getValue } from '../utils/miscUtil'; export default function useRangeDisabled( { @@ -20,8 +20,7 @@ export default function useRangeDisabled( locale: Locale; generateConfig: GenerateConfig; }, - disabledStart: boolean, - disabledEnd: boolean, + firstTimeOpen: boolean, ) { const startDate = getValue(selectedValue, 0); const endDate = getValue(selectedValue, 1); @@ -54,7 +53,7 @@ export default function useRangeDisabled( } // Disabled part - if (disabledStart && endDate) { + if (!firstTimeOpen && endDate) { switch (picker) { case 'quarter': return quarterNumber(date) > quarterNumber(endDate); @@ -71,7 +70,7 @@ export default function useRangeDisabled( return false; }, - [disabledDate, disabled[1], endDate, disabledStart], + [disabledDate, disabled[1], endDate, firstTimeOpen], ); const disabledEndDate = React.useCallback( @@ -88,7 +87,7 @@ export default function useRangeDisabled( } // Disabled part - if (disabledEnd && startDate) { + if (!firstTimeOpen && startDate) { switch (picker) { case 'quarter': return quarterNumber(date) < quarterNumber(startDate); @@ -106,7 +105,7 @@ export default function useRangeDisabled( return false; }, - [disabledDate, disabled[0], startDate, disabledEnd], + [disabledDate, disabled[0], startDate, firstTimeOpen], ); return [disabledStartDate, disabledEndDate]; diff --git a/src/hooks/useRangeOpen.ts b/src/hooks/useRangeOpen.ts new file mode 100644 index 000000000..f883e91a8 --- /dev/null +++ b/src/hooks/useRangeOpen.ts @@ -0,0 +1,129 @@ +import { useMergedState } from 'rc-util'; +import useEvent from 'rc-util/lib/hooks/useEvent'; +import raf from 'rc-util/lib/raf'; +import * as React from 'react'; + +/** + * 1. Click input to show picker + * 2. Calculate next open index + * + * If click `confirm`: + * 3. Hide current picker + * 4. Open next index picker if exist + * + * If not `changeOnBlur` and click outside: + * 3. Hide picker + * + * If `changeOnBlur` and click outside: + * 3. Hide current picker + * 4. Open next index picker if exist + */ + +export type SourceType = 'open' | 'blur' | 'confirm' | 'cancel' | 'clear' | 'preset'; + +/** + * Auto control of open state + */ +export default function useRangeOpen( + defaultOpen: boolean, + open: boolean, + activePickerIndex: 0 | 1 | undefined, + changeOnBlur: boolean, + startInputRef: React.RefObject, + endInputRef: React.RefObject, + startSelectedValue: any, + endSelectedValue: any, + onOpenChange?: (open: boolean) => void, +): [ + open: boolean, + activeIndex: 0 | 1, + firstTimeOpen: boolean, + triggerOpen: (open: boolean, activeIndex: 0 | 1 | false, source: SourceType) => void, +] { + // We record opened status here in case repeat open with picker + // const [openRecord, setOpenRecord] = React.useState<{ + // 0?: boolean; + // 1?: boolean; + // }>({}); + + const [firstTimeOpen, setFirstTimeOpen] = React.useState(false); + + const [mergedOpen, setMergedOpen] = useMergedState(defaultOpen || false, { + value: open, + onChange: (nextOpen) => { + onOpenChange?.(nextOpen); + }, + }); + + const [mergedActivePickerIndex, setMergedActivePickerIndex] = useMergedState<0 | 1>(0, { + value: activePickerIndex, + }); + + const [nextActiveIndex, setNextActiveIndex] = React.useState<0 | 1>(null); + + const triggerOpen = useEvent((nextOpen: boolean, index: 0 | 1 | false, source: SourceType) => { + // console.error('✅', nextOpen, index, source, startSelectedValue, endSelectedValue); + + if (index === false) { + // Only when `nextOpen` is false and no need open to next index + setMergedOpen(nextOpen); + } else if (nextOpen) { + setMergedActivePickerIndex(index); + setMergedOpen(nextOpen); + + const nextIndex = index === 0 ? 1 : 0; + + // Record next open index + if ( + !mergedOpen || + // Also set next index if next is empty + ![startSelectedValue, endSelectedValue][nextIndex] + ) { + // Reset open record + // setOpenRecord({ + // [index]: true, + // }); + setFirstTimeOpen(true); + setNextActiveIndex(nextIndex); + } else { + // setOpenRecord((ori) => ({ + // ...ori, + // [index]: true, + // })); + setFirstTimeOpen(false); + + if (nextActiveIndex !== null) { + setNextActiveIndex(null); + } + } + } else if (source === 'confirm' || (source === 'blur' && changeOnBlur)) { + // Close if current value is empty + // const selectedValue = [startSelectedValue, endSelectedValue][index]; + + // if (!selectedValue) { + // setMergedOpen(false); + // return; + // } + + if (nextActiveIndex !== null) { + setFirstTimeOpen(false); + setMergedActivePickerIndex(nextActiveIndex); + } + setNextActiveIndex(null); + + // Focus back + if (nextActiveIndex !== null) { + raf(() => { + const ref = [startInputRef, endInputRef][nextActiveIndex]; + ref.current?.focus(); + }); + } else { + setMergedOpen(false); + } + } else { + setMergedOpen(false); + } + }); + + return [mergedOpen, mergedActivePickerIndex, firstTimeOpen, triggerOpen]; +} diff --git a/tests/blur.spec.tsx b/tests/blur.spec.tsx new file mode 100644 index 000000000..65c4c926b --- /dev/null +++ b/tests/blur.spec.tsx @@ -0,0 +1,70 @@ +import { fireEvent, render } from '@testing-library/react'; +import React from 'react'; +import { getMoment, MomentPicker, MomentRangePicker } from './util/commonUtil'; + +describe('Picker.ChangeOnBlur', () => { + beforeEach(() => { + jest.useFakeTimers().setSystemTime(getMoment('1990-09-03 00:00:00').valueOf()); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); + }); + + it('Picker', () => { + const onSelect = jest.fn(); + const onChange = jest.fn(); + + const { container } = render( + <> + +
} />, ); + + const $input = container.querySelector('input'); + openPicker(container); + $input.focus(); keyDown(KeyCode.ESC); - fireEvent.mouseDown(container.querySelector('.suffix-icon')); - fireEvent.blur(container.querySelector('input')); - expect(onBlur).toHaveBeenCalledTimes(0); - fireEvent.blur(container.querySelector('input')); - expect(onBlur).toHaveBeenCalledTimes(1); + expect(document.activeElement).toBe($input); + + // Click suffix should preventDefault + const $suffix = container.querySelector('.suffix-icon'); + const mouseDownEvent = createEvent.mouseDown($suffix); + mouseDownEvent.preventDefault = jest.fn(); + fireEvent($suffix, mouseDownEvent); + + expect(mouseDownEvent.preventDefault).toHaveBeenCalled(); }); describe('full steps', () => { @@ -637,7 +646,7 @@ describe('Picker.Basic', () => { it(`should show integer when step is not integer (${unit})`, () => { const props = { [`${unit}Step`]: 5.5, - } + }; const { container } = render(); openPicker(container); expect(document.querySelectorAll('.rc-picker-time-panel-column')[index]).toMatchSnapshot(); diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index 63869f5cc..e075fc61a 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -41,7 +41,7 @@ describe('Picker.Range', () => { }); function keyDown(container: HTMLElement, index: number, keyCode: number) { - fireEvent.keyDown(container.querySelectorAll('input')[0], { + fireEvent.keyDown(container.querySelectorAll('input')[index], { keyCode, which: keyCode, charCode: keyCode, @@ -92,6 +92,7 @@ describe('Picker.Range', () => { onCalendarChange.mockReset(); selectCell(14); + expect(onChange).toHaveBeenCalled(); expect(isSame(onChange.mock.calls[0][0][0], '1990-09-13')).toBeTruthy(); expect(isSame(onChange.mock.calls[0][0][1], '1990-09-14')).toBeTruthy(); expect(onChange.mock.calls[0][1]).toEqual(['1990-09-13', '1990-09-14']); @@ -577,7 +578,7 @@ describe('Picker.Range', () => { // document.querySelector('input').last().simulate('keyDown', { // which: KeyCode.ENTER, // }); - keyDown(container, 1, KeyCode.ENTER); + keyDown(container, 0, KeyCode.ENTER); expect(onChange).not.toHaveBeenCalled(); }); @@ -1152,11 +1153,13 @@ describe('Picker.Range', () => { openPicker(container, 0); inputValue('1990-11-28'); - closePicker(container, 0); + // closePicker(container, 0); + keyDown(container, 0, KeyCode.ENTER); expect(isOpen()).toBeTruthy(); inputValue('1991-01-01'); - closePicker(container, 1); + // closePicker(container, 1); + keyDown(container, 1, KeyCode.ENTER); expect(isOpen()).toBeFalsy(); }); @@ -1168,11 +1171,13 @@ describe('Picker.Range', () => { openPicker(container, 0); inputValue('1990-11-28'); - closePicker(container, 0); + keyDown(container, 0, KeyCode.ENTER); + // closePicker(container, 0); expect(isOpen()).toBeTruthy(); inputValue('1990-12-23'); - closePicker(container, 1); + // closePicker(container, 1); + keyDown(container, 1, KeyCode.ENTER); expect(isOpen()).toBeFalsy(); }); @@ -1183,11 +1188,13 @@ describe('Picker.Range', () => { openPicker(container, 0); inputValue('1989-01-20'); - closePicker(container, 0); + // closePicker(container, 0); + keyDown(container, 0, KeyCode.ENTER); expect(isOpen()).toBeTruthy(); inputValue('1989-01-25'); - closePicker(container, 1); + // closePicker(container, 1); + keyDown(container, 1, KeyCode.ENTER); expect(isOpen()).toBeFalsy(); }); }); @@ -1197,11 +1204,13 @@ describe('Picker.Range', () => { openPicker(container, 1); inputValue('1990-11-28', 1); - closePicker(container, 1); + keyDown(container, 1, KeyCode.ENTER); + // closePicker(container, 1); expect(isOpen()).toBeTruthy(); inputValue('1989-01-01'); - closePicker(container, 0); + // closePicker(container, 0); + keyDown(container, 0, KeyCode.ENTER); expect(isOpen()).toBeFalsy(); }); @@ -1213,11 +1222,13 @@ describe('Picker.Range', () => { openPicker(container, 1); inputValue('1990-11-28', 1); - closePicker(container, 1); + keyDown(container, 1, KeyCode.ENTER); + // closePicker(container, 1); expect(isOpen()).toBeTruthy(); inputValue('1989-01-01'); - closePicker(container, 0); + keyDown(container, 0, KeyCode.ENTER); + // closePicker(container, 0); expect(isOpen()).toBeFalsy(); }); @@ -1228,11 +1239,12 @@ describe('Picker.Range', () => { openPicker(container, 1); inputValue('1989-01-07', 1); - closePicker(container, 1); + console.log('close!'); + keyDown(container, 1, KeyCode.ENTER); expect(isOpen()).toBeTruthy(); inputValue('1989-01-01'); - closePicker(container, 0); + keyDown(container, 0, KeyCode.ENTER); expect(isOpen()).toBeFalsy(); }); }); @@ -1269,21 +1281,15 @@ describe('Picker.Range', () => { }); it("shouldn't let mousedown blur the input", () => { jest.useFakeTimers(); - // const preventDefault = jest.fn(); const { container } = render(); const node = container.querySelector('.rc-picker'); - // document.querySelector('.rc-picker').simulate('click'); fireEvent.click(node); act(() => { jest.runAllTimers(); }); - // document.querySelector('.rc-picker').simulate('mousedown', { - // preventDefault, - // }); const mouseDownEvent = createEvent.mouseDown(node); fireEvent(node, mouseDownEvent); expect(isOpen()).toBeTruthy(); - // expect(preventDefault).toHaveBeenCalled(); expect(mouseDownEvent.defaultPrevented).toBeTruthy(); jest.useRealTimers(); }); @@ -1514,9 +1520,12 @@ describe('Picker.Range', () => { expect(document.querySelectorAll('input')[0].value).toBe('1990-09-07'); // back to first panel and clear input value + fireEvent.mouseDown(document.querySelectorAll('input')[0]); fireEvent.focus(document.querySelectorAll('input')[0]); inputValue('', 0); + console.log(container.querySelector('.rc-picker').innerHTML); + // reselect date selectCell(9, 0); expect(document.querySelectorAll('input')[0].value).toBe('1990-09-09'); @@ -1541,6 +1550,7 @@ describe('Picker.Range', () => { selectCell(15); fireEvent.click(document.querySelector('.rc-picker-month-btn')); + expect(findCell('Jan')).toHaveClass('rc-picker-cell-disabled'); expect(findCell('Dec')).not.toHaveClass('rc-picker-cell-disabled'); }); @@ -1785,7 +1795,7 @@ describe('Picker.Range', () => { it('use dateRender and monthCellRender in month range picker', () => { const { container, baseElement } = render(
{date.get('date')}
} monthCellRender={(date) =>
{date.get('month') + 1}
} />, @@ -1796,7 +1806,7 @@ describe('Picker.Range', () => { it('use dateRender and monthCellRender in date range picker', () => { const { container, baseElement } = render(
{date.get('date')}
} monthCellRender={(date) =>
{date.get('month') + 1}
} />,