diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 50dad2f2dab389..c0fe29861e3234 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -858,15 +858,19 @@ class VirtualizedList extends StateSafePureComponent { props: Props, ): {first: number, last: number} { const itemCount = props.getItemCount(props.data); - const last = Math.min(itemCount - 1, cells.last); + const lastPossibleCellIndex = itemCount - 1; + // Constraining `last` may significantly shrink the window. Adjust `first` + // to expand the window if the new `last` results in a new window smaller + // than the number of cells rendered per batch. const maxToRenderPerBatch = maxToRenderPerBatchOrDefault( props.maxToRenderPerBatch, ); + const maxFirst = Math.max(0, lastPossibleCellIndex - maxToRenderPerBatch); return { - first: clamp(0, itemCount - 1 - maxToRenderPerBatch, cells.first), - last, + first: clamp(0, cells.first, maxFirst), + last: Math.min(lastPossibleCellIndex, cells.last), }; } diff --git a/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js b/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js index c030e8a2d7e690..774a2473556bc1 100644 --- a/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js +++ b/packages/virtualized-lists/Lists/__tests__/VirtualizedList-test.js @@ -2224,6 +2224,53 @@ it('handles maintainVisibleContentPosition', () => { expect(component).toMatchSnapshot(); }); +it('handles maintainVisibleContentPosition when anchor moves before minIndexForVisible', () => { + const items = generateItems(20); + const ITEM_HEIGHT = 10; + + // Render a list with `minIndexForVisible: 1` + let component; + ReactTestRenderer.act(() => { + component = ReactTestRenderer.create( + , + ); + }); + + ReactTestRenderer.act(() => { + simulateLayout(component, { + viewport: {width: 10, height: 50}, + content: {width: 10, height: items.length * ITEM_HEIGHT}, + }); + + performAllBatches(); + }); + + expect(component).toMatchSnapshot(); + + // Remove the first item to shift the previous anchor to be before + // `minIndexForVisible`. + const [, ...restItems] = items; + ReactTestRenderer.act(() => { + component.update( + , + ); + }); + + expect(component).toMatchSnapshot(); +}); + function generateItems(count, startKey = 0) { return Array(count) .fill() diff --git a/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap b/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap index b877e3da84d999..5e9f7f1adaeb70 100644 --- a/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -3658,6 +3658,273 @@ exports[`handles maintainVisibleContentPosition 3`] = ` `; +exports[`handles maintainVisibleContentPosition when anchor moves before minIndexForVisible 1`] = ` + + + + + + + + + + + + + + + + + + + + +`; + +exports[`handles maintainVisibleContentPosition when anchor moves before minIndexForVisible 2`] = ` + + + + + + + + + + + + + + + + + +`; + exports[`initially renders nothing when initialNumToRender is 0 1`] = `