From 035d0e214285460744fb63dac224cc862f51052c Mon Sep 17 00:00:00 2001 From: muzea Date: Tue, 19 Aug 2025 22:36:07 +0800 Subject: [PATCH 01/10] perf: skipping re-rendering --- src/Filler.tsx | 19 ++++++++++++------- src/List.tsx | 8 +++++--- src/hooks/useChildren.tsx | 33 ++++++++++++++++++--------------- src/hooks/useHeights.tsx | 13 +++++++------ 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/Filler.tsx b/src/Filler.tsx index 5e480e25..f52971b9 100644 --- a/src/Filler.tsx +++ b/src/Filler.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; -import ResizeObserver from 'rc-resize-observer'; import classNames from 'classnames'; +import ResizeObserver from 'rc-resize-observer'; +import * as React from 'react'; export type InnerProps = Pick, 'role' | 'id'>; @@ -69,14 +69,19 @@ const Filler = React.forwardRef( }; } + const handleResize = React.useCallback( + ({ offsetHeight }) => { + if (offsetHeight && onInnerResize) { + onInnerResize(); + } + }, + [onInnerResize], + ); + return (
{ - if (offsetHeight && onInnerResize) { - onInnerResize(); - } - }} + onResize={handleResize} >
(props: ListProps, ref: React.Ref) { setScrollMoving(false); }; - const sharedConfig: SharedConfig = { - getKey, - }; + const sharedConfig: SharedConfig = React.useMemo(() => { + return { + getKey, + }; + }, [getKey]); // ================================ Scroll ================================ function syncScrollTop(newTop: number | ((prev: number) => number)) { diff --git a/src/hooks/useChildren.tsx b/src/hooks/useChildren.tsx index 8c4fdf6d..00bc2219 100644 --- a/src/hooks/useChildren.tsx +++ b/src/hooks/useChildren.tsx @@ -12,20 +12,23 @@ export default function useChildren( renderFunc: RenderFunc, { getKey }: SharedConfig, ) { - return list.slice(startIndex, endIndex + 1).map((item, index) => { - const eleIndex = startIndex + index; - const node = renderFunc(item, eleIndex, { - style: { - width: scrollWidth, - }, - offsetX, - }) as React.ReactElement; + // 可能存在 list 不变但是里面的数据存在变化的情况,会与之前写法存在不同的行为 + return React.useMemo(() => { + return list.slice(startIndex, endIndex + 1).map((item, index) => { + const eleIndex = startIndex + index; + const node = renderFunc(item, eleIndex, { + style: { + width: scrollWidth, + }, + offsetX, + }) as React.ReactElement; - const key = getKey(item); - return ( - setNodeRef(item, ele)}> - {node} - - ); - }); + const key = getKey(item); + return ( + setNodeRef(item, ele)}> + {node} + + ); + }); + }, [list, startIndex, endIndex, setNodeRef, renderFunc, getKey, offsetX, scrollWidth]); } diff --git a/src/hooks/useHeights.tsx b/src/hooks/useHeights.tsx index ed13de72..bd7865aa 100644 --- a/src/hooks/useHeights.tsx +++ b/src/hooks/useHeights.tsx @@ -24,11 +24,11 @@ export default function useHeights( const promiseIdRef = useRef(0); - function cancelRaf() { + const cancelRaf = React.useCallback(function cancelRaf() { promiseIdRef.current += 1; - } + }, []); - function collectHeight(sync = false) { + const collectHeight = React.useCallback(function (sync = false) { cancelRaf(); const doCollect = () => { @@ -67,9 +67,9 @@ export default function useHeights( } }); } - } + }, [cancelRaf]); - function setInstanceRef(item: T, instance: HTMLElement) { + const setInstanceRef = React.useCallback(function setInstanceRef(item: T, instance: HTMLElement) { const key = getKey(item); const origin = instanceRef.current.get(key); @@ -88,11 +88,12 @@ export default function useHeights( onItemRemove?.(item); } } - } + }, [collectHeight, getKey, onItemAdd, onItemRemove]); useEffect(() => { return cancelRaf; }, []); + // 这里稍显迷惑性,当 heightsRef.current.set 被调用时,updatedMark 会变化,进而导致 heightsRef.current 也出现变化 return [setInstanceRef, collectHeight, heightsRef.current, updatedMark]; } From 03d7e0dcb0bc898be25de9db14cc3b7ec1b3407c Mon Sep 17 00:00:00 2001 From: muzea Date: Wed, 20 Aug 2025 22:59:27 +0800 Subject: [PATCH 02/10] fix: some test error --- tests/scroll-Firefox.test.js | 12 +++-- tests/scroll.test.js | 31 ++++++++---- tests/scrollWidth.test.tsx | 8 +++- tests/touch.test.js | 92 ++++++++++++++++++++---------------- 4 files changed, 84 insertions(+), 59 deletions(-) diff --git a/tests/scroll-Firefox.test.js b/tests/scroll-Firefox.test.js index 2a9290ea..e0ecc746 100644 --- a/tests/scroll-Firefox.test.js +++ b/tests/scroll-Firefox.test.js @@ -1,9 +1,9 @@ -import React from 'react'; -import { act } from 'react-dom/test-utils'; +import { act } from '@testing-library/react'; import { mount } from 'enzyme'; -import { spyElementPrototypes } from './utils/domHook'; +import React from 'react'; import List from '../src'; import isFF from '../src/utils/isFirefox'; +import { spyElementPrototypes } from './utils/domHook'; function genData(count) { return new Array(count).fill(null).map((_, index) => ({ id: String(index) })); @@ -124,8 +124,10 @@ describe('List.Firefox-Scroll', () => { const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100), ref: listRef }); const ulElement = wrapper.find('ul').instance(); // scroll to bottom - listRef.current.scrollTo(99999); - jest.runAllTimers(); + act(() => { + listRef.current.scrollTo(99999); + jest.runAllTimers(); + }); expect(wrapper.find('ul').instance().scrollTop).toEqual(1900); act(() => { diff --git a/tests/scroll.test.js b/tests/scroll.test.js index 2e30f468..2d2d6596 100644 --- a/tests/scroll.test.js +++ b/tests/scroll.test.js @@ -94,9 +94,13 @@ describe('List.Scroll', () => { jest.useFakeTimers(); const listRef = React.createRef(); const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100), ref: listRef }); - jest.runAllTimers(); - listRef.current.scrollTo(null); + act(() => { + jest.runAllTimers(); + + listRef.current.scrollTo(null); + }); + expect(wrapper.find('.rc-virtual-list-scrollbar-thumb').props().style.display).not.toEqual( 'none', ); @@ -107,8 +111,10 @@ describe('List.Scroll', () => { it('value scroll', () => { const listRef = React.createRef(); const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100), ref: listRef }); - listRef.current.scrollTo(903); - jest.runAllTimers(); + act(() => { + listRef.current.scrollTo(903); + jest.runAllTimers(); + }); expect(wrapper.find('ul').instance().scrollTop).toEqual(903); wrapper.unmount(); @@ -125,9 +131,8 @@ describe('List.Scroll', () => { ...result, ref, scrollTo: (...args) => { - ref.current.scrollTo(...args); - act(() => { + ref.current.scrollTo(...args); jest.runAllTimers(); }); }, @@ -153,8 +158,10 @@ describe('List.Scroll', () => { it('scroll top should not out of range', () => { const { scrollTo, container } = presetList(); - scrollTo({ index: 0, align: 'bottom' }); - jest.runAllTimers(); + act(() => { + scrollTo({ index: 0, align: 'bottom' }); + jest.runAllTimers(); + }); expect(container.querySelector('ul').scrollTop).toEqual(0); }); @@ -389,9 +396,13 @@ describe('List.Scroll', () => { ref: listRef, direction: 'rtl', }); - jest.runAllTimers(); - listRef.current.scrollTo(null); + act(() => { + jest.runAllTimers(); + + listRef.current.scrollTo(null); + }); + expect(wrapper.find('.rc-virtual-list-scrollbar-thumb').props().style.display).not.toEqual( 'none', ); diff --git a/tests/scrollWidth.test.tsx b/tests/scrollWidth.test.tsx index 79a141c1..094f40d5 100644 --- a/tests/scrollWidth.test.tsx +++ b/tests/scrollWidth.test.tsx @@ -230,10 +230,14 @@ describe('List.scrollWidth', () => { ref: listRef, }); - listRef.current.scrollTo({ left: 135 }); + act(() => { + listRef.current.scrollTo({ left: 135 }); + }); expect(listRef.current.getScrollInfo()).toEqual({ x: 135, y: 0 }); - listRef.current.scrollTo({ left: -99 }); + act(() => { + listRef.current.scrollTo({ left: -99 }); + }); expect(listRef.current.getScrollInfo()).toEqual({ x: 0, y: 0 }); }); diff --git a/tests/touch.test.js b/tests/touch.test.js index fa62a6a2..3c6e2645 100644 --- a/tests/touch.test.js +++ b/tests/touch.test.js @@ -71,22 +71,24 @@ describe('List.Touch', () => { return wrapper.find('.rc-virtual-list-holder').instance(); } - // start - const touchEvent = new Event('touchstart'); - touchEvent.touches = [{ pageY: 100 }]; - getElement().dispatchEvent(touchEvent); - - // move - const moveEvent = new Event('touchmove'); - moveEvent.touches = [{ pageY: 90 }]; - getElement().dispatchEvent(moveEvent); - - // end - const endEvent = new Event('touchend'); - getElement().dispatchEvent(endEvent); - - // smooth - jest.runAllTimers(); + act(() => { + // start + const touchEvent = new Event('touchstart'); + touchEvent.touches = [{ pageY: 100 }]; + getElement().dispatchEvent(touchEvent); + + // move + const moveEvent = new Event('touchmove'); + moveEvent.touches = [{ pageY: 90 }]; + getElement().dispatchEvent(moveEvent); + + // end + const endEvent = new Event('touchend'); + getElement().dispatchEvent(endEvent); + + // smooth + jest.runAllTimers(); + }); expect(wrapper.find('ul').instance().scrollTop > 10).toBeTruthy(); wrapper.unmount(); @@ -99,35 +101,39 @@ describe('List.Touch', () => { return wrapper.find('.rc-virtual-list-holder').instance(); } - // start - const touchEvent = new Event('touchstart'); - touchEvent.touches = [{ pageY: 500 }]; - getElement().dispatchEvent(touchEvent); - - // move const preventDefault = jest.fn(); - const moveEvent = new Event('touchmove'); - moveEvent.touches = [{ pageY: 0 }]; - moveEvent.preventDefault = preventDefault; - getElement().dispatchEvent(moveEvent); + act(() => { + // start + const touchEvent = new Event('touchstart'); + touchEvent.touches = [{ pageY: 500 }]; + getElement().dispatchEvent(touchEvent); + + // move + const moveEvent = new Event('touchmove'); + moveEvent.touches = [{ pageY: 0 }]; + moveEvent.preventDefault = preventDefault; + getElement().dispatchEvent(moveEvent); + }); // Call preventDefault expect(preventDefault).toHaveBeenCalled(); - // ======= Not call since scroll to the bottom ======= - jest.runAllTimers(); - preventDefault.mockReset(); + act(() => { + // ======= Not call since scroll to the bottom ======= + jest.runAllTimers(); + preventDefault.mockReset(); - // start - const touchEvent2 = new Event('touchstart'); - touchEvent2.touches = [{ pageY: 500 }]; - getElement().dispatchEvent(touchEvent2); + // start + const touchEvent2 = new Event('touchstart'); + touchEvent2.touches = [{ pageY: 500 }]; + getElement().dispatchEvent(touchEvent2); - // move - const moveEvent2 = new Event('touchmove'); - moveEvent2.touches = [{ pageY: 0 }]; - moveEvent2.preventDefault = preventDefault; - getElement().dispatchEvent(moveEvent2); + // move + const moveEvent2 = new Event('touchmove'); + moveEvent2.touches = [{ pageY: 0 }]; + moveEvent2.preventDefault = preventDefault; + getElement().dispatchEvent(moveEvent2); + }); expect(preventDefault).not.toHaveBeenCalled(); }); @@ -137,16 +143,18 @@ describe('List.Touch', () => { const preventDefault = jest.fn(); const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) }); - const touchEvent = new Event('touchstart'); - touchEvent.preventDefault = preventDefault; - wrapper.find('.rc-virtual-list-scrollbar').instance().dispatchEvent(touchEvent); + act(() => { + const touchEvent = new Event('touchstart'); + touchEvent.preventDefault = preventDefault; + wrapper.find('.rc-virtual-list-scrollbar').instance().dispatchEvent(touchEvent); + }); expect(preventDefault).toHaveBeenCalled(); }); it('nest touch', async () => { const { container } = render( - + {({ id }) => id === '0' ? (
  • From 50985c2886380c65f7ac850c9ba04ecb0a1c3811 Mon Sep 17 00:00:00 2001 From: muzea Date: Sat, 30 Aug 2025 20:36:26 +0800 Subject: [PATCH 03/10] test: no unnecessary re-render --- tests/props.test.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/props.test.js b/tests/props.test.js index bbc41e81..79d39ac9 100644 --- a/tests/props.test.js +++ b/tests/props.test.js @@ -53,4 +53,28 @@ describe('Props', () => { expect(scrollLeft).toEqual(0); }); + + it('no unnecessary re-render', () => { + const renderItem = sinon.fake(({ id, key }) =>
    {id}
    ); + const data = [{ id: 1, key: 1 }]; + function Wrapper() { + const [state, setState] = React.useState(0); + + React.useEffect(() => { + setState(1); + }, []); + + return ( +
    +

    {state}

    + + {renderItem} + +
    + ); + } + const wrapper = mount(); + expect(wrapper.find('h1').text()).toBe('1'); + expect(renderItem.callCount).toBe(1); + }); }); From 3dd92389bec79f088e5840651a14c16dcaec7665 Mon Sep 17 00:00:00 2001 From: muzea Date: Sat, 30 Aug 2025 20:36:40 +0800 Subject: [PATCH 04/10] fix: code style --- tests/props.test.js | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/tests/props.test.js b/tests/props.test.js index 79d39ac9..887a4c12 100644 --- a/tests/props.test.js +++ b/tests/props.test.js @@ -1,5 +1,5 @@ -import React from 'react'; import { mount } from 'enzyme'; +import React from 'react'; import List from '../src'; describe('Props', () => { @@ -11,30 +11,20 @@ describe('Props', () => { } const wrapper = mount( - item.id}> + item.id}> {({ id }) => {id}} , ); - expect( - wrapper - .find('Item') - .at(0) - .key(), - ).toBe('903'); + expect(wrapper.find('Item').at(0).key()).toBe('903'); - expect( - wrapper - .find('Item') - .at(1) - .key(), - ).toBe('1128'); + expect(wrapper.find('Item').at(1).key()).toBe('1128'); }); it('prefixCls', () => { const wrapper = mount( - id} prefixCls="prefix"> - {id =>
    {id}
    } + id} prefixCls="prefix"> + {(id) =>
    {id}
    }
    , ); @@ -44,10 +34,11 @@ describe('Props', () => { it('offsetX in renderFn', () => { let scrollLeft; mount( - id} prefixCls="prefix"> - {(id, _, { offsetX }) => { + id} prefixCls="prefix"> + {(id, _, { offsetX }) => { scrollLeft = offsetX; - return
    {id}
    }} + return
    {id}
    ; + }}
    , ); From 46ee6b4f60ed95bb9dcef5399f7e207ec541168e Mon Sep 17 00:00:00 2001 From: muzea Date: Sat, 30 Aug 2025 20:43:19 +0800 Subject: [PATCH 05/10] fix: test code error --- tests/props.test.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/props.test.js b/tests/props.test.js index 887a4c12..d3209b20 100644 --- a/tests/props.test.js +++ b/tests/props.test.js @@ -46,7 +46,9 @@ describe('Props', () => { }); it('no unnecessary re-render', () => { - const renderItem = sinon.fake(({ id, key }) =>
    {id}
    ); + const renderItem = jest.fn(); + renderItem.mockImplementation(({ id, key }) =>
    {id}
    ); + const data = [{ id: 1, key: 1 }]; function Wrapper() { const [state, setState] = React.useState(0); @@ -66,6 +68,6 @@ describe('Props', () => { } const wrapper = mount(); expect(wrapper.find('h1').text()).toBe('1'); - expect(renderItem.callCount).toBe(1); + expect(renderItem).toHaveBeenCalledTimes(1); }); }); From 2a00a14101f0d0fcb89e953c62c0323ece666d2f Mon Sep 17 00:00:00 2001 From: MuYu Date: Mon, 15 Sep 2025 17:45:54 +0000 Subject: [PATCH 06/10] fix: remove sharedConfig --- src/List.tsx | 10 ++-------- src/hooks/useChildren.tsx | 4 ++-- src/interface.ts | 4 ---- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/List.tsx b/src/List.tsx index ff205362..b699caae 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -18,7 +18,7 @@ import useOriginScroll from './hooks/useOriginScroll'; import useScrollDrag from './hooks/useScrollDrag'; import type { ScrollPos, ScrollTarget } from './hooks/useScrollTo'; import useScrollTo from './hooks/useScrollTo'; -import type { ExtraRenderInfo, GetKey, RenderFunc, SharedConfig } from './interface'; +import type { ExtraRenderInfo, GetKey, RenderFunc } from './interface'; import type { ScrollBarDirectionType, ScrollBarRef } from './ScrollBar'; import ScrollBar from './ScrollBar'; import { getSpinSize } from './utils/scrollbarUtil'; @@ -165,12 +165,6 @@ export function RawList(props: ListProps, ref: React.Ref) { setScrollMoving(false); }; - const sharedConfig: SharedConfig = React.useMemo(() => { - return { - getKey, - }; - }, [getKey]); - // ================================ Scroll ================================ function syncScrollTop(newTop: number | ((prev: number) => number)) { setOffsetTop((origin) => { @@ -574,7 +568,7 @@ export function RawList(props: ListProps, ref: React.Ref) { offsetLeft, setInstanceRef, children, - sharedConfig, + getKey, ); let componentStyle: React.CSSProperties = null; diff --git a/src/hooks/useChildren.tsx b/src/hooks/useChildren.tsx index 00bc2219..80bc64d3 100644 --- a/src/hooks/useChildren.tsx +++ b/src/hooks/useChildren.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import type { RenderFunc, SharedConfig } from '../interface'; +import type { RenderFunc, GetKey } from '../interface'; import { Item } from '../Item'; export default function useChildren( @@ -10,7 +10,7 @@ export default function useChildren( offsetX: number, setNodeRef: (item: T, element: HTMLElement) => void, renderFunc: RenderFunc, - { getKey }: SharedConfig, + getKey: GetKey, ) { // 可能存在 list 不变但是里面的数据存在变化的情况,会与之前写法存在不同的行为 return React.useMemo(() => { diff --git a/src/interface.ts b/src/interface.ts index e0fd765d..3b903d54 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -4,10 +4,6 @@ export type RenderFunc = ( props: { style: React.CSSProperties; offsetX: number }, ) => React.ReactNode; -export interface SharedConfig { - getKey: (item: T) => React.Key; -} - export type GetKey = (item: T) => React.Key; export type GetSize = (startKey: React.Key, endKey?: React.Key) => { top: number; bottom: number }; From a6914a11bd97b36d39afb96630fe2a7e8222a5b2 Mon Sep 17 00:00:00 2001 From: MuYu Date: Mon, 15 Sep 2025 18:01:00 +0000 Subject: [PATCH 07/10] test: origin scroll --- tests/touch.test.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/touch.test.js b/tests/touch.test.js index 3c6e2645..82de3089 100644 --- a/tests/touch.test.js +++ b/tests/touch.test.js @@ -94,6 +94,40 @@ describe('List.Touch', () => { wrapper.unmount(); }); + it('origin scroll', () => { + const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) }); + + function getElement() { + return wrapper.find('.rc-virtual-list-holder').instance(); + } + + act(() => { + // start + const touchEvent = new Event('touchstart'); + touchEvent.touches = [{ pageY: 100 }]; + getElement().dispatchEvent(touchEvent); + + // move + const moveEvent1 = new Event('touchmove'); + moveEvent1.touches = [{ pageY: 110 }]; + getElement().dispatchEvent(moveEvent1); + + // move + const moveEvent2 = new Event('touchmove'); + moveEvent2.touches = [{ pageY: 150 }]; + getElement().dispatchEvent(moveEvent2); + + // end + const endEvent = new Event('touchend'); + getElement().dispatchEvent(endEvent); + + // smooth + jest.runAllTimers(); + }); + expect(wrapper.find('ul').instance().scrollTop).toBe(0); + wrapper.unmount(); + }); + it('not call when not scroll-able', () => { const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) }); From e82a20d0d0bef2aee4117287255f5a1ea2d7587c Mon Sep 17 00:00:00 2001 From: MuYu Date: Mon, 15 Sep 2025 18:05:37 +0000 Subject: [PATCH 08/10] docs: apply copilot suggestion --- src/hooks/useChildren.tsx | 2 +- src/hooks/useHeights.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/hooks/useChildren.tsx b/src/hooks/useChildren.tsx index 80bc64d3..55f8f584 100644 --- a/src/hooks/useChildren.tsx +++ b/src/hooks/useChildren.tsx @@ -12,7 +12,7 @@ export default function useChildren( renderFunc: RenderFunc, getKey: GetKey, ) { - // 可能存在 list 不变但是里面的数据存在变化的情况,会与之前写法存在不同的行为 + // The list reference may remain unchanged, but its internal data may change, which can result in different behavior compared to the previous implementation. return React.useMemo(() => { return list.slice(startIndex, endIndex + 1).map((item, index) => { const eleIndex = startIndex + index; diff --git a/src/hooks/useHeights.tsx b/src/hooks/useHeights.tsx index bd7865aa..a7d83ccd 100644 --- a/src/hooks/useHeights.tsx +++ b/src/hooks/useHeights.tsx @@ -94,6 +94,6 @@ export default function useHeights( return cancelRaf; }, []); - // 这里稍显迷惑性,当 heightsRef.current.set 被调用时,updatedMark 会变化,进而导致 heightsRef.current 也出现变化 + // This is somewhat confusing: when heightsRef.current.set is called, updatedMark changes, which in turn causes heightsRef.current to also change. return [setInstanceRef, collectHeight, heightsRef.current, updatedMark]; } From 3fa5f31d9f79c26f31cc6a229d87980c220ec404 Mon Sep 17 00:00:00 2001 From: MuYu Date: Thu, 25 Sep 2025 17:29:10 +0000 Subject: [PATCH 09/10] test: try to improve coverage --- tests/touch.test.js | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/touch.test.js b/tests/touch.test.js index 82de3089..fef06197 100644 --- a/tests/touch.test.js +++ b/tests/touch.test.js @@ -128,6 +128,49 @@ describe('List.Touch', () => { wrapper.unmount(); }); + it('origin scroll', () => { + const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) }); + + function getElement() { + return wrapper.find('.rc-virtual-list-holder').instance(); + } + + // start + const touchEvent = new Event('touchstart'); + touchEvent.touches = [{ pageY: 100 }]; + getElement().dispatchEvent(touchEvent); + + // move + const moveEvent1 = new Event('touchmove'); + moveEvent1.touches = [{ pageY: 110 }]; + getElement().dispatchEvent(moveEvent1); + + // move + const moveEvent2 = new Event('touchmove'); + moveEvent2.touches = [{ pageY: 150 }]; + getElement().dispatchEvent(moveEvent2); + + // move + const moveEvent3 = new Event('touchmove'); + moveEvent3.touches = [{ pageY: 20 }]; + getElement().dispatchEvent(moveEvent3); + + // move + const moveEvent4 = new Event('touchmove'); + moveEvent4.touches = [{ pageY: 100 }]; + getElement().dispatchEvent(moveEvent4); + + // end + const endEvent = new Event('touchend'); + getElement().dispatchEvent(endEvent); + + // smooth + jest.runAllTimers(); + + expect(wrapper.find('ul').instance().scrollTop).toBe(0); + wrapper.unmount(); + }); + it('not call when not scroll-able', () => { const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) }); From 6a3ee26f1c1b47f673dcf2732897230b371b12d5 Mon Sep 17 00:00:00 2001 From: MuYu Date: Tue, 30 Sep 2025 16:00:00 +0800 Subject: [PATCH 10/10] Update tests/touch.test.js Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- tests/touch.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/touch.test.js b/tests/touch.test.js index fef06197..93412697 100644 --- a/tests/touch.test.js +++ b/tests/touch.test.js @@ -128,7 +128,7 @@ describe('List.Touch', () => { wrapper.unmount(); }); - it('origin scroll', () => { + it('should handle complex touch gestures', () => { const wrapper = genList({ itemHeight: 20, height: 100, data: genData(100) }); function getElement() {