Skip to content

Commit 846b3d0

Browse files
authored
fix: align jump for the first time render (#421)
* fix: align should trigger before motion * test: add test case * chore: reorder * test: update snapshot
1 parent 3e019ed commit 846b3d0

File tree

3 files changed

+49
-21
lines changed

3 files changed

+49
-21
lines changed

docs/examples/simple.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ const RefTarget = React.forwardRef((props, ref) => {
6969
interface TestState {
7070
mask: boolean;
7171
maskClosable: boolean;
72-
placement: 'right';
72+
placement: string;
7373
trigger: {
7474
click?: boolean;
7575
focus?: boolean;
@@ -90,13 +90,13 @@ class Test extends React.Component<any, TestState> {
9090
state: TestState = {
9191
mask: true,
9292
maskClosable: true,
93-
placement: 'right',
93+
placement: 'bottom',
9494
trigger: {
9595
click: true,
9696
},
9797
offsetX: undefined,
9898
offsetY: undefined,
99-
stretch: '',
99+
stretch: 'minWidth',
100100
transitionName: 'rc-trigger-popup-zoom',
101101
};
102102

src/index.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,23 @@ export function generateTrigger(
444444
forceAlign: triggerAlign,
445445
}));
446446

447+
// ========================== Stretch ===========================
448+
const [targetWidth, setTargetWidth] = React.useState(0);
449+
const [targetHeight, setTargetHeight] = React.useState(0);
450+
451+
const syncTargetSize = () => {
452+
if (stretch && targetEle) {
453+
const rect = targetEle.getBoundingClientRect();
454+
setTargetWidth(rect.width);
455+
setTargetHeight(rect.height);
456+
}
457+
};
458+
459+
const onTargetResize = () => {
460+
syncTargetSize();
461+
triggerAlign();
462+
};
463+
447464
// ========================== Motion ============================
448465
const onVisibleChanged = (visible: boolean) => {
449466
setInMotion(false);
@@ -454,6 +471,7 @@ export function generateTrigger(
454471
// We will trigger align when motion is in prepare
455472
const onPrepare = () =>
456473
new Promise<void>((resolve) => {
474+
syncTargetSize();
457475
setMotionPrepareResolve(() => resolve);
458476
});
459477

@@ -465,20 +483,6 @@ export function generateTrigger(
465483
}
466484
}, [motionPrepareResolve]);
467485

468-
// ========================== Stretch ===========================
469-
const [targetWidth, setTargetWidth] = React.useState(0);
470-
const [targetHeight, setTargetHeight] = React.useState(0);
471-
472-
const onTargetResize = (_: object, ele: HTMLElement) => {
473-
triggerAlign();
474-
475-
if (stretch) {
476-
const rect = ele.getBoundingClientRect();
477-
setTargetWidth(rect.width);
478-
setTargetHeight(rect.height);
479-
}
480-
};
481-
482486
// =========================== Action ===========================
483487
/**
484488
* Util wrapper for trigger action

tests/basic.test.jsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -510,13 +510,14 @@ describe('Trigger.Basic', () => {
510510
});
511511

512512
describe('stretch', () => {
513-
const createTrigger = (stretch) =>
513+
const createTrigger = (stretch, restProps) =>
514514
render(
515515
<Trigger
516516
action={['click']}
517517
popupAlign={placementAlignMap.left}
518518
popup={<strong className="x-content">tooltip2</strong>}
519519
stretch={stretch}
520+
{...restProps}
520521
>
521522
<div className="target">
522523
click me to show trigger
@@ -530,6 +531,7 @@ describe('Trigger.Basic', () => {
530531
const height = 903;
531532
let domSpy;
532533
let rect = {};
534+
let rectCalled = false;
533535

534536
beforeAll(() => {
535537
domSpy = spyElementPrototypes(HTMLElement, {
@@ -540,23 +542,45 @@ describe('Trigger.Basic', () => {
540542
get: () => height,
541543
},
542544
getBoundingClientRect() {
545+
rectCalled = true;
543546
return rect;
544547
},
545548
});
546549
});
547550

551+
beforeEach(() => {
552+
rectCalled = false;
553+
});
554+
548555
afterAll(() => {
549556
domSpy.mockRestore();
550557
});
551558

552559
[null, { width, height }].forEach((mockRect) => {
553560
['width', 'height', 'min-width', 'min-height'].forEach((prop) => {
554-
it(`${mockRect ? 'offset' : 'getBoundingClientRect'}: ${prop}`, () => {
555-
const { container } = createTrigger(prop);
561+
it(`${
562+
mockRect ? 'offset' : 'getBoundingClientRect'
563+
}: ${prop}`, async () => {
564+
const onPopupAlign = jest.fn();
565+
566+
const { container } = createTrigger(prop, {
567+
onPopupAlign,
568+
});
556569
rect = mockRect || {};
557570

571+
expect(rectCalled).toBeFalsy();
572+
expect(onPopupAlign).not.toHaveBeenCalled();
573+
574+
// Click will trigger `onPrepare` which need measure target size
558575
fireEvent.click(container.querySelector('.target'));
559-
act(() => jest.runAllTimers());
576+
expect(rectCalled).toBeTruthy();
577+
578+
// Flush for motion
579+
await act(async () => {
580+
jest.advanceTimersByTime(100);
581+
await Promise.resolve();
582+
});
583+
expect(onPopupAlign).toHaveBeenCalled();
560584

561585
expect(
562586
document.querySelector('.rc-trigger-popup').style,

0 commit comments

Comments
 (0)