From eee4255102df156196cda502fde45f0e0da79857 Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Sat, 1 Apr 2023 08:38:12 +0800 Subject: [PATCH 01/12] fix: correct flip --- src/hooks/useAlign.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts index 448fc1eb..253f4cbd 100644 --- a/src/hooks/useAlign.ts +++ b/src/hooks/useAlign.ts @@ -270,6 +270,9 @@ export default function useAlign( // ============== Intersection =============== // Get area by position. Used for check if flip area is better function getIntersectionVisibleArea(x: number, y: number) { + x += popupRect.x; + y += popupRect.y; + const r = x + popupWidth; const b = y + popupHeight; From b0253c892459a92851310222a48376bc31d9211b Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Sat, 1 Apr 2023 15:11:54 +0800 Subject: [PATCH 02/12] chore: fix lint --- src/hooks/useAlign.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts index 253f4cbd..ddaac781 100644 --- a/src/hooks/useAlign.ts +++ b/src/hooks/useAlign.ts @@ -269,9 +269,9 @@ export default function useAlign( // ============== Intersection =============== // Get area by position. Used for check if flip area is better - function getIntersectionVisibleArea(x: number, y: number) { - x += popupRect.x; - y += popupRect.y; + function getIntersectionVisibleArea(offsetX: number, offsetY: number) { + const x = popupRect.x + offsetX; + const y = popupRect.y + offsetY; const r = x + popupWidth; const b = y + popupHeight; From 32e9feecf33373ffeb7eb0df10eece9470272a72 Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Sat, 1 Apr 2023 19:31:19 +0800 Subject: [PATCH 03/12] chore: optimize test --- tests/flip.test.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/flip.test.tsx b/tests/flip.test.tsx index 95287e78..e6ecaaaf 100644 --- a/tests/flip.test.tsx +++ b/tests/flip.test.tsx @@ -54,10 +54,10 @@ describe('Trigger.Align', () => { spyElementPrototypes(HTMLDivElement, { getBoundingClientRect() { return { - x: 0, - y: 0, - width: 100, - height: 100, + x: -100, + y: -100, + width: 300, + height: 300, }; }, }); From 0e722d3419a4ecbf7ed4c98e08d8b0c4508b99df Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Sat, 1 Apr 2023 19:45:02 +0800 Subject: [PATCH 04/12] chore: optimize test --- tests/flip.test.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/flip.test.tsx b/tests/flip.test.tsx index e6ecaaaf..b9348213 100644 --- a/tests/flip.test.tsx +++ b/tests/flip.test.tsx @@ -56,8 +56,8 @@ describe('Trigger.Align', () => { return { x: -100, y: -100, - width: 300, - height: 300, + width: 100, + height: 100, }; }, }); From 9699bcfd6788162f6ffabcf8a2f26e06750d2535 Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Tue, 4 Apr 2023 08:20:36 +0800 Subject: [PATCH 05/12] chore: add test --- tests/flip.test.tsx | 137 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/tests/flip.test.tsx b/tests/flip.test.tsx index b9348213..48e40701 100644 --- a/tests/flip.test.tsx +++ b/tests/flip.test.tsx @@ -54,8 +54,8 @@ describe('Trigger.Align', () => { spyElementPrototypes(HTMLDivElement, { getBoundingClientRect() { return { - x: -100, - y: -100, + x: 0, + y: 0, width: 100, height: 100, }; @@ -187,4 +187,137 @@ describe('Trigger.Align', () => { }); }); }); + + describe('flip when scroll', () => { + const popupRect = { + width: 100, + height: 100, + }; + + /** + * 模拟有滚动条时 + * popupRect的x,y值等于popupElement相对与target的位置减去target相对与视口的位置 + * 假设popupElement相对与target的位置x,y均为-1000 + * + * 重置pupupElement位置 https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L137-L139 + * 获取popupRect https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L159 + */ + Object.defineProperty(popupRect, 'x', { + get() { + return -1000 - spanRect.x; + } + }); + + Object.defineProperty(popupRect, 'y', { + get() { + return -1000 - spanRect.y; + } + }); + + beforeAll(() => { + spyElementPrototypes(HTMLDivElement, { + getBoundingClientRect() { + return popupRect; + } + }); + }); + + describe('not flip if cant', () => { + const list = [ + { + placement: 'right', + x: 10, + className: '.rc-trigger-popup-placement-right', + }, + { + placement: 'left', + x: 90, + className: '.rc-trigger-popup-placement-left', + }, + { + placement: 'top', + y: 90, + className: '.rc-trigger-popup-placement-top', + }, + { + placement: 'bottom', + y: 10, + className: '.rc-trigger-popup-placement-bottom', + }, + ]; + + list.forEach(({ placement, x = 0, y = 0, className }) => { + it(placement, async () => { + spanRect.x = x; + spanRect.y = y; + + render( + trigger} + > + + , + ); + + await act(async () => { + await Promise.resolve(); + }); + + expect(document.querySelector(className)).toBeTruthy(); + }); + }); + }); + + describe('flip if can', () => { + const list = [ + { + placement: 'right', + x: 90, + className: '.rc-trigger-popup-placement-left', + }, + { + placement: 'left', + x: 10, + className: '.rc-trigger-popup-placement-right', + }, + { + placement: 'top', + y: 10, + className: '.rc-trigger-popup-placement-bottom', + }, + { + placement: 'bottom', + y: 90, + className: '.rc-trigger-popup-placement-top', + }, + ]; + + list.forEach(({ placement, x = 0, y = 0, className }) => { + it(placement, async () => { + spanRect.x = x; + spanRect.y = y; + + render( + trigger} + > + + , + ); + + await act(async () => { + await Promise.resolve(); + }); + + expect(document.querySelector(className)).toBeTruthy(); + }); + }); + }); + }); }); From 6bda20cd024c0e0039c9fe5d30076d2efa50f0ae Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:30:31 +0800 Subject: [PATCH 06/12] chore: update --- tests/flip.test.tsx | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/tests/flip.test.tsx b/tests/flip.test.tsx index 48e40701..448a0ccb 100644 --- a/tests/flip.test.tsx +++ b/tests/flip.test.tsx @@ -189,11 +189,7 @@ describe('Trigger.Align', () => { }); describe('flip when scroll', () => { - const popupRect = { - width: 100, - height: 100, - }; - + let domSpy: ReturnType | undefined; /** * 模拟有滚动条时 * popupRect的x,y值等于popupElement相对与target的位置减去target相对与视口的位置 @@ -202,26 +198,23 @@ describe('Trigger.Align', () => { * 重置pupupElement位置 https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L137-L139 * 获取popupRect https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L159 */ - Object.defineProperty(popupRect, 'x', { - get() { - return -1000 - spanRect.x; - } - }); - - Object.defineProperty(popupRect, 'y', { - get() { - return -1000 - spanRect.y; - } - }); - beforeAll(() => { - spyElementPrototypes(HTMLDivElement, { + domSpy = spyElementPrototypes(HTMLDivElement, { getBoundingClientRect() { - return popupRect; + return { + x: -1000 - spanRect.x, + y: -1000 - spanRect.y, + width: 100, + height: 100, + }; } }); }); + afterAll(() => { + domSpy.mockRestore(); + }); + describe('not flip if cant', () => { const list = [ { From 44eb76d3e6211dbac73b991e5fea0cfd12de06db Mon Sep 17 00:00:00 2001 From: acyza <101238421+acyza@users.noreply.github.com> Date: Tue, 4 Apr 2023 16:39:22 +0800 Subject: [PATCH 07/12] chore: update --- tests/flip.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/flip.test.tsx b/tests/flip.test.tsx index 448a0ccb..0b99b33c 100644 --- a/tests/flip.test.tsx +++ b/tests/flip.test.tsx @@ -192,7 +192,7 @@ describe('Trigger.Align', () => { let domSpy: ReturnType | undefined; /** * 模拟有滚动条时 - * popupRect的x,y值等于popupElement相对与target的位置减去target相对与视口的位置 + * popupRect的x,y值等于popupElement相对与target的位置加上target相对与视口的位置 * 假设popupElement相对与target的位置x,y均为-1000 * * 重置pupupElement位置 https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L137-L139 @@ -202,8 +202,8 @@ describe('Trigger.Align', () => { domSpy = spyElementPrototypes(HTMLDivElement, { getBoundingClientRect() { return { - x: -1000 - spanRect.x, - y: -1000 - spanRect.y, + x: -1000 + spanRect.x, + y: -1000 + spanRect.y, width: 100, height: 100, }; From 8add52a421ee9b46cb4d8b77d7b655d3ef2d573a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 4 Apr 2023 22:56:27 +0800 Subject: [PATCH 08/12] test: merge test case --- tests/flip.test.tsx | 209 ++++++++++++++++++-------------------------- 1 file changed, 83 insertions(+), 126 deletions(-) diff --git a/tests/flip.test.tsx b/tests/flip.test.tsx index 0b99b33c..441698c8 100644 --- a/tests/flip.test.tsx +++ b/tests/flip.test.tsx @@ -41,6 +41,13 @@ describe('Trigger.Align', () => { height: 1, }; + let popupRect = { + x: 0, + y: 0, + width: 100, + height: 100, + }; + beforeAll(() => { spyElementPrototypes(HTMLElement, { clientWidth: { @@ -53,12 +60,7 @@ describe('Trigger.Align', () => { spyElementPrototypes(HTMLDivElement, { getBoundingClientRect() { - return { - x: 0, - y: 0, - width: 100, - height: 100, - }; + return popupRect; }, }); @@ -82,6 +84,12 @@ describe('Trigger.Align', () => { width: 1, height: 1, }; + popupRect = { + x: 0, + y: 0, + width: 100, + height: 100, + }; jest.useFakeTimers(); }); @@ -188,129 +196,78 @@ describe('Trigger.Align', () => { }); }); - describe('flip when scroll', () => { - let domSpy: ReturnType | undefined; - /** - * 模拟有滚动条时 - * popupRect的x,y值等于popupElement相对与target的位置加上target相对与视口的位置 - * 假设popupElement相对与target的位置x,y均为-1000 - * - * 重置pupupElement位置 https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L137-L139 - * 获取popupRect https://github.com/react-component/trigger/blob/e6fa971f97196ea791d0799f25c318c9d8c0ae0f/src/hooks/useAlign.ts#L159 - */ - beforeAll(() => { - domSpy = spyElementPrototypes(HTMLDivElement, { - getBoundingClientRect() { - return { - x: -1000 + spanRect.x, - y: -1000 + spanRect.y, - width: 100, - height: 100, - }; - } - }); - }); + // `getPopupContainer` sometime makes the popup 0/0 not start at left top. + // We need cal the real visible position + /* - afterAll(() => { - domSpy.mockRestore(); - }); + ******************* + * Target * + * ************* + * * Popup * + * ************* + * * + ******************* - describe('not flip if cant', () => { - const list = [ - { - placement: 'right', - x: 10, - className: '.rc-trigger-popup-placement-right', - }, - { - placement: 'left', - x: 90, - className: '.rc-trigger-popup-placement-left', - }, - { - placement: 'top', - y: 90, - className: '.rc-trigger-popup-placement-top', - }, - { - placement: 'bottom', - y: 10, - className: '.rc-trigger-popup-placement-bottom', - }, - ]; - - list.forEach(({ placement, x = 0, y = 0, className }) => { - it(placement, async () => { - spanRect.x = x; - spanRect.y = y; - - render( - trigger} - > - - , - ); - - await act(async () => { - await Promise.resolve(); - }); - - expect(document.querySelector(className)).toBeTruthy(); - }); - }); + To: + + ******************* + * Target * + * ************* * + * * Popup * * + * ************* * + * * + ******************* + + */ + it('popup start position not at left top', async () => { + spanRect.x = 99; + spanRect.y = 0; + + popupRect = { + x: 100, + y: 1, + width: 100, + height: 100, + }; + + render( + trigger} + > + + , + ); + + await act(async () => { + await Promise.resolve(); }); - - describe('flip if can', () => { - const list = [ - { - placement: 'right', - x: 90, - className: '.rc-trigger-popup-placement-left', - }, - { - placement: 'left', - x: 10, - className: '.rc-trigger-popup-placement-right', - }, - { - placement: 'top', - y: 10, - className: '.rc-trigger-popup-placement-bottom', - }, - { - placement: 'bottom', - y: 90, - className: '.rc-trigger-popup-placement-top', - }, - ]; - - list.forEach(({ placement, x = 0, y = 0, className }) => { - it(placement, async () => { - spanRect.x = x; - spanRect.y = y; - - render( - trigger} - > - - , - ); - - await act(async () => { - await Promise.resolve(); - }); - - expect(document.querySelector(className)).toBeTruthy(); - }); - }); + + // Flip + expect( + document.querySelector('.rc-trigger-popup-placement-topRight'), + ).toBeTruthy(); + + expect(document.querySelector('.rc-trigger-popup')).toHaveStyle({ + left: `-100px`, // (left: 100) - (offset: 100) = 0 + top: `0px`, }); }); }); From 332ecbb6238ebb7f3a0f0198e7839d6633c0cf4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 4 Apr 2023 22:57:26 +0800 Subject: [PATCH 09/12] chore: rename --- src/hooks/useAlign.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts index ddaac781..fc16af1a 100644 --- a/src/hooks/useAlign.ts +++ b/src/hooks/useAlign.ts @@ -269,19 +269,19 @@ export default function useAlign( // ============== Intersection =============== // Get area by position. Used for check if flip area is better - function getIntersectionVisibleArea(offsetX: number, offsetY: number) { - const x = popupRect.x + offsetX; - const y = popupRect.y + offsetY; + function getIntersectionVisibleArea(ox: number, oy: number) { + const l = popupRect.x + ox; + const t = popupRect.y + oy; - const r = x + popupWidth; - const b = y + popupHeight; + const r = l + popupWidth; + const b = t + popupHeight; - const visibleX = Math.max(x, visibleArea.left); - const visibleY = Math.max(y, visibleArea.top); + const visibleL = Math.max(l, visibleArea.left); + const visibleT = Math.max(t, visibleArea.top); const visibleR = Math.min(r, visibleArea.right); const visibleB = Math.min(b, visibleArea.bottom); - return (visibleR - visibleX) * (visibleB - visibleY); + return (visibleR - visibleL) * (visibleB - visibleT); } const originIntersectionVisibleArea = getIntersectionVisibleArea( From 017abce8cb1cf78d3aec3e7762c0fc6d2f6edf50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 4 Apr 2023 22:58:33 +0800 Subject: [PATCH 10/12] chore: merge naming --- src/hooks/useAlign.ts | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts index 4ea6df3a..f25ae8dc 100644 --- a/src/hooks/useAlign.ts +++ b/src/hooks/useAlign.ts @@ -269,9 +269,9 @@ export default function useAlign( // ============== Intersection =============== // Get area by position. Used for check if flip area is better - function getIntersectionVisibleArea(ox: number, oy: number) { - const l = popupRect.x + ox; - const t = popupRect.y + oy; + function getIntersectionVisibleArea(offsetX: number, offsetY: number) { + const l = popupRect.x + offsetX; + const t = popupRect.y + offsetY; const r = l + popupWidth; const b = t + popupHeight; @@ -289,7 +289,7 @@ export default function useAlign( nextOffsetY, ); - // ========================== Overflow =========================== + // ================ Overflow ================= const targetAlignPointTL = getAlignPoint(targetRect, ['t', 'l']); const popupAlignPointTL = getAlignPoint(popupRect, ['t', 'l']); const targetAlignPointBR = getAlignPoint(targetRect, ['b', 'r']); @@ -305,21 +305,10 @@ export default function useAlign( return val >= 0; }; - // Prepare position - let nextPopupY: number; - let nextPopupBottom: number; - let nextPopupX: number; - let nextPopupRight: number; - - function syncNextPopupPosition() { - nextPopupY = popupRect.y + nextOffsetY; - nextPopupBottom = nextPopupY + popupHeight; - nextPopupX = popupRect.x + nextOffsetX; - nextPopupRight = nextPopupX + popupWidth; - } - syncNextPopupPosition(); - // >>>>>>>>>> Top & Bottom + const nextPopupY = popupRect.y + nextOffsetY; + const nextPopupBottom = nextPopupY + popupHeight; + const needAdjustY = supportAdjust(adjustY); const sameTB = popupPoints[0] === targetPoints[0]; @@ -381,6 +370,9 @@ export default function useAlign( } // >>>>>>>>>> Left & Right + const nextPopupX = popupRect.x + nextOffsetX; + const nextPopupRight = nextPopupX + popupWidth; + const needAdjustX = supportAdjust(adjustX); // >>>>> Flip @@ -442,9 +434,7 @@ export default function useAlign( } } - // ============================ Shift ============================ - syncNextPopupPosition(); - + // >>>>> Shift const numShiftX = shiftX === true ? 0 : shiftX; if (typeof numShiftX === 'number') { // Left @@ -489,7 +479,6 @@ export default function useAlign( } } - // ============================ Arrow ============================ // Arrow center align const popupLeft = popupRect.x + nextOffsetX; const popupRight = popupLeft + popupWidth; From b6d7cdfc01445192f33aa9b0098692f92daef418 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 4 Apr 2023 23:01:39 +0800 Subject: [PATCH 11/12] chore: merge master --- src/hooks/useAlign.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts index f25ae8dc..0f98f868 100644 --- a/src/hooks/useAlign.ts +++ b/src/hooks/useAlign.ts @@ -289,7 +289,7 @@ export default function useAlign( nextOffsetY, ); - // ================ Overflow ================= + // ========================== Overflow =========================== const targetAlignPointTL = getAlignPoint(targetRect, ['t', 'l']); const popupAlignPointTL = getAlignPoint(popupRect, ['t', 'l']); const targetAlignPointBR = getAlignPoint(targetRect, ['b', 'r']); @@ -434,7 +434,7 @@ export default function useAlign( } } - // >>>>> Shift + // ============================ Shift ============================ const numShiftX = shiftX === true ? 0 : shiftX; if (typeof numShiftX === 'number') { // Left @@ -479,6 +479,7 @@ export default function useAlign( } } + // ============================ Arrow ============================ // Arrow center align const popupLeft = popupRect.x + nextOffsetX; const popupRight = popupLeft + popupWidth; From 305242727259c437a9357fb0a4126e66040ec487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 4 Apr 2023 23:09:43 +0800 Subject: [PATCH 12/12] chore: merge master --- src/hooks/useAlign.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/hooks/useAlign.ts b/src/hooks/useAlign.ts index 0f98f868..089bc5e2 100644 --- a/src/hooks/useAlign.ts +++ b/src/hooks/useAlign.ts @@ -305,10 +305,21 @@ export default function useAlign( return val >= 0; }; - // >>>>>>>>>> Top & Bottom - const nextPopupY = popupRect.y + nextOffsetY; - const nextPopupBottom = nextPopupY + popupHeight; + // Prepare position + let nextPopupY: number; + let nextPopupBottom: number; + let nextPopupX: number; + let nextPopupRight: number; + + function syncNextPopupPosition() { + nextPopupY = popupRect.y + nextOffsetY; + nextPopupBottom = nextPopupY + popupHeight; + nextPopupX = popupRect.x + nextOffsetX; + nextPopupRight = nextPopupX + popupWidth; + } + syncNextPopupPosition(); + // >>>>>>>>>> Top & Bottom const needAdjustY = supportAdjust(adjustY); const sameTB = popupPoints[0] === targetPoints[0]; @@ -370,9 +381,6 @@ export default function useAlign( } // >>>>>>>>>> Left & Right - const nextPopupX = popupRect.x + nextOffsetX; - const nextPopupRight = nextPopupX + popupWidth; - const needAdjustX = supportAdjust(adjustX); // >>>>> Flip @@ -435,6 +443,8 @@ export default function useAlign( } // ============================ Shift ============================ + syncNextPopupPosition(); + const numShiftX = shiftX === true ? 0 : shiftX; if (typeof numShiftX === 'number') { // Left