Skip to content

Commit 7d22219

Browse files
authored
fix: Patch for getPopupContainer check (#200)
* chore: Not trigger getContainer when dom not ready * test: check for nodes test * update deps * clean up
1 parent 028908e commit 7d22219

File tree

3 files changed

+75
-6
lines changed

3 files changed

+75
-6
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,6 @@
6161
"classnames": "^2.2.6",
6262
"rc-align": "^4.0.0",
6363
"rc-motion": "^2.0.0",
64-
"rc-util": "^5.2.1"
64+
"rc-util": "^5.3.4"
6565
}
6666
}

src/index.tsx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export function generateTrigger(
146146

147147
triggerRef = React.createRef<React.ReactInstance>();
148148

149+
attachId?: number;
150+
149151
clickOutsideHandler: CommonEventHandler;
150152

151153
touchOutsideHandler: CommonEventHandler;
@@ -540,16 +542,26 @@ export function generateTrigger(
540542
};
541543

542544
attachParent = (popupContainer: HTMLDivElement) => {
543-
const { props } = this;
544-
const mountNode = props.getPopupContainer
545-
? props.getPopupContainer(this.getRootDomNode())
546-
: props.getDocument().body;
545+
raf.cancel(this.attachId);
546+
547+
const { getPopupContainer, getDocument } = this.props;
548+
const domNode = this.getRootDomNode();
549+
550+
let mountNode: HTMLElement;
551+
if (!getPopupContainer) {
552+
mountNode = getDocument().body;
553+
} else if (domNode || getPopupContainer.length === 0) {
554+
// Compatible for legacy getPopupContainer with domNode argument.
555+
// If no need `domNode` argument, will call directly.
556+
// https://codesandbox.io/s/eloquent-mclean-ss93m?file=/src/App.js
557+
mountNode = getPopupContainer(domNode);
558+
}
547559

548560
if (mountNode) {
549561
mountNode.appendChild(popupContainer);
550562
} else {
551563
// Retry after frame render in case parent not ready
552-
raf(() => {
564+
this.attachId = raf(() => {
553565
this.attachParent(popupContainer);
554566
});
555567
}

tests/basic.test.jsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { mount } from 'enzyme';
3+
import { act } from 'react-dom/test-utils';
34
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
45
import Portal from 'rc-util/lib/Portal';
56
import Trigger from '../src';
@@ -683,4 +684,60 @@ describe('Trigger.Basic', () => {
683684
expect(popupDomNode.style.color).toBe(style.color);
684685
expect(popupDomNode.style.top).toBe(`${style.top}px`);
685686
});
687+
688+
describe('getContainer', () => {
689+
it('not trigger when dom not ready', () => {
690+
const getPopupContainer = jest.fn(node => node.parentElement);
691+
692+
function Demo() {
693+
return (
694+
<Trigger
695+
popupVisible
696+
getPopupContainer={getPopupContainer}
697+
popup={<strong className="x-content">tooltip2</strong>}
698+
>
699+
<div className="target">click</div>
700+
</Trigger>
701+
);
702+
}
703+
704+
const wrapper = mount(<Demo />);
705+
706+
expect(getPopupContainer).toHaveReturnedTimes(0);
707+
708+
act(() => {
709+
jest.runAllTimers();
710+
});
711+
expect(getPopupContainer).toHaveReturnedTimes(1);
712+
expect(getPopupContainer).toHaveBeenCalledWith(
713+
wrapper.find('.target').instance(),
714+
);
715+
});
716+
717+
it('not trigger when dom no need', () => {
718+
let triggerTimes = 0;
719+
const getPopupContainer = () => {
720+
triggerTimes += 1;
721+
return document.body;
722+
};
723+
724+
function Demo() {
725+
return (
726+
<Trigger
727+
popupVisible
728+
getPopupContainer={getPopupContainer}
729+
popup={<strong className="x-content">tooltip2</strong>}
730+
>
731+
<div className="target">click</div>
732+
</Trigger>
733+
);
734+
}
735+
736+
const wrapper = mount(<Demo />);
737+
738+
expect(triggerTimes).toEqual(1);
739+
740+
wrapper.unmount();
741+
});
742+
});
686743
});

0 commit comments

Comments
 (0)