@shopify/flash-list 2.0.4-alpha.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/dist/AnimatedFlashList.js +4 -6
  2. package/dist/AnimatedFlashList.js.map +1 -1
  3. package/dist/FlashList.js +1 -5
  4. package/dist/FlashList.js.map +1 -1
  5. package/dist/FlashListProps.d.ts +41 -1
  6. package/dist/FlashListProps.d.ts.map +1 -1
  7. package/dist/FlashListProps.js +1 -4
  8. package/dist/FlashListProps.js.map +1 -1
  9. package/dist/FlashListRef.js +1 -2
  10. package/dist/benchmark/AutoScrollHelper.js +22 -30
  11. package/dist/benchmark/AutoScrollHelper.js.map +1 -1
  12. package/dist/benchmark/JSFPSMonitor.js +27 -33
  13. package/dist/benchmark/JSFPSMonitor.js.map +1 -1
  14. package/dist/benchmark/roundToDecimalPlaces.js +2 -5
  15. package/dist/benchmark/roundToDecimalPlaces.js.map +1 -1
  16. package/dist/benchmark/useBenchmark.d.ts +9 -1
  17. package/dist/benchmark/useBenchmark.d.ts.map +1 -1
  18. package/dist/benchmark/useBenchmark.js +86 -95
  19. package/dist/benchmark/useBenchmark.js.map +1 -1
  20. package/dist/benchmark/useDataMultiplier.js +6 -10
  21. package/dist/benchmark/useDataMultiplier.js.map +1 -1
  22. package/dist/benchmark/useFlatListBenchmark.d.ts +4 -1
  23. package/dist/benchmark/useFlatListBenchmark.d.ts.map +1 -1
  24. package/dist/benchmark/useFlatListBenchmark.js +73 -81
  25. package/dist/benchmark/useFlatListBenchmark.js.map +1 -1
  26. package/dist/errors/ErrorMessages.js +1 -4
  27. package/dist/errors/ErrorMessages.js.map +1 -1
  28. package/dist/errors/WarningMessages.js +1 -4
  29. package/dist/errors/WarningMessages.js.map +1 -1
  30. package/dist/index.js +17 -35
  31. package/dist/index.js.map +1 -1
  32. package/dist/isNewArch.js +6 -9
  33. package/dist/isNewArch.js.map +1 -1
  34. package/dist/native/config/PlatformHelper.android.js +2 -5
  35. package/dist/native/config/PlatformHelper.android.js.map +1 -1
  36. package/dist/native/config/PlatformHelper.ios.js +2 -5
  37. package/dist/native/config/PlatformHelper.ios.js.map +1 -1
  38. package/dist/native/config/PlatformHelper.js +2 -5
  39. package/dist/native/config/PlatformHelper.js.map +1 -1
  40. package/dist/native/config/PlatformHelper.web.js +2 -5
  41. package/dist/native/config/PlatformHelper.web.js.map +1 -1
  42. package/dist/recyclerview/LayoutCommitObserver.js +20 -24
  43. package/dist/recyclerview/LayoutCommitObserver.js.map +1 -1
  44. package/dist/recyclerview/RecyclerView.d.ts.map +1 -1
  45. package/dist/recyclerview/RecyclerView.js +134 -111
  46. package/dist/recyclerview/RecyclerView.js.map +1 -1
  47. package/dist/recyclerview/RecyclerViewContextProvider.js +7 -12
  48. package/dist/recyclerview/RecyclerViewContextProvider.js.map +1 -1
  49. package/dist/recyclerview/RecyclerViewManager.js +138 -167
  50. package/dist/recyclerview/RecyclerViewManager.js.map +1 -1
  51. package/dist/recyclerview/RecyclerViewProps.js +1 -2
  52. package/dist/recyclerview/RenderStackManager.js +97 -188
  53. package/dist/recyclerview/RenderStackManager.js.map +1 -1
  54. package/dist/recyclerview/ViewHolder.d.ts +2 -0
  55. package/dist/recyclerview/ViewHolder.d.ts.map +1 -1
  56. package/dist/recyclerview/ViewHolder.js +19 -21
  57. package/dist/recyclerview/ViewHolder.js.map +1 -1
  58. package/dist/recyclerview/ViewHolderCollection.d.ts +4 -0
  59. package/dist/recyclerview/ViewHolderCollection.d.ts.map +1 -1
  60. package/dist/recyclerview/ViewHolderCollection.js +26 -30
  61. package/dist/recyclerview/ViewHolderCollection.js.map +1 -1
  62. package/dist/recyclerview/components/CompatScroller.js +6 -7
  63. package/dist/recyclerview/components/CompatScroller.js.map +1 -1
  64. package/dist/recyclerview/components/CompatView.js +6 -7
  65. package/dist/recyclerview/components/CompatView.js.map +1 -1
  66. package/dist/recyclerview/components/ScrollAnchor.js +10 -15
  67. package/dist/recyclerview/components/ScrollAnchor.js.map +1 -1
  68. package/dist/recyclerview/components/StickyHeaders.d.ts +5 -1
  69. package/dist/recyclerview/components/StickyHeaders.d.ts.map +1 -1
  70. package/dist/recyclerview/components/StickyHeaders.js +77 -51
  71. package/dist/recyclerview/components/StickyHeaders.js.map +1 -1
  72. package/dist/recyclerview/helpers/ConsecutiveNumbers.js +39 -66
  73. package/dist/recyclerview/helpers/ConsecutiveNumbers.js.map +1 -1
  74. package/dist/recyclerview/helpers/EngagedIndicesTracker.js +57 -63
  75. package/dist/recyclerview/helpers/EngagedIndicesTracker.js.map +1 -1
  76. package/dist/recyclerview/helpers/RenderTimeTracker.js +19 -24
  77. package/dist/recyclerview/helpers/RenderTimeTracker.js.map +1 -1
  78. package/dist/recyclerview/helpers/VelocityTracker.js +16 -22
  79. package/dist/recyclerview/helpers/VelocityTracker.js.map +1 -1
  80. package/dist/recyclerview/hooks/useBoundDetection.js +37 -40
  81. package/dist/recyclerview/hooks/useBoundDetection.js.map +1 -1
  82. package/dist/recyclerview/hooks/useLayoutState.js +9 -15
  83. package/dist/recyclerview/hooks/useLayoutState.js.map +1 -1
  84. package/dist/recyclerview/hooks/useMappingHelper.js +6 -10
  85. package/dist/recyclerview/hooks/useMappingHelper.js.map +1 -1
  86. package/dist/recyclerview/hooks/useOnLoad.js +16 -22
  87. package/dist/recyclerview/hooks/useOnLoad.js.map +1 -1
  88. package/dist/recyclerview/hooks/useRecyclerViewController.d.ts.map +1 -1
  89. package/dist/recyclerview/hooks/useRecyclerViewController.js +169 -188
  90. package/dist/recyclerview/hooks/useRecyclerViewController.js.map +1 -1
  91. package/dist/recyclerview/hooks/useRecyclerViewManager.js +12 -17
  92. package/dist/recyclerview/hooks/useRecyclerViewManager.js.map +1 -1
  93. package/dist/recyclerview/hooks/useRecyclingState.js +10 -14
  94. package/dist/recyclerview/hooks/useRecyclingState.js.map +1 -1
  95. package/dist/recyclerview/hooks/useSecondaryProps.d.ts +2 -0
  96. package/dist/recyclerview/hooks/useSecondaryProps.d.ts.map +1 -1
  97. package/dist/recyclerview/hooks/useSecondaryProps.js +39 -30
  98. package/dist/recyclerview/hooks/useSecondaryProps.js.map +1 -1
  99. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js +17 -22
  100. package/dist/recyclerview/hooks/useUnmountAwareCallbacks.js.map +1 -1
  101. package/dist/recyclerview/hooks/useUnmountFlag.js +5 -9
  102. package/dist/recyclerview/hooks/useUnmountFlag.js.map +1 -1
  103. package/dist/recyclerview/layout-managers/GridLayoutManager.js +61 -80
  104. package/dist/recyclerview/layout-managers/GridLayoutManager.js.map +1 -1
  105. package/dist/recyclerview/layout-managers/LayoutManager.js +83 -123
  106. package/dist/recyclerview/layout-managers/LayoutManager.js.map +1 -1
  107. package/dist/recyclerview/layout-managers/LinearLayoutManager.js +51 -91
  108. package/dist/recyclerview/layout-managers/LinearLayoutManager.js.map +1 -1
  109. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js +77 -96
  110. package/dist/recyclerview/layout-managers/MasonryLayoutManager.js.map +1 -1
  111. package/dist/recyclerview/utils/adjustOffsetForRTL.js +1 -4
  112. package/dist/recyclerview/utils/adjustOffsetForRTL.js.map +1 -1
  113. package/dist/recyclerview/utils/componentUtils.js +4 -9
  114. package/dist/recyclerview/utils/componentUtils.js.map +1 -1
  115. package/dist/recyclerview/utils/findVisibleIndex.js +9 -13
  116. package/dist/recyclerview/utils/findVisibleIndex.js.map +1 -1
  117. package/dist/recyclerview/utils/measureLayout.js +12 -20
  118. package/dist/recyclerview/utils/measureLayout.js.map +1 -1
  119. package/dist/recyclerview/utils/measureLayout.web.js +15 -23
  120. package/dist/recyclerview/utils/measureLayout.web.js.map +1 -1
  121. package/dist/recyclerview/viewability/ViewToken.js +1 -2
  122. package/dist/recyclerview/viewability/ViewabilityHelper.js +34 -41
  123. package/dist/recyclerview/viewability/ViewabilityHelper.js.map +1 -1
  124. package/dist/recyclerview/viewability/ViewabilityManager.js +48 -61
  125. package/dist/recyclerview/viewability/ViewabilityManager.js.map +1 -1
  126. package/dist/tsconfig.tsbuildinfo +1 -1
  127. package/dist/utils/AverageWindow.js +28 -39
  128. package/dist/utils/AverageWindow.js.map +1 -1
  129. package/package.json +4 -6
  130. package/src/FlashListProps.ts +51 -1
  131. package/src/benchmark/useBenchmark.ts +47 -4
  132. package/src/benchmark/useFlatListBenchmark.ts +38 -5
  133. package/src/recyclerview/RecyclerView.tsx +42 -8
  134. package/src/recyclerview/ViewHolder.tsx +6 -1
  135. package/src/recyclerview/ViewHolderCollection.tsx +10 -0
  136. package/src/recyclerview/components/StickyHeaders.tsx +54 -13
  137. package/src/recyclerview/hooks/useRecyclerViewController.tsx +7 -4
  138. package/src/recyclerview/hooks/useSecondaryProps.tsx +23 -0
  139. package/dist/__tests__/AverageWindow.test.d.ts +0 -2
  140. package/dist/__tests__/AverageWindow.test.d.ts.map +0 -1
  141. package/dist/__tests__/AverageWindow.test.js +0 -104
  142. package/dist/__tests__/AverageWindow.test.js.map +0 -1
  143. package/dist/__tests__/ConsecutiveNumbers.test.d.ts +0 -2
  144. package/dist/__tests__/ConsecutiveNumbers.test.d.ts.map +0 -1
  145. package/dist/__tests__/ConsecutiveNumbers.test.js +0 -224
  146. package/dist/__tests__/ConsecutiveNumbers.test.js.map +0 -1
  147. package/dist/__tests__/GridLayoutManager.test.d.ts +0 -2
  148. package/dist/__tests__/GridLayoutManager.test.d.ts.map +0 -1
  149. package/dist/__tests__/GridLayoutManager.test.js +0 -69
  150. package/dist/__tests__/GridLayoutManager.test.js.map +0 -1
  151. package/dist/__tests__/LayoutCommitObserver.test.d.ts +0 -2
  152. package/dist/__tests__/LayoutCommitObserver.test.d.ts.map +0 -1
  153. package/dist/__tests__/LayoutCommitObserver.test.js +0 -37
  154. package/dist/__tests__/LayoutCommitObserver.test.js.map +0 -1
  155. package/dist/__tests__/LinearLayoutManager.test.d.ts +0 -2
  156. package/dist/__tests__/LinearLayoutManager.test.d.ts.map +0 -1
  157. package/dist/__tests__/LinearLayoutManager.test.js +0 -140
  158. package/dist/__tests__/LinearLayoutManager.test.js.map +0 -1
  159. package/dist/__tests__/MasonryLayoutManager.test.d.ts +0 -2
  160. package/dist/__tests__/MasonryLayoutManager.test.d.ts.map +0 -1
  161. package/dist/__tests__/MasonryLayoutManager.test.js +0 -148
  162. package/dist/__tests__/MasonryLayoutManager.test.js.map +0 -1
  163. package/dist/__tests__/RecyclerView.test.d.ts +0 -2
  164. package/dist/__tests__/RecyclerView.test.d.ts.map +0 -1
  165. package/dist/__tests__/RecyclerView.test.js +0 -103
  166. package/dist/__tests__/RecyclerView.test.js.map +0 -1
  167. package/dist/__tests__/RecyclerViewManager.test.d.ts +0 -2
  168. package/dist/__tests__/RecyclerViewManager.test.d.ts.map +0 -1
  169. package/dist/__tests__/RecyclerViewManager.test.js +0 -56
  170. package/dist/__tests__/RecyclerViewManager.test.js.map +0 -1
  171. package/dist/__tests__/RenderStackManager.test.d.ts +0 -2
  172. package/dist/__tests__/RenderStackManager.test.d.ts.map +0 -1
  173. package/dist/__tests__/RenderStackManager.test.js +0 -485
  174. package/dist/__tests__/RenderStackManager.test.js.map +0 -1
  175. package/dist/__tests__/ViewabilityHelper.test.d.ts +0 -2
  176. package/dist/__tests__/ViewabilityHelper.test.d.ts.map +0 -1
  177. package/dist/__tests__/ViewabilityHelper.test.js +0 -186
  178. package/dist/__tests__/ViewabilityHelper.test.js.map +0 -1
  179. package/dist/__tests__/findVisibleIndex.test.d.ts +0 -2
  180. package/dist/__tests__/findVisibleIndex.test.d.ts.map +0 -1
  181. package/dist/__tests__/findVisibleIndex.test.js +0 -259
  182. package/dist/__tests__/findVisibleIndex.test.js.map +0 -1
  183. package/dist/__tests__/helpers/createLayoutManager.d.ts +0 -34
  184. package/dist/__tests__/helpers/createLayoutManager.d.ts.map +0 -1
  185. package/dist/__tests__/helpers/createLayoutManager.js +0 -110
  186. package/dist/__tests__/helpers/createLayoutManager.js.map +0 -1
  187. package/dist/__tests__/useUnmountAwareCallbacks.test.d.ts +0 -2
  188. package/dist/__tests__/useUnmountAwareCallbacks.test.d.ts.map +0 -1
  189. package/dist/__tests__/useUnmountAwareCallbacks.test.js +0 -185
  190. package/dist/__tests__/useUnmountAwareCallbacks.test.js.map +0 -1
  191. package/src/__tests__/AverageWindow.test.ts +0 -128
  192. package/src/__tests__/ConsecutiveNumbers.test.ts +0 -232
  193. package/src/__tests__/GridLayoutManager.test.ts +0 -113
  194. package/src/__tests__/LayoutCommitObserver.test.tsx +0 -63
  195. package/src/__tests__/LinearLayoutManager.test.ts +0 -227
  196. package/src/__tests__/MasonryLayoutManager.test.ts +0 -202
  197. package/src/__tests__/RecyclerView.test.tsx +0 -144
  198. package/src/__tests__/RecyclerViewManager.test.ts +0 -74
  199. package/src/__tests__/RenderStackManager.test.ts +0 -574
  200. package/src/__tests__/ViewabilityHelper.test.ts +0 -282
  201. package/src/__tests__/findVisibleIndex.test.ts +0 -369
  202. package/src/__tests__/helpers/createLayoutManager.ts +0 -141
  203. package/src/__tests__/useUnmountAwareCallbacks.test.tsx +0 -285
