diff --git a/src/List.tsx b/src/List.tsx index 04468b7..88c1a73 100644 --- a/src/List.tsx +++ b/src/List.tsx @@ -113,9 +113,28 @@ export function RawList(props: ListProps, ref: React.Ref) { ...restProps } = props; + // =============================== Item Key =============================== + const getKey = React.useCallback>( + (item: T) => { + if (typeof itemKey === 'function') { + return itemKey(item); + } + return item?.[itemKey as string]; + }, + [itemKey], + ); + + // ================================ Height ================================ + const [setInstanceRef, collectHeight, heights, heightUpdatedMark] = useHeights( + getKey, + null, + null, + ); + // ================================= MISC ================================= const useVirtual = !!(virtual !== false && height && itemHeight); - const inVirtual = useVirtual && data && (itemHeight * data.length > height || !!scrollWidth); + const containerHeight = React.useMemo(() => Object.values(heights.maps).reduce((total, curr) => total + curr, 0), [heights.id, heights.maps]); + const inVirtual = useVirtual && data && (Math.max(itemHeight * data.length, containerHeight) > height || !!scrollWidth); const isRTL = direction === 'rtl'; const mergedClassName = classNames(prefixCls, { [`${prefixCls}-rtl`]: isRTL }, className); @@ -136,17 +155,6 @@ export function RawList(props: ListProps, ref: React.Ref) { setScrollMoving(false); }; - // =============================== Item Key =============================== - const getKey = React.useCallback>( - (item: T) => { - if (typeof itemKey === 'function') { - return itemKey(item); - } - return item?.[itemKey as string]; - }, - [itemKey], - ); - const sharedConfig: SharedConfig = { getKey, }; @@ -176,13 +184,6 @@ export function RawList(props: ListProps, ref: React.Ref) { const [diffItem] = useDiffItem(mergedData, getKey); diffItemRef.current = diffItem; - // ================================ Height ================================ - const [setInstanceRef, collectHeight, heights, heightUpdatedMark] = useHeights( - getKey, - null, - null, - ); - // ========================== Visible Calculation ========================= const { scrollHeight, @@ -307,6 +308,8 @@ export function RawList(props: ListProps, ref: React.Ref) { const getVirtualScrollInfo = () => ({ x: isRTL ? -offsetLeft : offsetLeft, y: offsetTop, + maxScrollWidth: !!scrollWidth ? scrollWidth - size.width : 0, + maxScrollHeight: scrollHeight > height ? maxScrollHeight : 0, }); const lastVirtualScrollInfoRef = useRef(getVirtualScrollInfo()); @@ -354,7 +357,7 @@ export function RawList(props: ListProps, ref: React.Ref) { const keepInHorizontalRange = (nextOffsetLeft: number) => { let tmpOffsetLeft = nextOffsetLeft; - const max = scrollWidth - size.width; + const max = !!scrollWidth ? scrollWidth - size.width : 0; tmpOffsetLeft = Math.max(tmpOffsetLeft, 0); tmpOffsetLeft = Math.min(tmpOffsetLeft, max); diff --git a/tests/scroll.test.js b/tests/scroll.test.js index e676f02..082c846 100644 --- a/tests/scroll.test.js +++ b/tests/scroll.test.js @@ -473,4 +473,25 @@ describe('List.Scroll', () => { height: `20px`, }); }); + + it('show scrollbar when actual height is larger than container height', async () => { + jest.useRealTimers(); + const { container } = genList( + // set itemHeight * data.length < height, but sum of actual height > height + { + itemHeight: 8, + height: 100, + data: genData(10) + }, + render, + ); + + await act(async () => { + await new Promise((resolve) => { + setTimeout(resolve, 10); + }); + }); + + expect(container.querySelector('.rc-virtual-list-scrollbar-thumb')).toBeVisible() + }); }); diff --git a/tests/scrollWidth.test.tsx b/tests/scrollWidth.test.tsx index 7a5db3b..4f06693 100644 --- a/tests/scrollWidth.test.tsx +++ b/tests/scrollWidth.test.tsx @@ -132,8 +132,8 @@ describe('List.scrollWidth', () => { width: '20px', }); - expect(onVirtualScroll).toHaveBeenCalledWith({ x: 900, y: 0 }); - expect(listRef.current.getScrollInfo()).toEqual({ x: 900, y: 0 }); + expect(onVirtualScroll).toHaveBeenCalledWith({ x: 900, y: 0, maxScrollWidth: 900, maxScrollHeight: 1900 }); + expect(listRef.current.getScrollInfo()).toEqual({ x: 900, y: 0, maxScrollWidth: 900, maxScrollHeight: 1900 }); }); it('wheel', async () => { @@ -151,7 +151,7 @@ describe('List.scrollWidth', () => { fireEvent.wheel(container.querySelector('.rc-virtual-list-holder')!, { deltaX: 123, }); - expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0 }); + expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0, maxScrollWidth: 900, maxScrollHeight: 1900 }); }); it('trigger event when less count', async () => { @@ -169,7 +169,7 @@ describe('List.scrollWidth', () => { fireEvent.wheel(container.querySelector('.rc-virtual-list-holder')!, { deltaX: 123, }); - expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0 }); + expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0, maxScrollWidth: 900, maxScrollHeight: 0 }); }); it('shift wheel', async () => { @@ -188,7 +188,7 @@ describe('List.scrollWidth', () => { deltaY: 123, shiftKey: true, }); - expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0 }); + expect(onVirtualScroll).toHaveBeenCalledWith({ x: 123, y: 0, maxScrollWidth: 900, maxScrollHeight: 1900 }); }); }); @@ -204,10 +204,10 @@ describe('List.scrollWidth', () => { }); listRef.current.scrollTo({ left: 135 }); - expect(listRef.current.getScrollInfo()).toEqual({ x: 135, y: 0 }); + expect(listRef.current.getScrollInfo()).toEqual({ x: 135, y: 0, maxScrollWidth: 900, maxScrollHeight: 1900 }); listRef.current.scrollTo({ left: -99 }); - expect(listRef.current.getScrollInfo()).toEqual({ x: 0, y: 0 }); + expect(listRef.current.getScrollInfo()).toEqual({ x: 0, y: 0, maxScrollWidth: 900, maxScrollHeight: 1900 }); }); it('support extraRender', async () => {