From 1ec2feba7e8724654ba064395503baabc79a7f36 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sat, 26 Oct 2024 22:00:26 +0800 Subject: [PATCH 01/12] feat: picker need to receive the placement aligned by popup --- src/PickerInput/Popup/index.tsx | 4 ++-- src/PickerInput/RangePicker.tsx | 15 ++++++++++++++- src/PickerInput/Selector/RangeSelector.tsx | 6 +++--- src/PickerInput/context.tsx | 4 +++- src/PickerTrigger/index.tsx | 12 +++++++++++- 5 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/PickerInput/Popup/index.tsx b/src/PickerInput/Popup/index.tsx index 5482ada9d..ae6bbc4b0 100644 --- a/src/PickerInput/Popup/index.tsx +++ b/src/PickerInput/Popup/index.tsx @@ -84,7 +84,7 @@ export default function Popup(props: PopupProps(props: PopupProps( onKeyDown?.(event, preventDefault); }; + // ======================= popup align ======================= + const [alignedPlacement, setAlignedPlacement] = React.useState(); + // ======================= Context ======================== const context = React.useMemo( () => ({ @@ -674,8 +677,18 @@ function RangePicker( generateConfig, button: components.button, input: components.input, + alignedPlacement, + setAlignedPlacement, }), - [prefixCls, locale, generateConfig, components.button, components.input], + [ + prefixCls, + locale, + generateConfig, + components.button, + components.input, + alignedPlacement, + setAlignedPlacement, + ], ); // ======================== Effect ======================== diff --git a/src/PickerInput/Selector/RangeSelector.tsx b/src/PickerInput/Selector/RangeSelector.tsx index 7cd36bd29..d6dbd47ab 100644 --- a/src/PickerInput/Selector/RangeSelector.tsx +++ b/src/PickerInput/Selector/RangeSelector.tsx @@ -120,7 +120,7 @@ function RangeSelector( const rtl = direction === 'rtl'; // ======================== Prefix ======================== - const { prefixCls } = React.useContext(PickerContext); + const { prefixCls, alignedPlacement } = React.useContext(PickerContext); // ========================== Id ========================== const ids = React.useMemo(() => { @@ -173,7 +173,7 @@ function RangeSelector( }); // ====================== ActiveBar ======================= - const realPlacement = getRealPlacement(placement, rtl); + const realPlacement = getRealPlacement(alignedPlacement || placement, rtl); const offsetUnit = getoffsetUnit(realPlacement, rtl); const placementRight = realPlacement?.toLowerCase().endsWith('right'); const [activeBarStyle, setActiveBarStyle] = React.useState({ @@ -186,7 +186,7 @@ function RangeSelector( if (input) { const { offsetWidth, offsetLeft, offsetParent } = input.nativeElement; const parentWidth = (offsetParent as HTMLElement)?.offsetWidth || 0; - const activeOffset = placementRight ? (parentWidth - offsetWidth - offsetLeft) : offsetLeft; + const activeOffset = placementRight ? parentWidth - offsetWidth - offsetLeft : offsetLeft; setActiveBarStyle((ori) => ({ ...ori, width: offsetWidth, diff --git a/src/PickerInput/context.tsx b/src/PickerInput/context.tsx index fcbc581b5..f7fb32b5c 100644 --- a/src/PickerInput/context.tsx +++ b/src/PickerInput/context.tsx @@ -9,7 +9,9 @@ export interface PickerContextProps { /** Customize button component */ button?: Components['button']; input?: Components['input']; - + /** trigger will change placement while aligining */ + alignedPlacement?: string; + setAlignedPlacement?: React.Dispatch>; } const PickerContext = React.createContext(null!); diff --git a/src/PickerTrigger/index.tsx b/src/PickerTrigger/index.tsx index a46011345..b87006281 100644 --- a/src/PickerTrigger/index.tsx +++ b/src/PickerTrigger/index.tsx @@ -77,7 +77,7 @@ function PickerTrigger({ visible, onClose, }: PickerTriggerProps) { - const { prefixCls } = React.useContext(PickerContext); + const { prefixCls, setAlignedPlacement } = React.useContext(PickerContext); const dropdownPrefixCls = `${prefixCls}-dropdown`; const realPlacement = getRealPlacement(placement, direction === 'rtl'); @@ -100,6 +100,16 @@ function PickerTrigger({ popupStyle={popupStyle} stretch="minWidth" getPopupContainer={getPopupContainer} + onPopupAlign={(_, align) => { + Object.keys(BUILT_IN_PLACEMENTS).forEach((key) => { + if ( + BUILT_IN_PLACEMENTS[key].points[0] === align.points[0] && + BUILT_IN_PLACEMENTS[key].points[1] === align.points[1] + ) { + setAlignedPlacement?.(key); + } + }); + }} onPopupVisibleChange={(nextVisible) => { if (!nextVisible) { onClose(); From e5f2671fbc84c1803dc776992c7f1c1658a25084 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sat, 26 Oct 2024 22:03:56 +0800 Subject: [PATCH 02/12] feat: add test cases for rang being realigned --- tests/range-aligin.spec.tsx | 134 ++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 tests/range-aligin.spec.tsx diff --git a/tests/range-aligin.spec.tsx b/tests/range-aligin.spec.tsx new file mode 100644 index 000000000..915f8e6a9 --- /dev/null +++ b/tests/range-aligin.spec.tsx @@ -0,0 +1,134 @@ +import { act, cleanup, render } from '@testing-library/react'; +import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; +import React from 'react'; +import { DayRangePicker } from './util/commonUtil'; + +describe('the popup arrow should be placed in the correct position.', () => { + let rangeRect = { x: 0, y: 0, width: 0, height: 0 }; + + beforeEach(() => { + rangeRect = { + x: 0, + y: 0, + width: 200, + height: 100, + }; + + document.documentElement.scrollLeft = 0; + }); + + beforeAll(() => { + jest.spyOn(document.documentElement, 'scrollWidth', 'get').mockReturnValue(1000); + + // Viewport size + spyElementPrototypes(HTMLElement, { + clientWidth: { + get: () => 400, + }, + clientHeight: { + get: () => 400, + }, + }); + + // Popup size + spyElementPrototypes(HTMLDivElement, { + getBoundingClientRect() { + if (this.className.includes('rc-picker-dropdown')) { + return { + x: 0, + y: 0, + width: 300, + height: 100, + }; + } + if (this.className.includes('rc-picker-range')) { + return rangeRect; + } + }, + offsetWidth: { + get() { + if (this.className.includes('rc-picker-range-wrapper')) { + return rangeRect.width; + } + if (this.className.includes('rc-picker-range-arrow')) { + return 10; + } + if (this.className.includes('rc-picker-input')) { + return 100; + } + if (this.className.includes('rc-picker-dropdown')) { + return 300; + } + }, + }, + offsetLeft: { + get() { + if (this.className.includes('rc-picker-input')) { + return 0; + } + }, + }, + }); + spyElementPrototypes(HTMLElement, { + offsetParent: { + get: () => document.body, + }, + }); + }); + + beforeEach(() => { + jest.useFakeTimers(); + }); + + afterEach(() => { + cleanup(); + jest.useRealTimers(); + }); + + it('the arrow should be set to `inset-inline-start` when the popup is aligned to `bottomLeft`.', async () => { + render(); + + await act(async () => { + jest.runAllTimers(); + + await Promise.resolve(); + }); + expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ + 'inset-inline-start': '0', + }); + }); + + it('the arrow should be set to `inset-inline-end` when the popup is aligned to `bottomRight`.', async () => { + const mock = spyElementPrototypes(HTMLDivElement, { + getBoundingClientRect() { + if (this.className.includes('rc-picker-dropdown')) { + return { + x: 0, + y: 0, + width: 300, + height: 100, + }; + } + if (this.className.includes('rc-picker-range')) { + return { + ...rangeRect, + x: 300, + }; + } + }, + }); + + render(); + + await act(async () => { + jest.runAllTimers(); + + await Promise.resolve(); + }); + expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ + 'inset-inline-end': '0', + }); + + mock.mockRestore(); + }); +}); From 1f63ee96c02a3722ac8953c68638f2280048a3e1 Mon Sep 17 00:00:00 2001 From: AKing <827088092@qq.com> Date: Sat, 26 Oct 2024 22:10:56 +0800 Subject: [PATCH 03/12] Update src/PickerTrigger/index.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- src/PickerTrigger/index.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/PickerTrigger/index.tsx b/src/PickerTrigger/index.tsx index b87006281..59f5506d9 100644 --- a/src/PickerTrigger/index.tsx +++ b/src/PickerTrigger/index.tsx @@ -101,14 +101,17 @@ function PickerTrigger({ stretch="minWidth" getPopupContainer={getPopupContainer} onPopupAlign={(_, align) => { - Object.keys(BUILT_IN_PLACEMENTS).forEach((key) => { - if ( + if (!setAlignedPlacement) return; + + const matchedKey = Object.keys(BUILT_IN_PLACEMENTS).find( + (key) => BUILT_IN_PLACEMENTS[key].points[0] === align.points[0] && BUILT_IN_PLACEMENTS[key].points[1] === align.points[1] - ) { - setAlignedPlacement?.(key); - } - }); + ); + + if (matchedKey) { + setAlignedPlacement(matchedKey); + } }} onPopupVisibleChange={(nextVisible) => { if (!nextVisible) { From 18d41e00e01f44149984422da12f18acb03a865e Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sat, 26 Oct 2024 22:12:47 +0800 Subject: [PATCH 04/12] chore: optimize range-align.spec.tsx --- tests/{range-aligin.spec.tsx => range-align.spec.tsx} | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) rename tests/{range-aligin.spec.tsx => range-align.spec.tsx} (99%) diff --git a/tests/range-aligin.spec.tsx b/tests/range-align.spec.tsx similarity index 99% rename from tests/range-aligin.spec.tsx rename to tests/range-align.spec.tsx index 915f8e6a9..b5699a1d2 100644 --- a/tests/range-aligin.spec.tsx +++ b/tests/range-align.spec.tsx @@ -15,6 +15,7 @@ describe('the popup arrow should be placed in the correct position.', () => { }; document.documentElement.scrollLeft = 0; + jest.useFakeTimers(); }); beforeAll(() => { @@ -76,10 +77,6 @@ describe('the popup arrow should be placed in the correct position.', () => { }); }); - beforeEach(() => { - jest.useFakeTimers(); - }); - afterEach(() => { cleanup(); jest.useRealTimers(); From e551d1245c4976842e03fa9b4130ec1879cf0bf2 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Fri, 1 Nov 2024 00:50:17 +0800 Subject: [PATCH 05/12] fix: avtive bar will be wrong position while init --- src/PickerInput/Selector/RangeSelector.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PickerInput/Selector/RangeSelector.tsx b/src/PickerInput/Selector/RangeSelector.tsx index d6dbd47ab..067b06e77 100644 --- a/src/PickerInput/Selector/RangeSelector.tsx +++ b/src/PickerInput/Selector/RangeSelector.tsx @@ -187,8 +187,8 @@ function RangeSelector( const { offsetWidth, offsetLeft, offsetParent } = input.nativeElement; const parentWidth = (offsetParent as HTMLElement)?.offsetWidth || 0; const activeOffset = placementRight ? parentWidth - offsetWidth - offsetLeft : offsetLeft; - setActiveBarStyle((ori) => ({ - ...ori, + setActiveBarStyle(({ position }) => ({ + position, width: offsetWidth, [offsetUnit]: activeOffset, })); @@ -198,7 +198,7 @@ function RangeSelector( React.useEffect(() => { syncActiveOffset(); - }, [activeIndex]); + }, [activeIndex, alignedPlacement]); // ======================== Clear ========================= const showClear = clearIcon && ((value[0] && !disabled[0]) || (value[1] && !disabled[1])); From f33f8413b030cffd72152d69383a7d996b4e45b0 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Fri, 1 Nov 2024 21:23:28 +0800 Subject: [PATCH 06/12] fix: simulate parent width for active bar --- tests/range-align.spec.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/range-align.spec.tsx b/tests/range-align.spec.tsx index b5699a1d2..5db40bd55 100644 --- a/tests/range-align.spec.tsx +++ b/tests/range-align.spec.tsx @@ -74,6 +74,13 @@ describe('the popup arrow should be placed in the correct position.', () => { offsetParent: { get: () => document.body, }, + offsetWidth: { + get() { + if (this.tagName === 'BODY') { + return 200; + } + }, + }, }); }); @@ -123,7 +130,11 @@ describe('the popup arrow should be placed in the correct position.', () => { await Promise.resolve(); }); expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ - 'inset-inline-end': '0', + 'inset-inline-end': '100px', + }); + + expect(document.querySelector('.rc-picker-active-bar')).toHaveStyle({ + 'inset-inline-end': '100px', }); mock.mockRestore(); From cba76c7f9cdda54116f3295601b8481d2b5d0b21 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sun, 3 Nov 2024 12:43:08 +0800 Subject: [PATCH 07/12] fix: arrow place above while placement is `topRight` --- src/PickerInput/RangePicker.tsx | 1 + src/PickerTrigger/index.tsx | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/PickerInput/RangePicker.tsx b/src/PickerInput/RangePicker.tsx index 56c93c048..cb9d19f5c 100644 --- a/src/PickerInput/RangePicker.tsx +++ b/src/PickerInput/RangePicker.tsx @@ -752,6 +752,7 @@ function RangePicker( // Visible visible={mergedOpen} onClose={onPopupClose} + alignedPlacement={alignedPlacement} // Range range > diff --git a/src/PickerTrigger/index.tsx b/src/PickerTrigger/index.tsx index 59f5506d9..d01675568 100644 --- a/src/PickerTrigger/index.tsx +++ b/src/PickerTrigger/index.tsx @@ -54,7 +54,7 @@ export type PickerTriggerProps = { placement?: string; builtinPlacements?: BuildInPlacements; direction?: 'ltr' | 'rtl'; - + alignedPlacement: string; // Visible visible: boolean; onClose: () => void; @@ -72,7 +72,7 @@ function PickerTrigger({ placement, builtinPlacements = BUILT_IN_PLACEMENTS, direction, - + alignedPlacement, // Visible visible, onClose, @@ -80,7 +80,7 @@ function PickerTrigger({ const { prefixCls, setAlignedPlacement } = React.useContext(PickerContext); const dropdownPrefixCls = `${prefixCls}-dropdown`; - const realPlacement = getRealPlacement(placement, direction === 'rtl'); + const realPlacement = getRealPlacement(alignedPlacement || placement, direction === 'rtl'); return ( { if (!setAlignedPlacement) return; - + const matchedKey = Object.keys(BUILT_IN_PLACEMENTS).find( (key) => BUILT_IN_PLACEMENTS[key].points[0] === align.points[0] && - BUILT_IN_PLACEMENTS[key].points[1] === align.points[1] + BUILT_IN_PLACEMENTS[key].points[1] === align.points[1], ); - + if (matchedKey) { setAlignedPlacement(matchedKey); } From 7add0588b8ca1654dc2afa86cb9bba1095bf027f Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sun, 3 Nov 2024 12:44:24 +0800 Subject: [PATCH 08/12] test: move range-align into new-range and fix tests exceed time problem --- tests/new-range.spec.tsx | 140 +++++++++++++++++++++++++++++++++++- tests/range-align.spec.tsx | 142 ------------------------------------- 2 files changed, 138 insertions(+), 144 deletions(-) delete mode 100644 tests/range-align.spec.tsx diff --git a/tests/new-range.spec.tsx b/tests/new-range.spec.tsx index 97427cf69..9c97f29c2 100644 --- a/tests/new-range.spec.tsx +++ b/tests/new-range.spec.tsx @@ -2,7 +2,7 @@ import { act, fireEvent, render } from '@testing-library/react'; import dayjs, { type Dayjs } from 'dayjs'; import 'dayjs/locale/ar'; -import { spyElementPrototype } from 'rc-util/lib/test/domHook'; +import { spyElementPrototype, spyElementPrototypes } from 'rc-util/lib/test/domHook'; import { resetWarned } from 'rc-util/lib/warning'; import React from 'react'; import type { RangePickerProps } from '../src'; @@ -43,6 +43,89 @@ describe('NewPicker.Range', () => { jest.useRealTimers(); }); + let rangeRect = { x: 0, y: 0, width: 0, height: 0 }; + + beforeEach(() => { + rangeRect = { + x: 0, + y: 0, + width: 200, + height: 100, + }; + + document.documentElement.scrollLeft = 0; + // jest.useFakeTimers(); + }); + + beforeAll(() => { + jest.spyOn(document.documentElement, 'scrollWidth', 'get').mockReturnValue(1000); + + // Viewport size + spyElementPrototypes(HTMLElement, { + clientWidth: { + get: () => 400, + }, + clientHeight: { + get: () => 400, + }, + }); + + // Popup size + spyElementPrototypes(HTMLDivElement, { + getBoundingClientRect() { + if (this.className.includes('rc-picker-dropdown')) { + return { + x: 0, + y: 0, + width: 300, + height: 100, + }; + } + if (this.className.includes('rc-picker-range')) { + return rangeRect; + } + if (this.className.includes('rc-picker')) { + return rangeRect; + } + }, + offsetWidth: { + get() { + if (this.className.includes('rc-picker-range-wrapper')) { + return rangeRect.width; + } + if (this.className.includes('rc-picker-range-arrow')) { + return 10; + } + if (this.className.includes('rc-picker-input')) { + return 100; + } + if (this.className.includes('rc-picker-dropdown')) { + return 300; + } + }, + }, + offsetLeft: { + get() { + if (this.className.includes('rc-picker-input')) { + return 0; + } + }, + }, + }); + spyElementPrototypes(HTMLElement, { + offsetParent: { + get: () => document.body, + }, + offsetWidth: { + get() { + if (this.tagName === 'BODY') { + return 200; + } + }, + }, + }); + }); + describe('PickerValue', () => { it('defaultPickerValue should reset every time when opened', () => { const { container } = render( @@ -1098,7 +1181,7 @@ describe('NewPicker.Range', () => { it('pass tabIndex', () => { const { container } = render(
- +
, ); @@ -1338,4 +1421,57 @@ describe('NewPicker.Range', () => { } expect(existed).toBeTruthy(); }); + + describe('pupop aligned position', () => { + it('the arrow should be set to `inset-inline-start` when the popup is aligned to `bottomLeft`.', async () => { + render(); + + await act(async () => { + jest.runAllTimers(); + + await Promise.resolve(); + }); + expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ + 'inset-inline-start': '0', + }); + }); + + it('the arrow should be set to `inset-inline-end` when the popup is aligned to `bottomRight`.', async () => { + const mock = spyElementPrototypes(HTMLDivElement, { + getBoundingClientRect() { + if (this.className.includes('rc-picker-dropdown')) { + return { + x: 0, + y: 0, + width: 300, + height: 100, + }; + } + if (this.className.includes('rc-picker-range')) { + return { + ...rangeRect, + x: 300, + }; + } + }, + }); + + render(); + + await act(async () => { + jest.runAllTimers(); + + await Promise.resolve(); + }); + expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ + 'inset-inline-end': '100px', + }); + + expect(document.querySelector('.rc-picker-active-bar')).toHaveStyle({ + 'inset-inline-end': '100px', + }); + + mock.mockRestore(); + }); + }); }); diff --git a/tests/range-align.spec.tsx b/tests/range-align.spec.tsx deleted file mode 100644 index 5db40bd55..000000000 --- a/tests/range-align.spec.tsx +++ /dev/null @@ -1,142 +0,0 @@ -import { act, cleanup, render } from '@testing-library/react'; -import { spyElementPrototypes } from 'rc-util/lib/test/domHook'; -import React from 'react'; -import { DayRangePicker } from './util/commonUtil'; - -describe('the popup arrow should be placed in the correct position.', () => { - let rangeRect = { x: 0, y: 0, width: 0, height: 0 }; - - beforeEach(() => { - rangeRect = { - x: 0, - y: 0, - width: 200, - height: 100, - }; - - document.documentElement.scrollLeft = 0; - jest.useFakeTimers(); - }); - - beforeAll(() => { - jest.spyOn(document.documentElement, 'scrollWidth', 'get').mockReturnValue(1000); - - // Viewport size - spyElementPrototypes(HTMLElement, { - clientWidth: { - get: () => 400, - }, - clientHeight: { - get: () => 400, - }, - }); - - // Popup size - spyElementPrototypes(HTMLDivElement, { - getBoundingClientRect() { - if (this.className.includes('rc-picker-dropdown')) { - return { - x: 0, - y: 0, - width: 300, - height: 100, - }; - } - if (this.className.includes('rc-picker-range')) { - return rangeRect; - } - }, - offsetWidth: { - get() { - if (this.className.includes('rc-picker-range-wrapper')) { - return rangeRect.width; - } - if (this.className.includes('rc-picker-range-arrow')) { - return 10; - } - if (this.className.includes('rc-picker-input')) { - return 100; - } - if (this.className.includes('rc-picker-dropdown')) { - return 300; - } - }, - }, - offsetLeft: { - get() { - if (this.className.includes('rc-picker-input')) { - return 0; - } - }, - }, - }); - spyElementPrototypes(HTMLElement, { - offsetParent: { - get: () => document.body, - }, - offsetWidth: { - get() { - if (this.tagName === 'BODY') { - return 200; - } - }, - }, - }); - }); - - afterEach(() => { - cleanup(); - jest.useRealTimers(); - }); - - it('the arrow should be set to `inset-inline-start` when the popup is aligned to `bottomLeft`.', async () => { - render(); - - await act(async () => { - jest.runAllTimers(); - - await Promise.resolve(); - }); - expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ - 'inset-inline-start': '0', - }); - }); - - it('the arrow should be set to `inset-inline-end` when the popup is aligned to `bottomRight`.', async () => { - const mock = spyElementPrototypes(HTMLDivElement, { - getBoundingClientRect() { - if (this.className.includes('rc-picker-dropdown')) { - return { - x: 0, - y: 0, - width: 300, - height: 100, - }; - } - if (this.className.includes('rc-picker-range')) { - return { - ...rangeRect, - x: 300, - }; - } - }, - }); - - render(); - - await act(async () => { - jest.runAllTimers(); - - await Promise.resolve(); - }); - expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ - 'inset-inline-end': '100px', - }); - - expect(document.querySelector('.rc-picker-active-bar')).toHaveStyle({ - 'inset-inline-end': '100px', - }); - - mock.mockRestore(); - }); -}); From d02ddec3caf7fab84478c6dde337a2508cd79e03 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sun, 3 Nov 2024 12:53:00 +0800 Subject: [PATCH 09/12] chore: merge two beforeEach hooks logics --- tests/new-range.spec.tsx | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/new-range.spec.tsx b/tests/new-range.spec.tsx index 9c97f29c2..c6e9bae2d 100644 --- a/tests/new-range.spec.tsx +++ b/tests/new-range.spec.tsx @@ -25,6 +25,7 @@ jest.mock('rc-util/lib/Dom/isVisible', () => { }); describe('NewPicker.Range', () => { + let rangeRect = { x: 0, y: 0, width: 0, height: 0 }; beforeEach(() => { resetWarned(); jest.useFakeTimers().setSystemTime(getDay('1990-09-03 00:00:00').valueOf()); @@ -36,16 +37,8 @@ describe('NewPicker.Range', () => { return childList.indexOf(this) * 30; }, }); - }); - - afterEach(() => { - jest.clearAllTimers(); - jest.useRealTimers(); - }); - let rangeRect = { x: 0, y: 0, width: 0, height: 0 }; - - beforeEach(() => { + // =============== handle trigger align =============== rangeRect = { x: 0, y: 0, @@ -54,7 +47,11 @@ describe('NewPicker.Range', () => { }; document.documentElement.scrollLeft = 0; - // jest.useFakeTimers(); + }); + + afterEach(() => { + jest.clearAllTimers(); + jest.useRealTimers(); }); beforeAll(() => { From 05eeced6aff1e54e00cd20e3f37b3aa2efaff6bd Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sun, 3 Nov 2024 15:40:23 +0800 Subject: [PATCH 10/12] fix: active bar always offset 1px --- src/PickerInput/Selector/RangeSelector.tsx | 24 +++++++++++++++++----- src/PickerInput/Selector/util.ts | 6 +++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/PickerInput/Selector/RangeSelector.tsx b/src/PickerInput/Selector/RangeSelector.tsx index 067b06e77..a5e36e80e 100644 --- a/src/PickerInput/Selector/RangeSelector.tsx +++ b/src/PickerInput/Selector/RangeSelector.tsx @@ -9,6 +9,7 @@ import useRootProps from './hooks/useRootProps'; import Icon, { ClearIcon } from './Icon'; import Input, { type InputRef } from './Input'; import { getoffsetUnit, getRealPlacement } from '../../utils/uiUtil'; +import { getWin } from './util'; export type SelectorIdType = | string @@ -184,13 +185,26 @@ function RangeSelector( const syncActiveOffset = useEvent(() => { const input = getInput(activeIndex); if (input) { - const { offsetWidth, offsetLeft, offsetParent } = input.nativeElement; - const parentWidth = (offsetParent as HTMLElement)?.offsetWidth || 0; - const activeOffset = placementRight ? parentWidth - offsetWidth - offsetLeft : offsetLeft; + const { offsetParent } = input.nativeElement; + // offsetLeft is an integer, which will cause incorrect reulst. + const { x = 0, width: inputWidth = 0 } = input.nativeElement.getBoundingClientRect() || {}; + const { x: pX = 0, width: parentWidth = 0 } = offsetParent?.getBoundingClientRect() || {}; + const parentStyles = + offsetParent && getWin(offsetParent as HTMLElement).getComputedStyle(offsetParent); + const parentBorderRightWidth = Number( + (placementRight ? parentStyles?.borderRightWidth : parentStyles?.borderLeftWidth)?.replace( + 'px', + '', + ) || 0, + ); + const offsetLeft = x - pX; + + const activeOffset = placementRight ? parentWidth - inputWidth - offsetLeft : offsetLeft; setActiveBarStyle(({ position }) => ({ position, - width: offsetWidth, - [offsetUnit]: activeOffset, + width: inputWidth, + // parent will have border while focus, so need to cut `parentBorderWidth` on opposite side. + [offsetUnit]: activeOffset - parentBorderRightWidth, })); onActiveOffset(activeOffset); } diff --git a/src/PickerInput/Selector/util.ts b/src/PickerInput/Selector/util.ts index 245b3dcc8..4eb7a6af8 100644 --- a/src/PickerInput/Selector/util.ts +++ b/src/PickerInput/Selector/util.ts @@ -12,4 +12,8 @@ export function getMaskRange(key: string): [startVal: number, endVal: number, de }; return PresetRange[key]; -} \ No newline at end of file +} + +export function getWin(ele: HTMLElement) { + return ele.ownerDocument.defaultView; +} From 9e4ee41f311f5b442cd5c958e8092a9c22192454 Mon Sep 17 00:00:00 2001 From: jingzouzou <827088092@qq.com> Date: Sun, 3 Nov 2024 15:43:10 +0800 Subject: [PATCH 11/12] test: change the proxy `offsetLeft` to proxy `getBoundingClientRect` --- tests/new-range.spec.tsx | 60 ++++++++++++++++++++++++++++++++-------- tests/range.spec.tsx | 39 +++++++++++++++++++++----- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/tests/new-range.spec.tsx b/tests/new-range.spec.tsx index c6e9bae2d..32961cea6 100644 --- a/tests/new-range.spec.tsx +++ b/tests/new-range.spec.tsx @@ -57,16 +57,6 @@ describe('NewPicker.Range', () => { beforeAll(() => { jest.spyOn(document.documentElement, 'scrollWidth', 'get').mockReturnValue(1000); - // Viewport size - spyElementPrototypes(HTMLElement, { - clientWidth: { - get: () => 400, - }, - clientHeight: { - get: () => 400, - }, - }); - // Popup size spyElementPrototypes(HTMLDivElement, { getBoundingClientRect() { @@ -110,6 +100,13 @@ describe('NewPicker.Range', () => { }, }); spyElementPrototypes(HTMLElement, { + // Viewport size + clientWidth: { + get: () => 400, + }, + clientHeight: { + get: () => 400, + }, offsetParent: { get: () => document.body, }, @@ -120,6 +117,17 @@ describe('NewPicker.Range', () => { } }, }, + // offsetParent + getBoundingClientRect() { + if (this.tagName === 'BODY') { + return { + x: 0, + y: 0, + width: 200, + height: 200, + }; + } + }, }); }); @@ -1423,14 +1431,28 @@ describe('NewPicker.Range', () => { it('the arrow should be set to `inset-inline-start` when the popup is aligned to `bottomLeft`.', async () => { render(); + const oriGetComputedStyle = window.getComputedStyle; + window.getComputedStyle = (ele: HTMLElement) => { + const retObj = oriGetComputedStyle(ele); + + retObj.borderRightWidth = '4px'; + retObj.borderLeftWidth = '2px'; + return retObj; + }; + await act(async () => { jest.runAllTimers(); await Promise.resolve(); }); + expect(document.querySelector('.rc-picker-range-arrow')).toHaveStyle({ 'inset-inline-start': '0', }); + expect(document.querySelector('.rc-picker-active-bar')).toHaveStyle({ + 'inset-inline-start': '-2px', + }); + window.getComputedStyle = oriGetComputedStyle; }); it('the arrow should be set to `inset-inline-end` when the popup is aligned to `bottomRight`.', async () => { @@ -1450,9 +1472,24 @@ describe('NewPicker.Range', () => { x: 300, }; } + if (this.className.includes('rc-picker-input')) { + return { + ...rangeRect, + width: 100, + }; + } }, }); + const oriGetComputedStyle = window.getComputedStyle; + window.getComputedStyle = (ele: HTMLElement) => { + const retObj = oriGetComputedStyle(ele); + + retObj.borderRightWidth = '4px'; + retObj.borderLeftWidth = '2px'; + return retObj; + }; + render(); await act(async () => { @@ -1465,10 +1502,11 @@ describe('NewPicker.Range', () => { }); expect(document.querySelector('.rc-picker-active-bar')).toHaveStyle({ - 'inset-inline-end': '100px', + 'inset-inline-end': '96px', }); mock.mockRestore(); + window.getComputedStyle = oriGetComputedStyle; }); }); }); diff --git a/tests/range.spec.tsx b/tests/range.spec.tsx index db5660154..87f2d37ae 100644 --- a/tests/range.spec.tsx +++ b/tests/range.spec.tsx @@ -541,7 +541,7 @@ describe('Picker.Range', () => { it('pass tabIndex', () => { const { container } = render(
- +
, ); @@ -705,12 +705,7 @@ describe('Picker.Range', () => { }); it('prefix', () => { - render( - } - allowClear - />, - ); + render(} allowClear />); expect(document.querySelector('.prefix')).toBeInTheDocument(); }); @@ -1771,6 +1766,36 @@ describe('Picker.Range', () => { } }, }, + getBoundingClientRect() { + if (this.className.includes('rc-picker-dropdown')) { + return { + x: 0, + y: 0, + width: 300, + }; + } + if (this.className.includes('rc-picker-range')) { + return { + x: 0, + y: 0, + width: 200, + }; + } + if (this.className.includes('rc-picker-input')) { + return { + x: 100, + y: 0, + width: 100, + }; + } + if (this.className.includes('rc-picker')) { + return { + x: 0, + y: 0, + width: 200, + }; + } + }, }); const { container } = render( Date: Fri, 8 Nov 2024 00:39:18 +0800 Subject: [PATCH 12/12] fix: tsc error --- src/PickerTrigger/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PickerTrigger/index.tsx b/src/PickerTrigger/index.tsx index d01675568..e13e48cae 100644 --- a/src/PickerTrigger/index.tsx +++ b/src/PickerTrigger/index.tsx @@ -54,7 +54,7 @@ export type PickerTriggerProps = { placement?: string; builtinPlacements?: BuildInPlacements; direction?: 'ltr' | 'rtl'; - alignedPlacement: string; + alignedPlacement?: string; // Visible visible: boolean; onClose: () => void;