diff --git a/examples/uncontrolled.tsx b/examples/uncontrolled.tsx
index 2be9dcef8..1debaa240 100644
--- a/examples/uncontrolled.tsx
+++ b/examples/uncontrolled.tsx
@@ -9,6 +9,15 @@ export default () => (
Uncontrolled
+
+ generateConfig={momentGenerateConfig}
+ locale={zhCN}
+ picker="week"
+ allowClear
+ onOpenChange={open => {
+ console.log('1 =>', open);
+ }}
+ />
generateConfig={momentGenerateConfig}
locale={zhCN}
@@ -16,7 +25,7 @@ export default () => (
allowClear
open
onOpenChange={open => {
- console.log('=>', open);
+ console.log('2 =>', open);
}}
/>
diff --git a/src/Picker.tsx b/src/Picker.tsx
index a66117f44..f08068645 100644
--- a/src/Picker.tsx
+++ b/src/Picker.tsx
@@ -28,7 +28,7 @@ import { PickerMode } from './interface';
import {
getDefaultFormat,
getInputSize,
- addGlobalClickEvent,
+ addGlobalMouseDownEvent,
} from './utils/uiUtil';
export interface PickerSharedProps {
@@ -326,10 +326,25 @@ function InnerPicker(props: PickerProps) {
}
};
- const onInputBlur: React.FocusEventHandler = e => {
+ /**
+ * We will prevent blur to handle open event when user click outside,
+ * since this will repeat trigger `onOpenChange` event.
+ */
+ const preventBlurRef = React.useRef(false);
+
+ const triggerClose = () => {
triggerOpen(false);
setInnerValue(selectedValue);
triggerChange(selectedValue);
+ };
+
+ const onInputBlur: React.FocusEventHandler = e => {
+ if (preventBlurRef.current) {
+ preventBlurRef.current = false;
+ return;
+ }
+
+ triggerClose();
setFocused(false);
if (onBlur) {
@@ -361,7 +376,7 @@ function InnerPicker(props: PickerProps) {
// Global click handler
React.useEffect(() =>
- addGlobalClickEvent(({ target }: MouseEvent) => {
+ addGlobalMouseDownEvent(({ target }: MouseEvent) => {
if (
mergedOpen &&
panelDivRef.current &&
@@ -370,7 +385,13 @@ function InnerPicker(props: PickerProps) {
!inputDivRef.current.contains(target as Node) &&
onOpenChange
) {
- onOpenChange(false);
+ preventBlurRef.current = true;
+ triggerClose();
+
+ // Always set back in case `onBlur` prevented by user
+ window.setTimeout(() => {
+ preventBlurRef.current = false;
+ }, 0);
}
}),
);
diff --git a/src/utils/uiUtil.ts b/src/utils/uiUtil.ts
index f5ee4545a..1256044dd 100644
--- a/src/utils/uiUtil.ts
+++ b/src/utils/uiUtil.ts
@@ -157,7 +157,7 @@ type ClickEventHandler = (event: MouseEvent) => void;
let globalClickFunc: ClickEventHandler | null = null;
const clickCallbacks = new Set();
-export function addGlobalClickEvent(callback: ClickEventHandler) {
+export function addGlobalMouseDownEvent(callback: ClickEventHandler) {
if (
!globalClickFunc &&
typeof window !== 'undefined' &&
@@ -168,7 +168,7 @@ export function addGlobalClickEvent(callback: ClickEventHandler) {
queueFunc(e);
});
};
- window.addEventListener('click', globalClickFunc);
+ window.addEventListener('mousedown', globalClickFunc);
}
clickCallbacks.add(callback);
@@ -176,7 +176,7 @@ export function addGlobalClickEvent(callback: ClickEventHandler) {
return () => {
clickCallbacks.delete(callback);
if (clickCallbacks.size === 0) {
- window.removeEventListener('click', globalClickFunc!);
+ window.removeEventListener('mousedown', globalClickFunc!);
globalClickFunc = null;
}
};
diff --git a/tests/picker.spec.tsx b/tests/picker.spec.tsx
index 6f93b1313..fe9164888 100644
--- a/tests/picker.spec.tsx
+++ b/tests/picker.spec.tsx
@@ -120,17 +120,21 @@ describe('Basic', () => {
});
it('fixed open need repeat trigger onOpenChange', () => {
+ jest.useFakeTimers();
const onOpenChange = jest.fn();
- mount();
+ const wrapper = mount();
for (let i = 0; i < 10; i += 1) {
- const clickEvent = new Event('click');
+ const clickEvent = new Event('mousedown');
Object.defineProperty(clickEvent, 'target', {
get: () => document.body,
});
window.dispatchEvent(clickEvent);
+ wrapper.find('input').simulate('blur');
expect(onOpenChange).toHaveBeenCalledTimes(i + 1);
}
+ jest.runAllTimers();
+ jest.useRealTimers();
});
it('disabled should not open', () => {