From b1b3b4eee355c7507bc240c31b9141d8066b3b2e Mon Sep 17 00:00:00 2001 From: tangwenhui Date: Thu, 3 Nov 2022 09:52:07 +0800 Subject: [PATCH] fix: should not trigger blur event while picker's input focused --- docs/examples/range.tsx | 3 +++ src/RangePicker.tsx | 6 ++++++ src/hooks/usePickerInput.ts | 23 ++++++++++++++++++----- tests/range.spec.tsx | 26 ++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/docs/examples/range.tsx b/docs/examples/range.tsx index 7c00e8d34..286c0956b 100644 --- a/docs/examples/range.tsx +++ b/docs/examples/range.tsx @@ -62,6 +62,9 @@ export default () => { defaultValue={[moment('1990-09-03'), moment('1989-11-28')]} clearIcon={X} suffixIcon={O} + onBlur={() => { + console.log('trigger blur') + }} /> {...sharedProps} diff --git a/src/RangePicker.tsx b/src/RangePicker.tsx index 9962d935b..732b51f89 100644 --- a/src/RangePicker.tsx +++ b/src/RangePicker.tsx @@ -648,10 +648,13 @@ function InnerRangePicker(props: RangePickerProps) { }, }); + const currentFocusedKey = useRef(''); const [startInputProps, { focused: startFocused, typing: startTyping }] = usePickerInput({ ...getSharedInputHookProps(0, resetStartText), open: startOpen, value: startText, + currentFocusedKey, + key: "start", onKeyDown: (e, preventDefault) => { onKeyDown?.(e, preventDefault); }, @@ -661,11 +664,14 @@ function InnerRangePicker(props: RangePickerProps) { ...getSharedInputHookProps(1, resetEndText), open: endOpen, value: endText, + currentFocusedKey, + key: "end", onKeyDown: (e, preventDefault) => { onKeyDown?.(e, preventDefault); }, }); + // ========================== Click Picker ========================== const onPickerClick = (e: React.MouseEvent) => { // When click inside the picker & outside the picker's input elements diff --git a/src/hooks/usePickerInput.ts b/src/hooks/usePickerInput.ts index 0d54e3fcf..4cb6f4ed6 100644 --- a/src/hooks/usePickerInput.ts +++ b/src/hooks/usePickerInput.ts @@ -15,6 +15,8 @@ export default function usePickerInput({ onCancel, onFocus, onBlur, + currentFocusedKey, + key = 'start' }: { open: boolean; value: string; @@ -27,9 +29,12 @@ export default function usePickerInput({ onCancel: () => void; onFocus?: React.FocusEventHandler; onBlur?: React.FocusEventHandler; + currentFocusedKey?: React.MutableRefObject + key: string; }): [React.DOMAttributes, { focused: boolean; typing: boolean }] { const [typing, setTyping] = useState(false); const [focused, setFocused] = useState(false); + const delayBlurTimer = useRef(); /** * We will prevent blur to handle open event when user click outside, @@ -98,7 +103,8 @@ export default function usePickerInput({ onFocus: (e) => { setTyping(true); setFocused(true); - + currentFocusedKey.current = key; + clearTimeout(delayBlurTimer.current); if (onFocus) { onFocus(e); } @@ -129,10 +135,15 @@ export default function usePickerInput({ } } setFocused(false); - - if (onBlur) { - onBlur(e); - } + currentFocusedKey.current = ''; + // Delay to prevent 'range' focus transitions from firing resulting in incorrect out-of-focus events + delayBlurTimer.current = setTimeout(() => { + // Prevent the 'blur' event from firing when there is currently a focused input + if(currentFocusedKey.current) return; + if (onBlur) { + onBlur(e); + } + }, 100); }, }; @@ -167,5 +178,7 @@ export default function usePickerInput({ }), ); + useEffect(() => () => clearTimeout(delayBlurTimer.current), []); + return [inputProps, { focused, typing }]; } diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index 6f5a2d806..1ca444e92 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -1128,6 +1128,32 @@ describe('Picker.Range', () => { expect(wrapper.isOpen()).toBeFalsy(); }); + it('blur in range', (done) => { + const ref = React.createRef(); + const blurFn = jest.fn(); + const wrapper = mount( + , + ); + + wrapper.openPicker(0); + wrapper.inputValue('1990-11-28'); + wrapper.closePicker(0); + expect(wrapper.isOpen()).toBeTruthy(); + expect(blurFn).toBeCalledTimes(0); + + wrapper.inputValue('1990-12-23'); + wrapper.closePicker(1); + expect(wrapper.isOpen()).toBeFalsy(); + expect(blurFn).toBeCalledTimes(0); + + ref.current!.rangePickerRef.current!.blur(); + setTimeout(() => { + expect(blurFn).toBeCalledTimes(1); + done(); + }, 120); + + }); + it('new start is after end', () => { const wrapper = mount( ,