Skip to content

Commit f2e92ee

Browse files
committed
fix: DOMRect value issues
1 parent c2424eb commit f2e92ee

File tree

1 file changed

+297
-0
lines changed

1 file changed

+297
-0
lines changed

tests/rect.test.tsx

Lines changed: 297 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,297 @@
1+
import { act, cleanup, fireEvent, render } from '@testing-library/react';
2+
import { spyElementPrototypes } from 'rc-util/lib/test/domHook';
3+
import React from 'react';
4+
import type { TriggerProps, TriggerRef } from '../src';
5+
import Trigger from '../src';
6+
import { awaitFakeTimer } from './util';
7+
8+
import { _rs } from 'rc-resize-observer';
9+
10+
export const triggerResize = (target: Element) => {
11+
act(() => {
12+
_rs([{ target } as ResizeObserverEntry]);
13+
});
14+
};
15+
16+
describe('Trigger.Rect', () => {
17+
let targetVisible = true;
18+
19+
let rectX = 100;
20+
let rectY = 100;
21+
let rectWidth = 100;
22+
let rectHeight = 100;
23+
24+
beforeAll(() => {
25+
spyElementPrototypes(HTMLDivElement, {
26+
getBoundingClientRect: () => ({
27+
left: rectX,
28+
top: rectY,
29+
width: rectWidth,
30+
height: rectHeight,
31+
right: 200,
32+
bottom: 200,
33+
}),
34+
});
35+
36+
spyElementPrototypes(HTMLElement, {
37+
offsetParent: {
38+
get: () => (targetVisible ? document.body : null),
39+
},
40+
});
41+
spyElementPrototypes(SVGElement, {
42+
offsetParent: {
43+
get: () => (targetVisible ? document.body : null),
44+
},
45+
});
46+
});
47+
48+
beforeEach(() => {
49+
targetVisible = true;
50+
51+
rectX = 100;
52+
rectY = 100;
53+
rectWidth = 100;
54+
rectHeight = 100;
55+
56+
jest.useFakeTimers();
57+
});
58+
59+
afterEach(() => {
60+
cleanup();
61+
jest.useRealTimers();
62+
});
63+
64+
it('not show', async () => {
65+
const onAlign = jest.fn();
66+
67+
const Demo = (props: Partial<TriggerProps>) => {
68+
const scrollRef = React.useRef<HTMLDivElement>(null);
69+
70+
return (
71+
<>
72+
<div
73+
className="scroll"
74+
ref={scrollRef}
75+
// Jest can not get calculated style in jsdom. So we need to set it manually
76+
style={{ overflowX: 'hidden', overflowY: 'hidden' }}
77+
/>
78+
<Trigger
79+
onPopupAlign={onAlign}
80+
popupAlign={{
81+
points: ['bl', 'tl'],
82+
}}
83+
popup={<strong>trigger</strong>}
84+
getPopupContainer={() => scrollRef.current!}
85+
{...props}
86+
>
87+
<div />
88+
</Trigger>
89+
</>
90+
);
91+
};
92+
93+
const { rerender, container } = render(<Demo />);
94+
const scrollDiv = container.querySelector('.scroll')!;
95+
96+
const mockAddEvent = jest.spyOn(scrollDiv, 'addEventListener');
97+
98+
expect(mockAddEvent).not.toHaveBeenCalled();
99+
100+
// Visible
101+
rerender(<Demo popupVisible />);
102+
expect(mockAddEvent).toHaveBeenCalled();
103+
104+
// Scroll
105+
onAlign.mockReset();
106+
fireEvent.scroll(scrollDiv);
107+
108+
await awaitFakeTimer();
109+
expect(onAlign).toHaveBeenCalled();
110+
});
111+
112+
it('resize align', async () => {
113+
const onAlign = jest.fn();
114+
115+
const { container } = render(
116+
<Trigger
117+
onPopupAlign={onAlign}
118+
popupVisible
119+
popupAlign={{
120+
points: ['bl', 'tl'],
121+
}}
122+
popup={<strong>trigger</strong>}
123+
>
124+
<span className="target" />
125+
</Trigger>,
126+
);
127+
128+
await Promise.resolve();
129+
onAlign.mockReset();
130+
131+
// Resize
132+
const target = container.querySelector('.target')!;
133+
triggerResize(target);
134+
135+
await awaitFakeTimer();
136+
expect(onAlign).toHaveBeenCalled();
137+
});
138+
139+
it('placement is higher than popupAlign', async () => {
140+
render(
141+
<Trigger
142+
popupVisible
143+
popup={<span className="bamboo" />}
144+
builtinPlacements={{
145+
top: {},
146+
}}
147+
popupPlacement="top"
148+
popupAlign={{}}
149+
>
150+
<span />
151+
</Trigger>,
152+
);
153+
154+
await awaitFakeTimer();
155+
156+
expect(
157+
document.querySelector('.rc-trigger-popup-placement-top'),
158+
).toBeTruthy();
159+
});
160+
161+
it('invisible should not align', async () => {
162+
const onPopupAlign = jest.fn();
163+
const triggerRef = React.createRef<TriggerRef>();
164+
165+
render(
166+
<Trigger
167+
popupVisible
168+
popup={<span className="bamboo" />}
169+
popupAlign={{}}
170+
onPopupAlign={onPopupAlign}
171+
ref={triggerRef}
172+
>
173+
<span />
174+
</Trigger>,
175+
);
176+
177+
await awaitFakeTimer();
178+
179+
expect(onPopupAlign).toHaveBeenCalled();
180+
onPopupAlign.mockReset();
181+
182+
for (let i = 0; i < 10; i += 1) {
183+
triggerRef.current!.forceAlign();
184+
185+
await awaitFakeTimer();
186+
expect(onPopupAlign).toHaveBeenCalled();
187+
onPopupAlign.mockReset();
188+
}
189+
190+
// Make invisible
191+
targetVisible = false;
192+
193+
triggerRef.current!.forceAlign();
194+
await awaitFakeTimer();
195+
expect(onPopupAlign).not.toHaveBeenCalled();
196+
});
197+
198+
it('align should merge into placement', async () => {
199+
render(
200+
<Trigger
201+
popupVisible
202+
popup={<span className="bamboo" />}
203+
builtinPlacements={{
204+
top: {
205+
targetOffset: [0, 0],
206+
},
207+
}}
208+
popupPlacement="top"
209+
popupAlign={{
210+
targetOffset: [-903, -1128],
211+
}}
212+
>
213+
<svg />
214+
</Trigger>,
215+
);
216+
217+
await awaitFakeTimer();
218+
219+
expect(
220+
document.querySelector('.rc-trigger-popup-placement-top'),
221+
).toHaveStyle({
222+
left: `753px`,
223+
top: `978px`,
224+
});
225+
});
226+
227+
it('targetOffset support ptg', async () => {
228+
render(
229+
<Trigger
230+
popupVisible
231+
popup={<span className="bamboo" />}
232+
popupAlign={{
233+
targetOffset: ['50%', '-50%'],
234+
}}
235+
>
236+
<div />
237+
</Trigger>,
238+
);
239+
240+
await awaitFakeTimer();
241+
242+
// Correct this if I miss understand the value calculation
243+
// from https://github.com/yiminghe/dom-align/blob/master/src/getElFuturePos.js
244+
expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({
245+
left: `-50px`,
246+
top: `50px`,
247+
});
248+
});
249+
250+
it('support dynamicInset', async () => {
251+
render(
252+
<Trigger
253+
popupVisible
254+
popup={<span className="bamboo" />}
255+
popupAlign={{
256+
points: ['bc', 'tc'],
257+
_experimental: {
258+
dynamicInset: true,
259+
},
260+
}}
261+
>
262+
<div />
263+
</Trigger>,
264+
);
265+
266+
await awaitFakeTimer();
267+
268+
expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({
269+
bottom: `100px`,
270+
});
271+
});
272+
273+
it('round when decimal precision', async () => {
274+
rectX = 22.6;
275+
rectY = 33.4;
276+
rectWidth = 33.7;
277+
rectHeight = 55.9;
278+
279+
render(
280+
<Trigger
281+
popupVisible
282+
popup={<span className="bamboo" />}
283+
popupAlign={{
284+
points: ['tl', 'bl'],
285+
}}
286+
>
287+
<div />
288+
</Trigger>,
289+
);
290+
291+
await awaitFakeTimer();
292+
293+
expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({
294+
top: `56px`,
295+
});
296+
});
297+
});

0 commit comments

Comments
 (0)