diff --git a/docs/examples/container.tsx b/docs/examples/container.tsx
index c1a17166..dd6fbb45 100644
--- a/docs/examples/container.tsx
+++ b/docs/examples/container.tsx
@@ -153,7 +153,7 @@ export default () => {
}
popupStyle={{ boxShadow: '0 0 5px red' }}
- popupVisible
+ // popupVisible
// getPopupContainer={() => popHolderRef.current}
popupPlacement={popupPlacement}
builtinPlacements={builtinPlacements}
diff --git a/src/index.tsx b/src/index.tsx
index 1e2c254f..a2541450 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -17,9 +17,9 @@ import useWatch from './hooks/useWatch';
import type {
ActionType,
AlignType,
+ AnimationType,
ArrowType,
ArrowTypeOuter,
- AnimationType,
BuildInPlacements,
TransitionNameType,
} from './interface';
@@ -27,7 +27,12 @@ import Popup from './Popup';
import TriggerWrapper from './TriggerWrapper';
import { getAlignPopupClassName, getMotion, getWin } from './util';
-export type { BuildInPlacements, AlignType, ActionType, ArrowTypeOuter as ArrowType };
+export type {
+ BuildInPlacements,
+ AlignType,
+ ActionType,
+ ArrowTypeOuter as ArrowType,
+};
export interface TriggerRef {
forceAlign: VoidFunction;
@@ -495,8 +500,16 @@ export function generateTrigger(
// Click to hide is special action since click popup element should not hide
React.useEffect(() => {
if (clickToHide && popupEle && (!mask || maskClosable)) {
+ let clickInside = false;
+
+ // User may mouseDown inside and drag out of popup and mouse up
+ // Record here to prevent close
+ const onWindowMouseDown = ({ target }: MouseEvent) => {
+ clickInside = inPopupOrChild(target);
+ };
+
const onWindowClick = ({ target }: MouseEvent) => {
- if (openRef.current && !inPopupOrChild(target)) {
+ if (openRef.current && !clickInside && !inPopupOrChild(target)) {
triggerOpen(false);
}
};
@@ -505,11 +518,16 @@ export function generateTrigger(
const targetRoot = targetEle?.getRootNode();
+ win.addEventListener('mousedown', onWindowMouseDown);
win.addEventListener('click', onWindowClick);
// shadow root
const inShadow = targetRoot && targetRoot !== targetEle.ownerDocument;
if (inShadow) {
+ (targetRoot as ShadowRoot).addEventListener(
+ 'mousedown',
+ onWindowMouseDown,
+ );
(targetRoot as ShadowRoot).addEventListener('click', onWindowClick);
}
@@ -524,9 +542,14 @@ export function generateTrigger(
}
return () => {
+ win.removeEventListener('mousedown', onWindowMouseDown);
win.removeEventListener('click', onWindowClick);
if (inShadow) {
+ (targetRoot as ShadowRoot).removeEventListener(
+ 'mousedown',
+ onWindowMouseDown,
+ );
(targetRoot as ShadowRoot).removeEventListener(
'click',
onWindowClick,
@@ -627,12 +650,14 @@ export function generateTrigger(
...passedProps,
});
- const innerArrow: ArrowType = arrow ? {
- // true and Object likely
- ...(arrow !== true ? arrow : {}),
- x: arrowX,
- y: arrowY
- }: null;
+ const innerArrow: ArrowType = arrow
+ ? {
+ // true and Object likely
+ ...(arrow !== true ? arrow : {}),
+ x: arrowX,
+ y: arrowY,
+ }
+ : null;
// Render
return (
diff --git a/tests/basic.test.jsx b/tests/basic.test.jsx
index 93c128a8..69887c6a 100644
--- a/tests/basic.test.jsx
+++ b/tests/basic.test.jsx
@@ -936,4 +936,56 @@ describe('Trigger.Basic', () => {
await awaitFakeTimer();
expect(document.querySelector('.rc-trigger-popup-hidden')).toBeTruthy();
});
+
+ describe('click window to hide', () => {
+ it('should hide', async () => {
+ const onPopupVisibleChange = jest.fn();
+
+ const { container } = render(
+ trigger}
+ >
+
+ ,
+ );
+
+ fireEvent.click(container.querySelector('.target'));
+ await awaitFakeTimer();
+ expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
+ onPopupVisibleChange.mockReset();
+
+ // Click outside to close
+ fireEvent.mouseDown(document.body);
+ fireEvent.click(document.body);
+ await awaitFakeTimer();
+ expect(onPopupVisibleChange).toHaveBeenCalledWith(false);
+ });
+
+ it('should not hide when mouseDown inside but mouseUp outside', async () => {
+ const onPopupVisibleChange = jest.fn();
+
+ const { container } = render(
+ trigger}
+ >
+
+ ,
+ );
+
+ fireEvent.click(container.querySelector('.target'));
+ await awaitFakeTimer();
+ expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
+ onPopupVisibleChange.mockReset();
+
+ // Click outside to close
+ fireEvent.mouseDown(document.querySelector('strong'));
+ fireEvent.click(document.body);
+ await awaitFakeTimer();
+ expect(onPopupVisibleChange).not.toHaveBeenCalled();
+ });
+ });
});