@@ -1,282 +0,0 @@
1
- import ViewabilityHelper from "../recyclerview/viewability/ViewabilityHelper";
2
- import { ErrorMessages } from "../errors/ErrorMessages";
3
- import {
4
- RVDimension,
5
- RVLayout,
6
- } from "../recyclerview/layout-managers/LayoutManager";
7
-
8
- describe("ViewabilityHelper", () => {
9
- const viewableIndicesChanged = jest.fn();
10
- beforeEach(() => {
11
- jest.resetAllMocks();
12
- jest.useFakeTimers();
13
- });
14
-
15
- it("does not report any changes when indices have not changed", () => {
16
- const viewabilityHelper = new ViewabilityHelper(
17
- null,
18
- viewableIndicesChanged
19
- );
20
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2];
21
- updateViewableItems({ viewabilityHelper });
22
- // Initial call
23
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
24
- [0, 1, 2],
25
- [0, 1, 2],
26
- []
27
- );
28
-
29
- // No changes
30
- viewableIndicesChanged.mockReset();
31
- updateViewableItems({ viewabilityHelper });
32
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
33
- });
34
-
35
- it("reports only viewable indices", () => {
36
- const viewabilityHelper = new ViewabilityHelper(
37
- null,
38
- viewableIndicesChanged
39
- );
40
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
41
- updateViewableItems({ viewabilityHelper });
42
- // Items 0, 1, 2 are initially viewable
43
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
44
- [0, 1, 2],
45
- [0, 1, 2],
46
- []
47
- );
48
-
49
- // After scroll, item 3 becomes viewable, too
50
- updateViewableItems({ viewabilityHelper, scrollOffset: 50 });
51
- expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
52
-
53
- // After additional scroll, the first item is no longer viewable
54
- updateViewableItems({ viewabilityHelper, scrollOffset: 100 });
55
- expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
56
- });
57
-
58
- it("reports only viewable indices when horizontal", () => {
59
- const viewabilityHelper = new ViewabilityHelper(
60
- null,
61
- viewableIndicesChanged
62
- );
63
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
64
- const getLayout = (index: number) => {
65
- return { x: index * 100, y: 0, height: 300, width: 100 } as RVLayout;
66
- };
67
- updateViewableItems({ viewabilityHelper, horizontal: true, getLayout });
68
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
69
- [0, 1, 2],
70
- [0, 1, 2],
71
- []
72
- );
73
-
74
- updateViewableItems({
75
- viewabilityHelper,
76
- horizontal: true,
77
- scrollOffset: 50,
78
- getLayout,
79
- });
80
- expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
81
-
82
- updateViewableItems({
83
- viewabilityHelper,
84
- horizontal: true,
85
- scrollOffset: 100,
86
- getLayout,
87
- });
88
- expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
89
- });
90
-
91
- it("reports items only after minimumViewTime has elapsed", () => {
92
- const viewabilityHelper = new ViewabilityHelper(
93
- { minimumViewTime: 500 },
94
- viewableIndicesChanged
95
- );
96
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
97
- updateViewableItems({ viewabilityHelper, runAllTimers: false });
98
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
99
- jest.advanceTimersByTime(400);
100
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
101
- jest.advanceTimersByTime(100);
102
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
103
- [0, 1, 2],
104
- [0, 1, 2],
105
- []
106
- );
107
-
108
- viewableIndicesChanged.mockReset();
109
- updateViewableItems({
110
- viewabilityHelper,
111
- scrollOffset: 50,
112
- runAllTimers: false,
113
- });
114
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
115
- jest.advanceTimersByTime(500);
116
- expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
117
-
118
- viewableIndicesChanged.mockReset();
119
- updateViewableItems({
120
- viewabilityHelper,
121
- scrollOffset: 100,
122
- runAllTimers: false,
123
- });
124
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
125
- jest.advanceTimersByTime(500);
126
- expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
127
- });
128
-
129
- it("reports items that only satisfy itemVisiblePercentThreshold", () => {
130
- const viewabilityHelper = new ViewabilityHelper(
131
- { itemVisiblePercentThreshold: 50 },
132
- viewableIndicesChanged
133
- );
134
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
135
- updateViewableItems({
136
- viewabilityHelper,
137
- });
138
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
139
- [0, 1, 2],
140
- [0, 1, 2],
141
- []
142
- );
143
- viewableIndicesChanged.mockReset();
144
-
145
- // User scrolled by 50 pixels, making both first and last item visible from 50 %
146
- updateViewableItems({
147
- viewabilityHelper,
148
- scrollOffset: 50,
149
- });
150
- expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
151
- viewableIndicesChanged.mockReset();
152
-
153
- // User scrolled by 55 pixels, first item no longer satisfies threshold
154
- updateViewableItems({
155
- viewabilityHelper,
156
- scrollOffset: 55,
157
- });
158
- expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
159
- });
160
-
161
- it("reports items that only satisfy viewAreaCoveragePercentThreshold", () => {
162
- const getLayout = (index: number) => {
163
- if (index === 4) {
164
- return { x: 0, y: index * 100, width: 100, height: 25 } as RVLayout;
165
- }
166
- return { x: 0, y: index * 100, height: 100, width: 300 } as RVLayout;
167
- };
168
- const viewabilityHelper = new ViewabilityHelper(
169
- { viewAreaCoveragePercentThreshold: 25 },
170
- viewableIndicesChanged
171
- );
172
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
173
- updateViewableItems({
174
- viewabilityHelper,
175
- getLayout,
176
- });
177
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
178
- [0, 1, 2],
179
- [0, 1, 2],
180
- []
181
- );
182
- viewableIndicesChanged.mockReset();
183
-
184
- // User scrolled by 75 pixels.
185
- // First item is visible only from 25 pixels, not meeting the threshold.
186
- // The last item is visible from 75 pixels, which is exactly the threshold (300 / 4 = 75 where 300 is height of the list)
187
- updateViewableItems({
188
- viewabilityHelper,
189
- scrollOffset: 75,
190
- getLayout,
191
- });
192
- expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [3], [0]);
193
- viewableIndicesChanged.mockReset();
194
-
195
- // User scrolled by 110 pixels, making the last small item only partially visible, not meeting the threshold.
196
- viewabilityHelper.possiblyViewableIndices = [1, 2, 3, 4];
197
- updateViewableItems({
198
- viewabilityHelper,
199
- scrollOffset: 110,
200
- getLayout,
201
- });
202
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
203
-
204
- // User scrolled by 125 pixels, making the last small item completely visible, even when it is not meeting the threshold.
205
- viewabilityHelper.possiblyViewableIndices = [1, 2, 3, 4];
206
- updateViewableItems({
207
- viewabilityHelper,
208
- scrollOffset: 125,
209
- getLayout,
210
- });
211
- expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3, 4], [4], []);
212
- });
213
-
214
- it("reports viewable items only after interaction if waitForInteraction is set to true", () => {
215
- const viewabilityHelper = new ViewabilityHelper(
216
- { waitForInteraction: true },
217
- viewableIndicesChanged
218
- );
219
- // Even when elements are visible, viewableIndicesChanged will not be called since interaction has not been recorded, yet
220
- viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
221
- updateViewableItems({
222
- viewabilityHelper,
223
- });
224
- // View is scrolled but programatically - not resulting in an interaction
225
- updateViewableItems({
226
- viewabilityHelper,
227
- scrollOffset: 50,
228
- });
229
- expect(viewableIndicesChanged).not.toHaveBeenCalled();
230
-
231
- // Interaction is recorded, leading to trigger of viewableIndicesChanged
232
- viewabilityHelper.hasInteracted = true;
233
- updateViewableItems({
234
- viewabilityHelper,
235
- scrollOffset: 50,
236
- });
237
- expect(viewableIndicesChanged).toHaveBeenCalledWith(
238
- [0, 1, 2, 3],
239
- [0, 1, 2, 3],
240
- []
241
- );
242
- });
243
-
244
- it("throws multipleViewabilityThresholdTypesNotSupported exception when both viewAreaCoveragePercentThreshold and itemVisiblePercentThreshold are defined", () => {
245
- const viewabilityHelper = new ViewabilityHelper(
246
- { viewAreaCoveragePercentThreshold: 1, itemVisiblePercentThreshold: 1 },
247
- viewableIndicesChanged
248
- );
249
- expect(() => updateViewableItems({ viewabilityHelper })).toThrow(
250
- ErrorMessages.multipleViewabilityThresholdTypesNotSupported
251
- );
252
- });
253
-
254
- const updateViewableItems = ({
255
- viewabilityHelper,
256
- horizontal,
257
- scrollOffset,
258
- listSize,
259
- getLayout,
260
- runAllTimers,
261
- }: {
262
- viewabilityHelper: ViewabilityHelper;
263
- horizontal?: boolean;
264
- scrollOffset?: number;
265
- listSize?: RVDimension;
266
- getLayout?: (index: number) => RVLayout | undefined;
267
- runAllTimers?: boolean;
268
- }) => {
269
- viewabilityHelper.updateViewableItems(
270
- horizontal ?? false,
271
- scrollOffset ?? 0,
272
- listSize ?? { height: 300, width: 300 },
273
- getLayout ??
274
- ((index) => {
275
- return { x: 0, y: index * 100, height: 100, width: 300 } as RVLayout;
276
- })
277
- );
278
- if (runAllTimers ?? true) {
279
- jest.runAllTimers();
280
- }
281
- };
282
- });
@@ -1,369 +0,0 @@
1
- import {
2
- findFirstVisibleIndex,
3
- findLastVisibleIndex,
4
- } from "../recyclerview/utils/findVisibleIndex";
5
- import { RVLayout } from "../recyclerview/layout-managers/LayoutManager";
6
-
7
- import {
8
- createPopulatedLayoutManager,
9
- LayoutManagerType,
10
- getAllLayouts,
11
- } from "./helpers/createLayoutManager";
12
-
13
- describe("findVisibleIndex", () => {
14
- // Helper function to create mock layouts directly for precise control
15
- function createMockLayouts(
16
- count: number,
17
- startPosition: number,
18
- itemSize: number,
19
- isHorizontal: boolean
20
- ): RVLayout[] {
21
- const layouts: RVLayout[] = [];
22
- for (let i = 0; i < count; i++) {
23
- const x = isHorizontal ? startPosition + i * itemSize : 0;
24
- const y = isHorizontal ? 0 : startPosition + i * itemSize;
25
- layouts.push({
26
- x,
27
- y,
28
- width: isHorizontal ? itemSize : 100,
29
- height: isHorizontal ? 100 : itemSize,
30
- });
31
- }
32
- return layouts;
33
- }
34
-
35
- describe("findFirstVisibleIndex", () => {
36
- // Test 1: Basic functionality - vertical layout
37
- it("finds the first visible index in a vertical layout", () => {
38
- const layoutManager = createPopulatedLayoutManager(
39
- LayoutManagerType.LINEAR,
40
- 20,
41
- { horizontal: false }
42
- );
43
- const layouts = getAllLayouts(layoutManager);
44
-
45
- // Viewport starts at y=150, so the second item (index 1) should be first visible
46
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, false);
47
- expect(firstVisibleIndex).toBe(1);
48
- });
49
-
50
- // Test 2: Basic functionality - horizontal layout
51
- it("finds the first visible index in a horizontal layout", () => {
52
- const layoutManager = createPopulatedLayoutManager(
53
- LayoutManagerType.LINEAR,
54
- 20,
55
- { horizontal: true }
56
- );
57
- const layouts = getAllLayouts(layoutManager);
58
-
59
- // Viewport starts at x=150, so the second item (index 1) should be first visible
60
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, true);
61
- expect(firstVisibleIndex).toBe(1);
62
- });
63
-
64
- // Test 3: Empty layouts array
65
- it("returns -1 for empty layouts array", () => {
66
- const firstVisibleIndex = findFirstVisibleIndex([], 100, false);
67
- expect(firstVisibleIndex).toBe(-1);
68
- });
69
-
70
- // Test 4: All items are visible (threshold at 0)
71
- it("returns 0 when all items are visible (threshold at 0)", () => {
72
- const layoutManager = createPopulatedLayoutManager(
73
- LayoutManagerType.LINEAR,
74
- 10
75
- );
76
- const layouts = getAllLayouts(layoutManager);
77
-
78
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 0, false);
79
- expect(firstVisibleIndex).toBe(0);
80
- });
81
-
82
- // Test 5: No items are visible (threshold beyond all items)
83
- it("returns -1 when no items are visible", () => {
84
- const layouts = createMockLayouts(10, 0, 100, false);
85
-
86
- // Threshold is beyond all items (10 items * 100 height = 1000)
87
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 1100, false);
88
- expect(firstVisibleIndex).toBe(-1);
89
- });
90
-
91
- // Test 6: Edge case - threshold exactly at item boundary
92
- it("returns correct index when threshold is exactly at item boundary", () => {
93
- const layouts = createMockLayouts(10, 0, 100, false);
94
-
95
- // Threshold exactly at the start of the 5th item
96
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 400, false);
97
- expect(firstVisibleIndex).toBe(4);
98
- });
99
-
100
- // Test 7: Edge case - threshold in the middle of an item
101
- it("returns correct index when threshold is in the middle of an item", () => {
102
- const layouts = createMockLayouts(10, 0, 100, false);
103
-
104
- // Threshold in the middle of the 3rd item
105
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 250, false);
106
- expect(firstVisibleIndex).toBe(2);
107
- });
108
-
109
- // Test 8: With grid layout - threshold crosses multiple columns
110
- it("finds first visible index with grid layout", () => {
111
- const layoutManager = createPopulatedLayoutManager(
112
- LayoutManagerType.GRID,
113
- 20,
114
- { maxColumns: 2 }
115
- );
116
- const layouts = getAllLayouts(layoutManager);
117
-
118
- // With 2 columns, items are positioned differently
119
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, false);
120
-
121
- // Expected result depends on how grid layout positions items
122
- // This test might need adjustment based on actual grid layout behavior
123
- expect(firstVisibleIndex).not.toBe(-1);
124
- });
125
-
126
- // Test 9: With masonry layout - variable height items
127
- it("finds first visible index with masonry layout and variable item sizes", () => {
128
- const layoutManager = createPopulatedLayoutManager(
129
- LayoutManagerType.MASONRY,
130
- 20,
131
- { maxColumns: 2 },
132
- 100,
133
- 100,
134
- true // Variable size
135
- );
136
- const layouts = getAllLayouts(layoutManager);
137
-
138
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 200, false);
139
- expect(firstVisibleIndex).not.toBe(-1);
140
- });
141
-
142
- // Test 10: Partial visibility - item just starting to appear
143
- it("finds item that is just starting to become visible", () => {
144
- const layouts = createMockLayouts(10, 0, 100, false);
145
-
146
- // Threshold just 1px before item 4 ends
147
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 399, false);
148
- expect(firstVisibleIndex).toBe(3);
149
- });
150
- });
151
-
152
- describe("findLastVisibleIndex", () => {
153
- // Test 11: Basic functionality - vertical layout
154
- it("finds the last visible index in a vertical layout", () => {
155
- const layoutManager = createPopulatedLayoutManager(
156
- LayoutManagerType.LINEAR,
157
- 20,
158
- { horizontal: false }
159
- );
160
- const layouts = getAllLayouts(layoutManager);
161
-
162
- // Viewport ends at y=250, so the third item (index 2) should be last visible
163
- const lastVisibleIndex = findLastVisibleIndex(layouts, 250, false);
164
- expect(lastVisibleIndex).toBe(2);
165
- });
166
-
167
- // Test 12: Basic functionality - horizontal layout
168
- it("finds the last visible index in a horizontal layout", () => {
169
- const layoutManager = createPopulatedLayoutManager(
170
- LayoutManagerType.LINEAR,
171
- 20,
172
- { horizontal: true }
173
- );
174
- const layouts = getAllLayouts(layoutManager);
175
-
176
- // Viewport ends at x=250, so the third item (index 2) should be last visible
177
- const lastVisibleIndex = findLastVisibleIndex(layouts, 250, true);
178
- expect(lastVisibleIndex).toBe(2);
179
- });
180
-
181
- // Test 13: Empty layouts array
182
- it("returns -1 for empty layouts array", () => {
183
- const lastVisibleIndex = findLastVisibleIndex([], 100, false);
184
- expect(lastVisibleIndex).toBe(-1);
185
- });
186
-
187
- // Test 14: All items are within viewport
188
- it("returns the last item index when all items are within viewport", () => {
189
- const layouts = createMockLayouts(5, 0, 100, false);
190
-
191
- // Viewport ends at y=1000, which includes all 5 items
192
- const lastVisibleIndex = findLastVisibleIndex(layouts, 1000, false);
193
- expect(lastVisibleIndex).toBe(4); // Last item index is 4
194
- });
195
-
196
- // Test 15: No items are visible (threshold before all items)
197
- it("returns -1 when no items are visible", () => {
198
- const layouts = createMockLayouts(10, 100, 100, false);
199
-
200
- // Threshold is before all items start
201
- const lastVisibleIndex = findLastVisibleIndex(layouts, 50, false);
202
- expect(lastVisibleIndex).toBe(-1);
203
- });
204
-
205
- // Test 16: Edge case - threshold exactly at item boundary
206
- it("returns correct index when threshold is exactly at item boundary", () => {
207
- const layouts = createMockLayouts(10, 0, 100, false);
208
-
209
- // Threshold exactly at the end of the 3rd item
210
- const lastVisibleIndex = findLastVisibleIndex(layouts, 300, false);
211
- expect(lastVisibleIndex).toBe(3);
212
- });
213
-
214
- // Test 17: Edge case - threshold in the middle of an item
215
- it("returns correct index when threshold is in the middle of an item", () => {
216
- const layouts = createMockLayouts(10, 0, 100, false);
217
-
218
- // Threshold in the middle of the 3rd item
219
- const lastVisibleIndex = findLastVisibleIndex(layouts, 250, false);
220
- expect(lastVisibleIndex).toBe(2);
221
- });
222
-
223
- // Test 18: With grid layout
224
- it("finds last visible index with grid layout", () => {
225
- const layoutManager = createPopulatedLayoutManager(
226
- LayoutManagerType.GRID,
227
- 20,
228
- { maxColumns: 2 }
229
- );
230
- const layouts = getAllLayouts(layoutManager);
231
-
232
- const lastVisibleIndex = findLastVisibleIndex(layouts, 350, false);
233
- expect(lastVisibleIndex).not.toBe(-1);
234
- });
235
-
236
- // Test 19: With masonry layout - variable height items
237
- it("finds last visible index with masonry layout and variable item sizes", () => {
238
- const layoutManager = createPopulatedLayoutManager(
239
- LayoutManagerType.MASONRY,
240
- 20,
241
- { maxColumns: 2 },
242
- 100,
243
- 100,
244
- true // Variable size
245
- );
246
- const layouts = getAllLayouts(layoutManager);
247
-
248
- const lastVisibleIndex = findLastVisibleIndex(layouts, 400, false);
249
- expect(lastVisibleIndex).not.toBe(-1);
250
- });
251
-
252
- // Test 20: Last item partially visible
253
- it("includes last item when it's partially visible", () => {
254
- const layouts = createMockLayouts(10, 0, 100, false);
255
-
256
- // Threshold just 1px into the 5th item
257
- const lastVisibleIndex = findLastVisibleIndex(layouts, 401, false);
258
- expect(lastVisibleIndex).toBe(4);
259
- });
260
- });
261
-
262
- describe("Edge cases and complex scenarios", () => {
263
- // Test 21: Single item layout
264
- it("correctly handles single item layout for first visible", () => {
265
- const layouts = createMockLayouts(1, 0, 100, false);
266
-
267
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 50, false);
268
- expect(firstVisibleIndex).toBe(0);
269
- });
270
-
271
- // Test 22: Single item layout
272
- it("correctly handles single item layout for last visible", () => {
273
- const layouts = createMockLayouts(1, 0, 100, false);
274
-
275
- const lastVisibleIndex = findLastVisibleIndex(layouts, 50, false);
276
- expect(lastVisibleIndex).toBe(0);
277
- });
278
-
279
- // Test 23: Variable size items for first visible index
280
- it("correctly finds first visible with variable size items", () => {
281
- const layouts: RVLayout[] = [
282
- { x: 0, y: 0, width: 100, height: 50 },
283
- { x: 0, y: 50, width: 100, height: 150 },
284
- { x: 0, y: 200, width: 100, height: 75 },
285
- { x: 0, y: 275, width: 100, height: 100 },
286
- ];
287
-
288
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 175, false);
289
- expect(firstVisibleIndex).toBe(1); // Second item is still visible at threshold 175
290
- });
291
-
292
- // Test 24: Variable size items for last visible index
293
- it("correctly finds last visible with variable size items", () => {
294
- const layouts: RVLayout[] = [
295
- { x: 0, y: 0, width: 100, height: 50 },
296
- { x: 0, y: 50, width: 100, height: 150 },
297
- { x: 0, y: 200, width: 100, height: 75 },
298
- { x: 0, y: 275, width: 100, height: 100 },
299
- ];
300
-
301
- const lastVisibleIndex = findLastVisibleIndex(layouts, 225, false);
302
- expect(lastVisibleIndex).toBe(2); // Third item is visible at threshold 225
303
- });
304
-
305
- // Test 25: Items with zero size
306
- it("correctly handles items with zero size for first visible", () => {
307
- const layouts: RVLayout[] = [
308
- { x: 0, y: 0, width: 100, height: 0 },
309
- { x: 0, y: 0, width: 100, height: 100 },
310
- ];
311
-
312
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 0, false);
313
- expect(firstVisibleIndex).toBe(0); // First item is at position but has zero height
314
- });
315
-
316
- // Test 26: Items with zero size
317
- it("correctly handles items with zero size for last visible", () => {
318
- const layouts: RVLayout[] = [
319
- { x: 0, y: 0, width: 100, height: 100 },
320
- { x: 0, y: 100, width: 100, height: 0 },
321
- ];
322
-
323
- const lastVisibleIndex = findLastVisibleIndex(layouts, 100, false);
324
- expect(lastVisibleIndex).toBe(1); // Second item is at threshold position but has zero height
325
- });
326
-
327
- // Test 27: Large number of items - performance test
328
- it("efficiently finds first visible index in large dataset", () => {
329
- const layouts = createMockLayouts(1000, 0, 100, false);
330
-
331
- // Threshold in the middle of the list
332
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 50000, false);
333
- expect(firstVisibleIndex).toBe(500);
334
- });
335
-
336
- // Test 28: Large number of items - performance test
337
- it("efficiently finds last visible index in large dataset", () => {
338
- const layouts = createMockLayouts(1000, 0, 100, false);
339
-
340
- // Threshold in the middle of the list
341
- const lastVisibleIndex = findLastVisibleIndex(layouts, 50000, false);
342
- expect(lastVisibleIndex).toBe(500);
343
- });
344
-
345
- // Test 29: Non-sequential indices
346
- it("works with non-sequential indices for first visible", () => {
347
- const layouts: RVLayout[] = [
348
- { x: 0, y: 0, width: 100, height: 100 },
349
- { x: 0, y: 100, width: 100, height: 100 },
350
- { x: 0, y: 200, width: 100, height: 100 },
351
- ];
352
-
353
- const firstVisibleIndex = findFirstVisibleIndex(layouts, 150, false);
354
- expect(firstVisibleIndex).toBe(1); // Second layout in the array, not index 1
355
- });
356
-
357
- // Test 30: Non-sequential indices
358
- it("works with non-sequential indices for last visible", () => {
359
- const layouts: RVLayout[] = [
360
- { x: 0, y: 0, width: 100, height: 100 },
361
- { x: 0, y: 100, width: 100, height: 100 },
362
- { x: 0, y: 200, width: 100, height: 100 },
363
- ];
364
-
365
- const lastVisibleIndex = findLastVisibleIndex(layouts, 150, false);
366
- expect(lastVisibleIndex).toBe(1); // Second layout in the array, not index 1
367
- });
368
- });
369
- });