Skip to content

Commit df60150

Browse files
authored
fix: Not trigger enter open for portal element (#442)
* fix: lazy for portal event * test: add test case * chore: clean up
1 parent b2a4124 commit df60150

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"@testing-library/react": "^14.0.0",
4646
"@types/classnames": "^2.2.10",
4747
"@types/jest": "^29.5.2",
48+
"@types/node": "^20.11.6",
4849
"@types/react": "^18.0.0",
4950
"@types/react-dom": "^18.0.11",
5051
"@umijs/fabric": "^4.0.1",

src/index.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -557,7 +557,7 @@ export function generateTrigger(
557557
const hoverToShow = showActions.has('hover');
558558
const hoverToHide = hideActions.has('hover');
559559

560-
let onPopupMouseEnter: VoidFunction;
560+
let onPopupMouseEnter: React.MouseEventHandler<HTMLDivElement>;
561561
let onPopupMouseLeave: VoidFunction;
562562

563563
if (hoverToShow) {
@@ -578,9 +578,12 @@ export function generateTrigger(
578578
setMousePosByEvent(event);
579579
},
580580
);
581-
onPopupMouseEnter = () => {
581+
onPopupMouseEnter = (event) => {
582582
// Only trigger re-open when popup is visible
583-
if (mergedOpen || inMotion) {
583+
if (
584+
(mergedOpen || inMotion) &&
585+
popupEle?.contains(event.target as HTMLElement)
586+
) {
584587
triggerOpen(true, mouseEnterDelay);
585588
}
586589
};

tests/portal.test.jsx

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/* eslint-disable max-classes-per-file */
2+
3+
import { act, cleanup, fireEvent, render } from '@testing-library/react';
4+
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
5+
import React from 'react';
6+
import ReactDOM from 'react-dom';
7+
import Trigger from '../src';
8+
import { placementAlignMap } from './util';
9+
10+
describe('Trigger.Portal', () => {
11+
beforeAll(() => {
12+
spyElementPrototypes(HTMLElement, {
13+
offsetParent: {
14+
get: () => document.body,
15+
},
16+
});
17+
});
18+
19+
beforeEach(() => {
20+
jest.useFakeTimers();
21+
});
22+
23+
afterEach(() => {
24+
cleanup();
25+
jest.useRealTimers();
26+
});
27+
28+
it('no trigger with portal element', () => {
29+
const PortalBox = () => {
30+
return ReactDOM.createPortal(
31+
<div className="portal-box" />,
32+
document.body,
33+
);
34+
};
35+
36+
const onPopupVisibleChange = jest.fn();
37+
38+
const { container } = render(
39+
<div className="holder">
40+
<Trigger
41+
action={['hover']}
42+
popupAlign={placementAlignMap.left}
43+
onPopupVisibleChange={onPopupVisibleChange}
44+
popup={
45+
<strong className="x-content">
46+
tooltip2
47+
<PortalBox />
48+
</strong>
49+
}
50+
>
51+
<div className="target">hover</div>
52+
</Trigger>
53+
</div>,
54+
);
55+
56+
// Show the popup
57+
fireEvent.mouseEnter(container.querySelector('.target'));
58+
expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
59+
fireEvent.mouseLeave(container.querySelector('.target'));
60+
61+
// Mouse enter popup
62+
fireEvent.mouseEnter(document.querySelector('.x-content'));
63+
fireEvent.mouseLeave(document.querySelector('.x-content'));
64+
65+
// To Portal
66+
fireEvent.mouseEnter(document.querySelector('.portal-box'));
67+
act(() => {
68+
jest.runAllTimers();
69+
});
70+
71+
expect(onPopupVisibleChange).toHaveBeenCalledWith(false);
72+
});
73+
});

0 commit comments

Comments
 (0)