@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,141 +0,0 @@
1
- /* eslint-disable @shopify/typescript/prefer-pascal-case-enums */
2
- import {
3
- LayoutParams,
4
- RVDimension,
5
- RVLayout,
6
- RVLayoutInfo,
7
- RVLayoutManager,
8
- } from "../../recyclerview/layout-managers/LayoutManager";
9
- import { RVLinearLayoutManagerImpl } from "../../recyclerview/layout-managers/LinearLayoutManager";
10
- import { RVGridLayoutManagerImpl } from "../../recyclerview/layout-managers/GridLayoutManager";
11
- import { RVMasonryLayoutManagerImpl } from "../../recyclerview/layout-managers/MasonryLayoutManager";
12
-
13
- /**
14
- * Layout manager types available in the app
15
- */
16
- export enum LayoutManagerType {
17
- LINEAR = "linear",
18
- GRID = "grid",
19
- MASONRY = "masonry",
20
- }
21
-
22
- /**
23
- * Default window size for layout managers
24
- */
25
- const DEFAULT_WINDOW_SIZE: RVDimension = {
26
- width: 400,
27
- height: 900,
28
- };
29
-
30
- /**
31
- * Create layout parameters with sensible defaults
32
- */
33
- export function createLayoutParams(
34
- params: Partial<LayoutParams> = {}
35
- ): LayoutParams {
36
- return {
37
- windowSize: params.windowSize || DEFAULT_WINDOW_SIZE,
38
- horizontal: params.horizontal ?? false,
39
- maxColumns: params.maxColumns ?? 1,
40
- optimizeItemArrangement: params.optimizeItemArrangement ?? true,
41
- overrideItemLayout: params.overrideItemLayout ?? (() => {}),
42
- getItemType: params.getItemType ?? (() => "default"),
43
- };
44
- }
45
-
46
- /**
47
- * Create a layout manager of the specified type
48
- */
49
- export function createLayoutManager(
50
- type: LayoutManagerType,
51
- params: Partial<LayoutParams> = {},
52
- previousLayoutManager?: RVLayoutManager
53
- ): RVLayoutManager {
54
- const layoutParams = createLayoutParams(params);
55
-
56
- switch (type) {
57
- case LayoutManagerType.LINEAR:
58
- return new RVLinearLayoutManagerImpl(layoutParams, previousLayoutManager);
59
- case LayoutManagerType.GRID:
60
- return new RVGridLayoutManagerImpl(layoutParams, previousLayoutManager);
61
- case LayoutManagerType.MASONRY:
62
- return new RVMasonryLayoutManagerImpl(
63
- layoutParams,
64
- previousLayoutManager
65
- );
66
- default:
67
- throw new Error(`Unknown layout manager type: ${type}`);
68
- }
69
- }
70
-
71
- /**
72
- * Generate mock layout info for testing
73
- */
74
- export function createMockLayoutInfo(
75
- index: number,
76
- width: number,
77
- height: number
78
- ): RVLayoutInfo {
79
- return {
80
- index,
81
- dimensions: {
82
- width,
83
- height,
84
- },
85
- };
86
- }
87
-
88
- /**
89
- * Populate layout data in a layout manager
90
- */
91
- export function populateLayouts(
92
- layoutManager: RVLayoutManager,
93
- itemCount: number,
94
- itemWidth = 100,
95
- itemHeight = 100,
96
- variableSize = false
97
- ): void {
98
- const layoutInfos: RVLayoutInfo[] = [];
99
-
100
- for (let i = 0; i < itemCount; i++) {
101
- // If variableSize is true, add some randomness to the item dimensions
102
- const width = variableSize ? itemWidth + (i % 3) * 20 : itemWidth;
103
- const height = variableSize ? itemHeight + (i % 5) * 25 : itemHeight;
104
-
105
- layoutInfos.push(createMockLayoutInfo(i, width, height));
106
- }
107
-
108
- layoutManager.modifyLayout(layoutInfos, itemCount);
109
- }
110
-
111
- /**
112
- * Create and populate a layout manager in one step
113
- */
114
- export function createPopulatedLayoutManager(
115
- type: LayoutManagerType,
116
- itemCount: number,
117
- params: Partial<LayoutParams> = {},
118
- itemWidth = 100,
119
- itemHeight = 100,
120
- variableSize = false
121
- ): RVLayoutManager {
122
- const layoutManager = createLayoutManager(type, params);
123
- populateLayouts(
124
- layoutManager,
125
- itemCount,
126
- itemWidth,
127
- itemHeight,
128
- variableSize
129
- );
130
- return layoutManager;
131
- }
132
-
133
- /**
134
- * Get all layouts from a layout manager
135
- */
136
- export function getAllLayouts(layoutManager: RVLayoutManager): RVLayout[] {
137
- // Access the internal layouts array
138
- return Array.from({ length: layoutManager.getLayoutCount() }, (_, index) =>
139
- layoutManager.getLayout(index)
140
- );
141
- }
@@ -1,285 +0,0 @@
1
- import React from "react";
2
- import { render } from "@quilted/react-testing";
3
-
4
- import { useUnmountAwareTimeout } from "../recyclerview/hooks/useUnmountAwareCallbacks";
5
-
6
- const TestComponent = ({
7
- onRender,
8
- }: {
9
- onRender: (api: ReturnType<typeof useUnmountAwareTimeout>) => void;
10
- }) => {
11
- const api = useUnmountAwareTimeout();
12
- onRender(api);
13
- return null;
14
- };
15
-
16
- describe("useUnmountAwareCallbacks", () => {
17
- beforeEach(() => {
18
- jest.useFakeTimers();
19
- });
20
-
21
- afterEach(() => {
22
- jest.clearAllTimers();
23
- jest.clearAllMocks();
24
- });
25
-
26
- it("returns a setTimeout function", () => {
27
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
28
- render(
29
- <TestComponent
30
- onRender={(hookApi) => {
31
- api = hookApi;
32
- }}
33
- />
34
- );
35
-
36
- expect(api).toBeDefined();
37
- expect(api?.setTimeout).toBeDefined();
38
- expect(typeof api?.setTimeout).toBe("function");
39
- });
40
-
41
- it("executes the callback after the specified delay", () => {
42
- const callback = jest.fn();
43
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
44
-
45
- render(
46
- <TestComponent
47
- onRender={(hookApi) => {
48
- api = hookApi;
49
- }}
50
- />
51
- );
52
-
53
- api?.setTimeout(callback, 1000);
54
-
55
- expect(callback).not.toHaveBeenCalled();
56
-
57
- // Fast-forward time
58
- jest.advanceTimersByTime(1000);
59
-
60
- expect(callback).toHaveBeenCalledTimes(1);
61
- });
62
-
63
- it("executes multiple callbacks after their respective delays", () => {
64
- const callback1 = jest.fn();
65
- const callback2 = jest.fn();
66
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
67
-
68
- render(
69
- <TestComponent
70
- onRender={(hookApi) => {
71
- api = hookApi;
72
- }}
73
- />
74
- );
75
-
76
- api?.setTimeout(callback1, 1000);
77
- api?.setTimeout(callback2, 2000);
78
-
79
- expect(callback1).not.toHaveBeenCalled();
80
- expect(callback2).not.toHaveBeenCalled();
81
-
82
- // Fast-forward time by 1000ms
83
- jest.advanceTimersByTime(1000);
84
-
85
- expect(callback1).toHaveBeenCalledTimes(1);
86
- expect(callback2).not.toHaveBeenCalled();
87
-
88
- // Fast-forward time by another 1000ms
89
- jest.advanceTimersByTime(1000);
90
-
91
- expect(callback1).toHaveBeenCalledTimes(1);
92
- expect(callback2).toHaveBeenCalledTimes(1);
93
- });
94
-
95
- it("clears all timeouts when the component unmounts", () => {
96
- const callback = jest.fn();
97
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
98
-
99
- const component = render(
100
- <TestComponent
101
- onRender={(hookApi) => {
102
- api = hookApi;
103
- }}
104
- />
105
- );
106
-
107
- api?.setTimeout(callback, 1000);
108
- api?.setTimeout(callback, 2000);
109
-
110
- // Spy on clearTimeout to verify it's called during unmount
111
- const clearTimeoutSpy = jest.spyOn(global, "clearTimeout");
112
-
113
- // Unmount the component
114
- component.unmount();
115
-
116
- // Fast-forward time
117
- jest.advanceTimersByTime(2000);
118
-
119
- // Expect callbacks not to be called because timeouts were cleared
120
- expect(callback).not.toHaveBeenCalled();
121
- expect(clearTimeoutSpy).toHaveBeenCalled();
122
- });
123
-
124
- it("removes timeout from tracking set once it executes", () => {
125
- const callback = jest.fn();
126
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
127
-
128
- const component = render(
129
- <TestComponent
130
- onRender={(hookApi) => {
131
- api = hookApi;
132
- }}
133
- />
134
- );
135
-
136
- api?.setTimeout(callback, 1000);
137
-
138
- // Fast-forward time
139
- jest.advanceTimersByTime(1000);
140
-
141
- // Verify callback was called
142
- expect(callback).toHaveBeenCalledTimes(1);
143
-
144
- // We can't directly check the timeoutIds Set, so we'll verify indirectly
145
- // by making sure no clearTimeout calls happen on unmount (since the timeout was already cleared)
146
- const clearTimeoutSpy = jest.spyOn(global, "clearTimeout");
147
- clearTimeoutSpy.mockClear(); // Reset the mock calls before unmount
148
-
149
- // Unmount the component
150
- component.unmount();
151
-
152
- // If the timeout was properly removed from the set, clearTimeout won't be called on unmount
153
- expect(clearTimeoutSpy).not.toHaveBeenCalled();
154
- });
155
-
156
- it("handles multiple timeouts correctly", () => {
157
- const callback1 = jest.fn();
158
- const callback2 = jest.fn();
159
- const callback3 = jest.fn();
160
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
161
-
162
- const component = render(
163
- <TestComponent
164
- onRender={(hookApi) => {
165
- api = hookApi;
166
- }}
167
- />
168
- );
169
-
170
- // Set up three timeouts with different delays
171
- api?.setTimeout(callback1, 1000);
172
- api?.setTimeout(callback2, 2000);
173
- api?.setTimeout(callback3, 3000);
174
-
175
- // Fast-forward time by 1500ms (should trigger only the first callback)
176
- jest.advanceTimersByTime(1500);
177
-
178
- expect(callback1).toHaveBeenCalledTimes(1);
179
- expect(callback2).not.toHaveBeenCalled();
180
- expect(callback3).not.toHaveBeenCalled();
181
-
182
- // Unmount the component (should clear remaining timeouts)
183
- component.unmount();
184
-
185
- // Fast-forward time to when all callbacks would have been called
186
- jest.advanceTimersByTime(2000);
187
-
188
- // Only the first callback should have been called
189
- expect(callback1).toHaveBeenCalledTimes(1);
190
- expect(callback2).not.toHaveBeenCalled();
191
- expect(callback3).not.toHaveBeenCalled();
192
- });
193
-
194
- it("handles callbacks that trigger new timeouts", () => {
195
- const finalCallback = jest.fn();
196
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
197
-
198
- render(
199
- <TestComponent
200
- onRender={(hookApi) => {
201
- api = hookApi;
202
- }}
203
- />
204
- );
205
-
206
- const firstCallback = () => {
207
- api?.setTimeout(finalCallback, 1000);
208
- };
209
-
210
- api?.setTimeout(firstCallback, 1000);
211
-
212
- // Fast-forward time to trigger first callback
213
- jest.advanceTimersByTime(1000);
214
-
215
- expect(finalCallback).not.toHaveBeenCalled();
216
-
217
- // Fast-forward time to trigger second callback
218
- jest.advanceTimersByTime(1000);
219
-
220
- expect(finalCallback).toHaveBeenCalledTimes(1);
221
- });
222
-
223
- it("handles zero delay timeouts", () => {
224
- const callback = jest.fn();
225
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
226
-
227
- render(
228
- <TestComponent
229
- onRender={(hookApi) => {
230
- api = hookApi;
231
- }}
232
- />
233
- );
234
-
235
- api?.setTimeout(callback, 0);
236
-
237
- expect(callback).not.toHaveBeenCalled();
238
-
239
- // Even with zero delay, we need to advance the timer to execute
240
- jest.advanceTimersByTime(0);
241
-
242
- expect(callback).toHaveBeenCalledTimes(1);
243
- });
244
-
245
- it("handles errors in callbacks without affecting other timeouts", () => {
246
- const errorCallback = jest.fn(() => {
247
- throw new Error("Test error");
248
- });
249
- const successCallback = jest.fn();
250
- let api: ReturnType<typeof useUnmountAwareTimeout> | undefined;
251
-
252
- // Suppress error log during test
253
- const originalConsoleError = console.error;
254
- console.error = jest.fn();
255
-
256
- render(
257
- <TestComponent
258
- onRender={(hookApi) => {
259
- api = hookApi;
260
- }}
261
- />
262
- );
263
-
264
- api?.setTimeout(errorCallback, 1000);
265
- api?.setTimeout(successCallback, 2000);
266
-
267
- // Fast-forward time to trigger error callback
268
- try {
269
- jest.advanceTimersByTime(1000);
270
- } catch (error) {
271
- // Expected error
272
- }
273
-
274
- expect(errorCallback).toHaveBeenCalledTimes(1);
275
- expect(successCallback).not.toHaveBeenCalled();
276
-
277
- // Fast-forward time to trigger success callback
278
- jest.advanceTimersByTime(1000);
279
-
280
- expect(successCallback).toHaveBeenCalledTimes(1);
281
-
282
- // Restore console.error
283
- console.error = originalConsoleError;
284
- });
285
- });