diff --git a/packages/virtualized-lists/Lists/ListMetricsAggregator.js b/packages/virtualized-lists/Lists/ListMetricsAggregator.js index a7972390211f17..9516269aee024e 100644 --- a/packages/virtualized-lists/Lists/ListMetricsAggregator.js +++ b/packages/virtualized-lists/Lists/ListMetricsAggregator.js @@ -167,6 +167,30 @@ export default class ListMetricsAggregator { // check for invalid frames due to row re-ordering return frame; } else { + let offset; + + const highestMeasuredCellIndex = this.getHighestMeasuredCellIndex(); + if (highestMeasuredCellIndex < index) { + // If any of the cells before this one have been laid out already, we + // should use that information in the estimations. + // This is important because if the list has a header, the initial cell + // will have a larger offset that we should take into account here. + const highestMeasuredCellFrame = this.getCellMetrics( + highestMeasuredCellIndex, + props, + ); + if (highestMeasuredCellFrame) { + offset = + highestMeasuredCellFrame.offset + + highestMeasuredCellFrame.length + + this._averageCellLength * (index - highestMeasuredCellIndex - 1); + } + } + + if (offset == null) { + offset = this._averageCellLength * index; + } + const {data, getItemCount} = props; invariant( index >= 0 && index < getItemCount(data), @@ -174,7 +198,7 @@ export default class ListMetricsAggregator { ); return { length: this._averageCellLength, - offset: this._averageCellLength * index, + offset, index, isMounted: false, }; diff --git a/packages/virtualized-lists/Lists/__tests__/ListMetricsAggregator-test.js b/packages/virtualized-lists/Lists/__tests__/ListMetricsAggregator-test.js index 2278ca9e50cbff..7b5e416d41960e 100644 --- a/packages/virtualized-lists/Lists/__tests__/ListMetricsAggregator-test.js +++ b/packages/virtualized-lists/Lists/__tests__/ListMetricsAggregator-test.js @@ -221,7 +221,7 @@ describe('ListMetricsAggregator', () => { height: 10, width: 5, x: 0, - y: 0, + y: 100, }, }); @@ -233,7 +233,7 @@ describe('ListMetricsAggregator', () => { height: 20, width: 5, x: 0, - y: 10, + y: 110, }, }); @@ -241,7 +241,7 @@ describe('ListMetricsAggregator', () => { expect(listMetrics.getCellMetricsApprox(2, props)).toEqual({ index: 2, length: 15, - offset: 30, + offset: 130, isMounted: false, }); }); @@ -481,7 +481,7 @@ describe('ListMetricsAggregator', () => { it('estimates RTL metrics of unmeasured cell', () => { const listMetrics = new ListMetricsAggregator(); - const orientation = {horizontal: true, rtl: false}; + const orientation = {horizontal: true, rtl: true}; const props: CellMetricProps = { data: [1, 2, 3, 4, 5], getItemCount: () => nullthrows(props.data).length, @@ -500,7 +500,7 @@ describe('ListMetricsAggregator', () => { layout: { height: 5, width: 10, - x: 90, + x: 70, y: 0, }, }); @@ -512,23 +512,22 @@ describe('ListMetricsAggregator', () => { layout: { height: 5, width: 20, - x: 70, + x: 50, y: 0, }, }); - expect(listMetrics.getCellMetrics(2, props)).toBeNull(); expect(listMetrics.getCellMetricsApprox(2, props)).toEqual({ index: 2, length: 15, - offset: 30, + offset: 50, isMounted: false, }); }); it('uses getItemLayout for RTL metrics of unmeasured cell', () => { const listMetrics = new ListMetricsAggregator(); - const orientation = {horizontal: true, rtl: false}; + const orientation = {horizontal: true, rtl: true}; const props: CellMetricProps = { data: [1, 2, 3, 4, 5], getItemCount: () => nullthrows(props.data).length,