From c49807e86c8a068706adee4d0a226e48d95f825c Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Tue, 22 Oct 2024 10:07:38 -0700 Subject: [PATCH] Add native prop to signal reversed lists Summary: While we certainly can check the order of content container children in a scroll view to determine if a list is in reverse order, this doesn't tell us definitively whether the semantic intent of the list is reverse ordered items. Instead, we can pass the same indicator that we use to determine list cell metrics to the native component. ## Changelog [General][Added] Native prop to signal that a ScrollView has reverse ordered children Differential Revision: D64759218 --- .../ScrollView/ScrollViewNativeComponent.js | 3 +- .../__snapshots__/FlatList-test.js.snap | 9 +++ .../__snapshots__/SectionList-test.js.snap | 5 ++ .../components/scrollview/ScrollViewProps.cpp | 16 ++++- .../components/scrollview/ScrollViewProps.h | 1 + .../Lists/VirtualizedList.js | 1 + .../VirtualizedList-test.js.snap | 58 +++++++++++++++++++ .../VirtualizedSectionList-test.js.snap | 12 ++++ 8 files changed, 103 insertions(+), 2 deletions(-) diff --git a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js index 0d39831a6c5061..31518af3af8dd8 100644 --- a/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js +++ b/packages/react-native/Libraries/Components/ScrollView/ScrollViewNativeComponent.js @@ -90,7 +90,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = process: require('../../StyleSheet/processColor').default, }, pointerEvents: true, - isInvertedVirtualizedList: true, + isReversedVirtualizedList: true, }, } : { @@ -140,6 +140,7 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = disableIntervalMomentum: true, indicatorStyle: true, inverted: true, + isReversedVirtualizedList: true, keyboardDismissMode: true, maintainVisibleContentPosition: true, maximumZoomScale: true, diff --git a/packages/react-native/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap b/packages/react-native/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap index 54d4c1a96d00bd..4136973ee4cfed 100644 --- a/packages/react-native/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap +++ b/packages/react-native/Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap @@ -6,6 +6,7 @@ exports[`FlatList ignores invalid data 1`] = ` data={123456} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={null} onLayout={[Function]} @@ -87,6 +88,7 @@ exports[`FlatList renders all the bells and whistles 1`] = ` getItem={[Function]} getItemCount={[Function]} getItemLayout={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -201,6 +203,7 @@ exports[`FlatList renders array-like data 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={null} onLayout={[Function]} @@ -287,6 +290,7 @@ exports[`FlatList renders empty list 1`] = ` data={Array []} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -309,6 +313,7 @@ exports[`FlatList renders null list 1`] = ` { ? [inversionStyle, this.props.style] : this.props.style, isInvertedVirtualizedList: this.props.inverted, + isReversedVirtualizedList: this._orientation().reversed, maintainVisibleContentPosition: this.props.maintainVisibleContentPosition != null ? { 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 7790f8581bdb85..9fc6047940d6b3 100644 --- a/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap +++ b/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedList-test.js.snap @@ -45,6 +45,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when ListHeaderCom getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={10} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -202,6 +203,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when all in initia getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={10} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -353,6 +355,7 @@ exports[`VirtualizedList forwards correct stickyHeaderIndices when partially in getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={5} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -437,6 +440,7 @@ exports[`VirtualizedList handles nested lists 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -468,6 +472,7 @@ exports[`VirtualizedList handles nested lists 1`] = ` getItem={[Function]} getItemCount={[Function]} horizontal={false} + isReversedVirtualizedList={false} onLayout={[Function]} onMomentumScrollBegin={[Function]} onMomentumScrollEnd={[Function]} @@ -517,6 +522,7 @@ exports[`VirtualizedList handles nested lists 1`] = ` getItem={[Function]} getItemCount={[Function]} horizontal={true} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -586,6 +592,7 @@ exports[`VirtualizedList handles separators correctly 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -663,6 +670,7 @@ exports[`VirtualizedList handles separators correctly 2`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -740,6 +748,7 @@ exports[`VirtualizedList handles separators correctly 3`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -877,6 +886,7 @@ exports[`VirtualizedList keeps sticky headers above viewport visualized 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -1014,6 +1024,7 @@ exports[`VirtualizedList renders all the bells and whistles 1`] = ` invertStickyHeaders={true} inverted={true} isInvertedVirtualizedList={true} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -1195,6 +1206,7 @@ exports[`VirtualizedList renders empty list 1`] = ` data={Array []} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -1215,6 +1227,7 @@ exports[`VirtualizedList renders empty list after batch 1`] = ` data={Array []} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -1238,6 +1251,7 @@ exports[`VirtualizedList renders empty list with empty component 1`] = ` data={Array []} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -1278,6 +1292,7 @@ exports[`VirtualizedList renders list with empty component 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -1307,6 +1322,7 @@ exports[`VirtualizedList renders null list 1`] = ` 0 and sc getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={14} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -2040,6 +2063,7 @@ exports[`constrains batch render region when an item is removed 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -2168,6 +2192,7 @@ exports[`discards intitial render if initialScrollIndex != 0 1`] = ` getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={5} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -2310,6 +2335,7 @@ exports[`does not adjust render area until content area layed out 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={5} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -2445,6 +2471,7 @@ exports[`does not move render area when initialScrollIndex is > 0 and offset not getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -2557,6 +2584,7 @@ exports[`does not over-render when there is less than initialNumToRender cells 1 getItemLayout={[Function]} initialNumToRender={20} initialScrollIndex={4} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -2670,6 +2698,7 @@ exports[`eventually renders all items when virtualization disabled 1`] = ` getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={1} + isReversedVirtualizedList={false} maxToRenderPerBatch={10} onContentSizeChange={[Function]} onLayout={[Function]} @@ -2809,6 +2838,7 @@ exports[`expands first in viewport to render up to maxToRenderPerBatch on initia getItemLayout={[Function]} initialNumToRender={2} initialScrollIndex={4} + isReversedVirtualizedList={false} maxToRenderPerBatch={10} onContentSizeChange={[Function]} onLayout={[Function]} @@ -2926,6 +2956,7 @@ exports[`expands render area by maxToRenderPerBatch on tick 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={5} + isReversedVirtualizedList={false} maxToRenderPerBatch={2} onContentSizeChange={[Function]} onLayout={[Function]} @@ -3047,6 +3078,7 @@ exports[`gracefully handles negative initialScrollIndex 1`] = ` getItemLayout={[Function]} initialNumToRender={4} initialScrollIndex={-1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -3213,6 +3245,7 @@ exports[`handles maintainVisibleContentPosition 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maintainVisibleContentPosition={ Object { "minIndexForVisible": 0, @@ -3382,6 +3415,7 @@ exports[`handles maintainVisibleContentPosition 2`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maintainVisibleContentPosition={ Object { "minIndexForVisible": 0, @@ -3566,6 +3600,7 @@ exports[`handles maintainVisibleContentPosition 3`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maintainVisibleContentPosition={ Object { "minIndexForVisible": 0, @@ -3728,6 +3763,7 @@ exports[`handles maintainVisibleContentPosition when anchor moves before minInde getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maintainVisibleContentPosition={ Object { "minIndexForVisible": 1, @@ -3864,6 +3900,7 @@ exports[`handles maintainVisibleContentPosition when anchor moves before minInde getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maintainVisibleContentPosition={ Object { "minIndexForVisible": 1, @@ -3965,6 +4002,7 @@ exports[`initially renders nothing when initialNumToRender is 0 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={0} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -4058,6 +4096,7 @@ exports[`keeps viewport above last focused rendered 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -4256,6 +4295,7 @@ exports[`keeps viewport below last focused rendered 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -4439,6 +4479,7 @@ exports[`renders a zero-height tail spacer on initial render if getItemLayout no getItem={[Function]} getItemCount={[Function]} initialNumToRender={3} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -4528,6 +4569,7 @@ exports[`renders full tail spacer if all cells measured 1`] = ` getItem={[Function]} getItemCount={[Function]} initialNumToRender={3} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -4640,6 +4682,7 @@ exports[`renders initialNumToRender cells when virtualization disabled 1`] = ` getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -4738,6 +4781,7 @@ exports[`renders items before initialScrollIndex on first batch tick when virtua getItemLayout={[Function]} initialNumToRender={1} initialScrollIndex={5} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -4834,6 +4878,7 @@ exports[`renders new items when data is updated with non-zero initialScrollIndex getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={1} + isReversedVirtualizedList={false} maxToRenderPerBatch={10} onContentSizeChange={[Function]} onLayout={[Function]} @@ -4917,6 +4962,7 @@ exports[`renders no spacers up to initialScrollIndex on first render when virtua getItemLayout={[Function]} initialNumToRender={2} initialScrollIndex={4} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -4991,6 +5037,7 @@ exports[`renders offset cells in initial render when initialScrollIndex set 1`] getItemLayout={[Function]} initialNumToRender={4} initialScrollIndex={4} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -5092,6 +5139,7 @@ exports[`renders tail spacer up to last measured index if getItemLayout not defi getItem={[Function]} getItemCount={[Function]} initialNumToRender={3} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -5201,6 +5249,7 @@ exports[`renders tail spacer up to last measured with irregular layout when getI getItem={[Function]} getItemCount={[Function]} initialNumToRender={3} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -5302,6 +5351,7 @@ exports[`renders windowSize derived region at bottom 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -5415,6 +5465,7 @@ exports[`renders windowSize derived region at top 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -5512,6 +5563,7 @@ exports[`renders windowSize derived region in middle 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -5647,6 +5699,7 @@ exports[`renders zero-height tail spacer on batch render if cells not yet measur getItem={[Function]} getItemCount={[Function]} initialNumToRender={3} + isReversedVirtualizedList={false} maxToRenderPerBatch={1} onContentSizeChange={[Function]} onLayout={[Function]} @@ -5889,6 +5942,7 @@ exports[`retains initial render region when an item is appended 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={3} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -6007,6 +6061,7 @@ exports[`retains intitial render if initialScrollIndex == 0 1`] = ` getItemLayout={[Function]} initialNumToRender={5} initialScrollIndex={0} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -6196,6 +6251,7 @@ exports[`unmounts sticky headers moved below viewport 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -6334,6 +6390,7 @@ exports[`virtualizes away last focused index if item removed 1`] = ` getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -6524,6 +6581,7 @@ exports[`virtualizes away last focused item if focus changes to a new cell 1`] = getItemCount={[Function]} getItemLayout={[Function]} initialNumToRender={1} + isReversedVirtualizedList={false} onContentSizeChange={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} diff --git a/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedSectionList-test.js.snap b/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedSectionList-test.js.snap index 8de2c76b838233..382c29ed8d041d 100644 --- a/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedSectionList-test.js.snap +++ b/packages/virtualized-lists/Lists/__tests__/__snapshots__/VirtualizedSectionList-test.js.snap @@ -19,6 +19,7 @@ exports[`VirtualizedSectionList handles nested lists 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -61,6 +62,7 @@ exports[`VirtualizedSectionList handles nested lists 1`] = ` getItem={[Function]} getItemCount={[Function]} horizontal={false} + isReversedVirtualizedList={false} keyExtractor={[Function]} onLayout={[Function]} onMomentumScrollBegin={[Function]} @@ -126,6 +128,7 @@ exports[`VirtualizedSectionList handles nested lists 1`] = ` getItem={[Function]} getItemCount={[Function]} horizontal={true} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -229,6 +232,7 @@ exports[`VirtualizedSectionList handles separators correctly 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -363,6 +367,7 @@ exports[`VirtualizedSectionList handles separators correctly 2`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -497,6 +502,7 @@ exports[`VirtualizedSectionList handles separators correctly 3`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -632,6 +638,7 @@ exports[`VirtualizedSectionList handles separators correctly 4`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -780,6 +787,7 @@ exports[`VirtualizedSectionList renders all the bells and whistles 1`] = ` invertStickyHeaders={true} inverted={true} isInvertedVirtualizedList={true} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -995,6 +1003,7 @@ exports[`VirtualizedSectionList renders empty list 1`] = ` data={Array []} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -1019,6 +1028,7 @@ exports[`VirtualizedSectionList renders empty list with empty component 1`] = ` data={Array []} getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -1065,6 +1075,7 @@ exports[`VirtualizedSectionList renders list with empty component 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]} @@ -1123,6 +1134,7 @@ exports[`VirtualizedSectionList renders simple list 1`] = ` } getItem={[Function]} getItemCount={[Function]} + isReversedVirtualizedList={false} keyExtractor={[Function]} onContentSizeChange={[Function]} onLayout={[Function]}