@react-native-ohos/flash-list 1.6.4-rc.1
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.
- package/LICENSE +7 -0
- package/README.OpenSource +11 -0
- package/README.md +9 -0
- package/dist/AnimatedFlashList.d.ts +6 -0
- package/dist/AnimatedFlashList.d.ts.map +1 -0
- package/dist/AnimatedFlashList.js +8 -0
- package/dist/AnimatedFlashList.js.map +1 -0
- package/dist/FlashList.d.ts +126 -0
- package/dist/FlashList.d.ts.map +1 -0
- package/dist/FlashList.js +524 -0
- package/dist/FlashList.js.map +1 -0
- package/dist/FlashListProps.d.ts +256 -0
- package/dist/FlashListProps.d.ts.map +1 -0
- package/dist/FlashListProps.js +9 -0
- package/dist/FlashListProps.js.map +1 -0
- package/dist/GridLayoutProviderWithProps.d.ts +42 -0
- package/dist/GridLayoutProviderWithProps.d.ts.map +1 -0
- package/dist/GridLayoutProviderWithProps.js +115 -0
- package/dist/GridLayoutProviderWithProps.js.map +1 -0
- package/dist/MasonryFlashList.d.ts +51 -0
- package/dist/MasonryFlashList.d.ts.map +1 -0
- package/dist/MasonryFlashList.js +252 -0
- package/dist/MasonryFlashList.js.map +1 -0
- package/dist/PureComponentWrapper.d.ts +22 -0
- package/dist/PureComponentWrapper.d.ts.map +1 -0
- package/dist/PureComponentWrapper.js +37 -0
- package/dist/PureComponentWrapper.js.map +1 -0
- package/dist/__tests__/AverageWindow.test.d.ts +2 -0
- package/dist/__tests__/AverageWindow.test.d.ts.map +1 -0
- package/dist/__tests__/AverageWindow.test.js +69 -0
- package/dist/__tests__/AverageWindow.test.js.map +1 -0
- package/dist/__tests__/ContentContainerUtils.test.d.ts +2 -0
- package/dist/__tests__/ContentContainerUtils.test.d.ts.map +1 -0
- package/dist/__tests__/ContentContainerUtils.test.js +85 -0
- package/dist/__tests__/ContentContainerUtils.test.js.map +1 -0
- package/dist/__tests__/FlashList.test.d.ts +2 -0
- package/dist/__tests__/FlashList.test.d.ts.map +1 -0
- package/dist/__tests__/FlashList.test.js +792 -0
- package/dist/__tests__/FlashList.test.js.map +1 -0
- package/dist/__tests__/GridLayoutProviderWithProps.test.d.ts +2 -0
- package/dist/__tests__/GridLayoutProviderWithProps.test.d.ts.map +1 -0
- package/dist/__tests__/GridLayoutProviderWithProps.test.js +143 -0
- package/dist/__tests__/GridLayoutProviderWithProps.test.js.map +1 -0
- package/dist/__tests__/MasonryFlashList.test.d.ts +2 -0
- package/dist/__tests__/MasonryFlashList.test.d.ts.map +1 -0
- package/dist/__tests__/MasonryFlashList.test.js +254 -0
- package/dist/__tests__/MasonryFlashList.test.js.map +1 -0
- package/dist/__tests__/PlatformHelper.web.test.d.ts +2 -0
- package/dist/__tests__/PlatformHelper.web.test.d.ts.map +1 -0
- package/dist/__tests__/PlatformHelper.web.test.js +33 -0
- package/dist/__tests__/PlatformHelper.web.test.js.map +1 -0
- package/dist/__tests__/ViewabilityHelper.test.d.ts +2 -0
- package/dist/__tests__/ViewabilityHelper.test.d.ts.map +1 -0
- package/dist/__tests__/ViewabilityHelper.test.js +187 -0
- package/dist/__tests__/ViewabilityHelper.test.js.map +1 -0
- package/dist/__tests__/helpers/mountFlashList.d.ts +19 -0
- package/dist/__tests__/helpers/mountFlashList.d.ts.map +1 -0
- package/dist/__tests__/helpers/mountFlashList.js +44 -0
- package/dist/__tests__/helpers/mountFlashList.js.map +1 -0
- package/dist/__tests__/helpers/mountMasonryFlashList.d.ts +18 -0
- package/dist/__tests__/helpers/mountMasonryFlashList.d.ts.map +1 -0
- package/dist/__tests__/helpers/mountMasonryFlashList.js +49 -0
- package/dist/__tests__/helpers/mountMasonryFlashList.js.map +1 -0
- package/dist/__tests__/useBlankAreaTracker.test.d.ts +2 -0
- package/dist/__tests__/useBlankAreaTracker.test.d.ts.map +1 -0
- package/dist/__tests__/useBlankAreaTracker.test.js +177 -0
- package/dist/__tests__/useBlankAreaTracker.test.js.map +1 -0
- package/dist/benchmark/AutoScrollHelper.d.ts +18 -0
- package/dist/benchmark/AutoScrollHelper.d.ts.map +1 -0
- package/dist/benchmark/AutoScrollHelper.js +68 -0
- package/dist/benchmark/AutoScrollHelper.js.map +1 -0
- package/dist/benchmark/JSFPSMonitor.d.ts +23 -0
- package/dist/benchmark/JSFPSMonitor.d.ts.map +1 -0
- package/dist/benchmark/JSFPSMonitor.js +65 -0
- package/dist/benchmark/JSFPSMonitor.js.map +1 -0
- package/dist/benchmark/roundToDecimalPlaces.d.ts +2 -0
- package/dist/benchmark/roundToDecimalPlaces.d.ts.map +1 -0
- package/dist/benchmark/roundToDecimalPlaces.js +9 -0
- package/dist/benchmark/roundToDecimalPlaces.js.map +1 -0
- package/dist/benchmark/useBenchmark.d.ts +35 -0
- package/dist/benchmark/useBenchmark.d.ts.map +1 -0
- package/dist/benchmark/useBenchmark.js +167 -0
- package/dist/benchmark/useBenchmark.js.map +1 -0
- package/dist/benchmark/useBlankAreaTracker.d.ts +34 -0
- package/dist/benchmark/useBlankAreaTracker.d.ts.map +1 -0
- package/dist/benchmark/useBlankAreaTracker.js +67 -0
- package/dist/benchmark/useBlankAreaTracker.js.map +1 -0
- package/dist/benchmark/useDataMultiplier.d.ts +9 -0
- package/dist/benchmark/useDataMultiplier.d.ts.map +1 -0
- package/dist/benchmark/useDataMultiplier.js +25 -0
- package/dist/benchmark/useDataMultiplier.js.map +1 -0
- package/dist/benchmark/useFlatListBenchmark.d.ts +13 -0
- package/dist/benchmark/useFlatListBenchmark.d.ts.map +1 -0
- package/dist/benchmark/useFlatListBenchmark.js +100 -0
- package/dist/benchmark/useFlatListBenchmark.js.map +1 -0
- package/dist/errors/CustomError.d.ts +8 -0
- package/dist/errors/CustomError.d.ts.map +1 -0
- package/dist/errors/CustomError.js +14 -0
- package/dist/errors/CustomError.js.map +1 -0
- package/dist/errors/ExceptionList.d.ts +24 -0
- package/dist/errors/ExceptionList.d.ts.map +1 -0
- package/dist/errors/ExceptionList.js +26 -0
- package/dist/errors/ExceptionList.js.map +1 -0
- package/dist/errors/Warnings.d.ts +9 -0
- package/dist/errors/Warnings.d.ts.map +1 -0
- package/dist/errors/Warnings.js +13 -0
- package/dist/errors/Warnings.js.map +1 -0
- package/dist/fabric/AutoLayoutNativeComponent.d.ts +19 -0
- package/dist/fabric/AutoLayoutNativeComponent.d.ts.map +1 -0
- package/dist/fabric/AutoLayoutNativeComponent.js +29 -0
- package/dist/fabric/AutoLayoutNativeComponent.js.map +1 -0
- package/dist/fabric/CellContainerNativeComponent.d.ts +8 -0
- package/dist/fabric/CellContainerNativeComponent.d.ts.map +1 -0
- package/dist/fabric/CellContainerNativeComponent.js +29 -0
- package/dist/fabric/CellContainerNativeComponent.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutView.d.ts +22 -0
- package/dist/native/auto-layout/AutoLayoutView.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutView.js +48 -0
- package/dist/native/auto-layout/AutoLayoutView.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.d.ts +4 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.js +6 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.android.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts +5 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.harmony.d.ts +4 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.harmony.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.harmony.js +29 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.harmony.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.d.ts +4 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.js +6 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.ios.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.js +6 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponent.js.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts +16 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.d.ts.map +1 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.js +3 -0
- package/dist/native/auto-layout/AutoLayoutViewNativeComponentProps.js.map +1 -0
- package/dist/native/cell-container/CellContainer.android.d.ts +6 -0
- package/dist/native/cell-container/CellContainer.android.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.android.js +9 -0
- package/dist/native/cell-container/CellContainer.android.js.map +1 -0
- package/dist/native/cell-container/CellContainer.d.ts +8 -0
- package/dist/native/cell-container/CellContainer.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.harmony.d.ts +6 -0
- package/dist/native/cell-container/CellContainer.harmony.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.harmony.js +32 -0
- package/dist/native/cell-container/CellContainer.harmony.js.map +1 -0
- package/dist/native/cell-container/CellContainer.ios.d.ts +6 -0
- package/dist/native/cell-container/CellContainer.ios.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.ios.js +9 -0
- package/dist/native/cell-container/CellContainer.ios.js.map +1 -0
- package/dist/native/cell-container/CellContainer.js +11 -0
- package/dist/native/cell-container/CellContainer.js.map +1 -0
- package/dist/native/cell-container/CellContainer.web.d.ts +7 -0
- package/dist/native/cell-container/CellContainer.web.d.ts.map +1 -0
- package/dist/native/cell-container/CellContainer.web.js +13 -0
- package/dist/native/cell-container/CellContainer.web.js.map +1 -0
- package/dist/native/config/PlatformHelper.android.d.ts +26 -0
- package/dist/native/config/PlatformHelper.android.d.ts.map +1 -0
- package/dist/native/config/PlatformHelper.android.js +23 -0
- package/dist/native/config/PlatformHelper.android.js.map +1 -0
- package/dist/native/config/PlatformHelper.d.ts +26 -0
- package/dist/native/config/PlatformHelper.d.ts.map +1 -0
- package/dist/native/config/PlatformHelper.harmony.d.ts +26 -0
- package/dist/native/config/PlatformHelper.harmony.d.ts.map +1 -0
- package/dist/native/config/PlatformHelper.harmony.js +22 -0
- package/dist/native/config/PlatformHelper.harmony.js.map +1 -0
- package/dist/native/config/PlatformHelper.ios.d.ts +26 -0
- package/dist/native/config/PlatformHelper.ios.d.ts.map +1 -0
- package/dist/native/config/PlatformHelper.ios.js +22 -0
- package/dist/native/config/PlatformHelper.ios.js.map +1 -0
- package/dist/native/config/PlatformHelper.js +23 -0
- package/dist/native/config/PlatformHelper.js.map +1 -0
- package/dist/native/config/PlatformHelper.web.d.ts +27 -0
- package/dist/native/config/PlatformHelper.web.d.ts.map +1 -0
- package/dist/native/config/PlatformHelper.web.js +25 -0
- package/dist/native/config/PlatformHelper.web.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/utils/AverageWindow.d.ts +21 -0
- package/dist/utils/AverageWindow.d.ts.map +1 -0
- package/dist/utils/AverageWindow.js +49 -0
- package/dist/utils/AverageWindow.js.map +1 -0
- package/dist/utils/ContentContainerUtils.d.ts +27 -0
- package/dist/utils/ContentContainerUtils.d.ts.map +1 -0
- package/dist/utils/ContentContainerUtils.js +48 -0
- package/dist/utils/ContentContainerUtils.js.map +1 -0
- package/dist/viewability/ViewToken.d.ts +8 -0
- package/dist/viewability/ViewToken.d.ts.map +1 -0
- package/dist/viewability/ViewToken.js +3 -0
- package/dist/viewability/ViewToken.js.map +1 -0
- package/dist/viewability/ViewabilityHelper.d.ts +25 -0
- package/dist/viewability/ViewabilityHelper.d.ts.map +1 -0
- package/dist/viewability/ViewabilityHelper.js +104 -0
- package/dist/viewability/ViewabilityHelper.js.map +1 -0
- package/dist/viewability/ViewabilityManager.d.ts +24 -0
- package/dist/viewability/ViewabilityManager.d.ts.map +1 -0
- package/dist/viewability/ViewabilityManager.js +94 -0
- package/dist/viewability/ViewabilityManager.js.map +1 -0
- package/harmony/flash_list/BuildProfile.ets +5 -0
- package/harmony/flash_list/LICENSE +7 -0
- package/harmony/flash_list/build-profile.json5 +8 -0
- package/harmony/flash_list/hvigorfile.ts +1 -0
- package/harmony/flash_list/index.ets +29 -0
- package/harmony/flash_list/obfuscation-rules.txt +18 -0
- package/harmony/flash_list/oh-package-lock.json5 +17 -0
- package/harmony/flash_list/oh-package.json5 +13 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutNode.cpp +63 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutNode.h +57 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutShadow.cpp +140 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutShadow.h +66 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutViewComponentInstance.cpp +208 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutViewComponentInstance.h +78 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutViewEventEmitRequestHandler.h +53 -0
- package/harmony/flash_list/src/main/cpp/AutoLayoutViewJSIBinder.h +50 -0
- package/harmony/flash_list/src/main/cpp/CMakeLists.txt +7 -0
- package/harmony/flash_list/src/main/cpp/CellContainerComponentInstance.cpp +80 -0
- package/harmony/flash_list/src/main/cpp/CellContainerComponentInstance.h +67 -0
- package/harmony/flash_list/src/main/cpp/CellContainerJSIBinder.h +39 -0
- package/harmony/flash_list/src/main/cpp/ComponentDescriptors.h +40 -0
- package/harmony/flash_list/src/main/cpp/EventEmitters.cpp +40 -0
- package/harmony/flash_list/src/main/cpp/EventEmitters.h +48 -0
- package/harmony/flash_list/src/main/cpp/FlashListPackage.h +78 -0
- package/harmony/flash_list/src/main/cpp/FlashListStackNode.cpp +89 -0
- package/harmony/flash_list/src/main/cpp/FlashListStackNode.h +59 -0
- package/harmony/flash_list/src/main/cpp/Props.cpp +52 -0
- package/harmony/flash_list/src/main/cpp/Props.h +64 -0
- package/harmony/flash_list/src/main/cpp/ShadowNodes.cpp +34 -0
- package/harmony/flash_list/src/main/cpp/ShadowNodes.h +48 -0
- package/harmony/flash_list/src/main/ets/Logger.ets +64 -0
- package/harmony/flash_list/src/main/ets/RNAutoLayoutShadow.ets +154 -0
- package/harmony/flash_list/src/main/ets/RNAutoLayoutView.ets +252 -0
- package/harmony/flash_list/src/main/ets/RNCellContainer.ets +113 -0
- package/harmony/flash_list/src/main/module.json5 +7 -0
- package/harmony/flash_list/src/main/resources/base/element/color.json +8 -0
- package/harmony/flash_list/src/main/resources/base/element/string.json +16 -0
- package/harmony/flash_list/src/main/resources/base/media/icon.png +0 -0
- package/harmony/flash_list/src/main/resources/base/profile/main_pages.json +5 -0
- package/harmony/flash_list/src/main/resources/en_US/element/string.json +16 -0
- package/harmony/flash_list/src/main/resources/zh_CN/element/string.json +16 -0
- package/harmony/flash_list.har +0 -0
- package/jestSetup.js +16 -0
- package/package.json +88 -0
- package/src/AnimatedFlashList.ts +11 -0
- package/src/FlashList.tsx +870 -0
- package/src/FlashListProps.ts +335 -0
- package/src/GridLayoutProviderWithProps.ts +180 -0
- package/src/MasonryFlashList.tsx +476 -0
- package/src/PureComponentWrapper.tsx +42 -0
- package/src/__tests__/AverageWindow.test.ts +80 -0
- package/src/__tests__/ContentContainerUtils.test.ts +130 -0
- package/src/__tests__/FlashList.test.tsx +886 -0
- package/src/__tests__/GridLayoutProviderWithProps.test.ts +179 -0
- package/src/__tests__/MasonryFlashList.test.ts +292 -0
- package/src/__tests__/PlatformHelper.web.test.ts +45 -0
- package/src/__tests__/ViewabilityHelper.test.ts +283 -0
- package/src/__tests__/helpers/mountFlashList.tsx +62 -0
- package/src/__tests__/helpers/mountMasonryFlashList.tsx +70 -0
- package/src/__tests__/useBlankAreaTracker.test.tsx +204 -0
- package/src/benchmark/AutoScrollHelper.ts +70 -0
- package/src/benchmark/JSFPSMonitor.ts +74 -0
- package/src/benchmark/roundToDecimalPlaces.ts +4 -0
- package/src/benchmark/useBenchmark.ts +240 -0
- package/src/benchmark/useBlankAreaTracker.ts +117 -0
- package/src/benchmark/useDataMultiplier.ts +19 -0
- package/src/benchmark/useFlatListBenchmark.ts +107 -0
- package/src/errors/CustomError.ts +10 -0
- package/src/errors/ExceptionList.ts +28 -0
- package/src/errors/Warnings.ts +15 -0
- package/src/fabric/AutoLayoutNativeComponent.ts +51 -0
- package/src/fabric/CellContainerNativeComponent.ts +34 -0
- package/src/index.ts +43 -0
- package/src/native/auto-layout/AutoLayoutView.tsx +73 -0
- package/src/native/auto-layout/AutoLayoutViewNativeComponent.android.ts +7 -0
- package/src/native/auto-layout/AutoLayoutViewNativeComponent.harmony.ts +30 -0
- package/src/native/auto-layout/AutoLayoutViewNativeComponent.ios.ts +7 -0
- package/src/native/auto-layout/AutoLayoutViewNativeComponent.ts +7 -0
- package/src/native/auto-layout/AutoLayoutViewNativeComponentProps.ts +17 -0
- package/src/native/cell-container/CellContainer.android.ts +7 -0
- package/src/native/cell-container/CellContainer.harmony.ts +30 -0
- package/src/native/cell-container/CellContainer.ios.ts +6 -0
- package/src/native/cell-container/CellContainer.tsx +14 -0
- package/src/native/cell-container/CellContainer.web.tsx +9 -0
- package/src/native/config/PlatformHelper.android.ts +29 -0
- package/src/native/config/PlatformHelper.harmony.ts +51 -0
- package/src/native/config/PlatformHelper.ios.ts +28 -0
- package/src/native/config/PlatformHelper.ts +29 -0
- package/src/native/config/PlatformHelper.web.ts +34 -0
- package/src/utils/AverageWindow.ts +49 -0
- package/src/utils/ContentContainerUtils.ts +92 -0
- package/src/viewability/ViewToken.ts +7 -0
- package/src/viewability/ViewabilityHelper.ts +162 -0
- package/src/viewability/ViewabilityManager.ts +134 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import { Dimension, Layout } from "recyclerlistview";
|
|
2
|
+
|
|
3
|
+
import CustomError from "../errors/CustomError";
|
|
4
|
+
import ExceptionList from "../errors/ExceptionList";
|
|
5
|
+
import ViewabilityHelper from "../viewability/ViewabilityHelper";
|
|
6
|
+
|
|
7
|
+
describe("ViewabilityHelper", () => {
|
|
8
|
+
const viewableIndicesChanged = jest.fn();
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
jest.resetAllMocks();
|
|
11
|
+
jest.useFakeTimers();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("does not report any changes when indices have not changed", () => {
|
|
15
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
16
|
+
null,
|
|
17
|
+
viewableIndicesChanged
|
|
18
|
+
);
|
|
19
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2];
|
|
20
|
+
updateViewableItems({ viewabilityHelper });
|
|
21
|
+
// Initial call
|
|
22
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
23
|
+
[0, 1, 2],
|
|
24
|
+
[0, 1, 2],
|
|
25
|
+
[]
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
// No changes
|
|
29
|
+
viewableIndicesChanged.mockReset();
|
|
30
|
+
updateViewableItems({ viewabilityHelper });
|
|
31
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("reports only viewable indices", () => {
|
|
35
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
36
|
+
null,
|
|
37
|
+
viewableIndicesChanged
|
|
38
|
+
);
|
|
39
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
|
|
40
|
+
updateViewableItems({ viewabilityHelper });
|
|
41
|
+
// Items 0, 1, 2 are initially viewable
|
|
42
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
43
|
+
[0, 1, 2],
|
|
44
|
+
[0, 1, 2],
|
|
45
|
+
[]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// After scroll, item 3 becomes viewable, too
|
|
49
|
+
updateViewableItems({ viewabilityHelper, scrollOffset: 50 });
|
|
50
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
|
|
51
|
+
|
|
52
|
+
// After additional scroll, the first item is no longer viewable
|
|
53
|
+
updateViewableItems({ viewabilityHelper, scrollOffset: 100 });
|
|
54
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("reports only viewable indices when horizontal", () => {
|
|
58
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
59
|
+
null,
|
|
60
|
+
viewableIndicesChanged
|
|
61
|
+
);
|
|
62
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
|
|
63
|
+
const getLayout = (index: number) => {
|
|
64
|
+
return { x: index * 100, y: 0, height: 300, width: 100 } as Layout;
|
|
65
|
+
};
|
|
66
|
+
updateViewableItems({ viewabilityHelper, horizontal: true, getLayout });
|
|
67
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
68
|
+
[0, 1, 2],
|
|
69
|
+
[0, 1, 2],
|
|
70
|
+
[]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
updateViewableItems({
|
|
74
|
+
viewabilityHelper,
|
|
75
|
+
horizontal: true,
|
|
76
|
+
scrollOffset: 50,
|
|
77
|
+
getLayout,
|
|
78
|
+
});
|
|
79
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
|
|
80
|
+
|
|
81
|
+
updateViewableItems({
|
|
82
|
+
viewabilityHelper,
|
|
83
|
+
horizontal: true,
|
|
84
|
+
scrollOffset: 100,
|
|
85
|
+
getLayout,
|
|
86
|
+
});
|
|
87
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("reports items only after minimumViewTime has elapsed", () => {
|
|
91
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
92
|
+
{ minimumViewTime: 500 },
|
|
93
|
+
viewableIndicesChanged
|
|
94
|
+
);
|
|
95
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
|
|
96
|
+
updateViewableItems({ viewabilityHelper, runAllTimers: false });
|
|
97
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
98
|
+
jest.advanceTimersByTime(400);
|
|
99
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
100
|
+
jest.advanceTimersByTime(100);
|
|
101
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
102
|
+
[0, 1, 2],
|
|
103
|
+
[0, 1, 2],
|
|
104
|
+
[]
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
viewableIndicesChanged.mockReset();
|
|
108
|
+
updateViewableItems({
|
|
109
|
+
viewabilityHelper,
|
|
110
|
+
scrollOffset: 50,
|
|
111
|
+
runAllTimers: false,
|
|
112
|
+
});
|
|
113
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
114
|
+
jest.advanceTimersByTime(500);
|
|
115
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
|
|
116
|
+
|
|
117
|
+
viewableIndicesChanged.mockReset();
|
|
118
|
+
updateViewableItems({
|
|
119
|
+
viewabilityHelper,
|
|
120
|
+
scrollOffset: 100,
|
|
121
|
+
runAllTimers: false,
|
|
122
|
+
});
|
|
123
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
124
|
+
jest.advanceTimersByTime(500);
|
|
125
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("reports items that only satisfy itemVisiblePercentThreshold", () => {
|
|
129
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
130
|
+
{ itemVisiblePercentThreshold: 50 },
|
|
131
|
+
viewableIndicesChanged
|
|
132
|
+
);
|
|
133
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
|
|
134
|
+
updateViewableItems({
|
|
135
|
+
viewabilityHelper,
|
|
136
|
+
});
|
|
137
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
138
|
+
[0, 1, 2],
|
|
139
|
+
[0, 1, 2],
|
|
140
|
+
[]
|
|
141
|
+
);
|
|
142
|
+
viewableIndicesChanged.mockReset();
|
|
143
|
+
|
|
144
|
+
// User scrolled by 50 pixels, making both first and last item visible from 50 %
|
|
145
|
+
updateViewableItems({
|
|
146
|
+
viewabilityHelper,
|
|
147
|
+
scrollOffset: 50,
|
|
148
|
+
});
|
|
149
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([0, 1, 2, 3], [3], []);
|
|
150
|
+
viewableIndicesChanged.mockReset();
|
|
151
|
+
|
|
152
|
+
// User scrolled by 55 pixels, first item no longer satisfies threshold
|
|
153
|
+
updateViewableItems({
|
|
154
|
+
viewabilityHelper,
|
|
155
|
+
scrollOffset: 55,
|
|
156
|
+
});
|
|
157
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [], [0]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("reports items that only satisfy viewAreaCoveragePercentThreshold", () => {
|
|
161
|
+
const getLayout = (index: number) => {
|
|
162
|
+
if (index === 4) {
|
|
163
|
+
return { x: 0, y: index * 100, width: 100, height: 25 } as Layout;
|
|
164
|
+
}
|
|
165
|
+
return { x: 0, y: index * 100, height: 100, width: 300 } as Layout;
|
|
166
|
+
};
|
|
167
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
168
|
+
{ viewAreaCoveragePercentThreshold: 25 },
|
|
169
|
+
viewableIndicesChanged
|
|
170
|
+
);
|
|
171
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
|
|
172
|
+
updateViewableItems({
|
|
173
|
+
viewabilityHelper,
|
|
174
|
+
getLayout,
|
|
175
|
+
});
|
|
176
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
177
|
+
[0, 1, 2],
|
|
178
|
+
[0, 1, 2],
|
|
179
|
+
[]
|
|
180
|
+
);
|
|
181
|
+
viewableIndicesChanged.mockReset();
|
|
182
|
+
|
|
183
|
+
// User scrolled by 75 pixels.
|
|
184
|
+
// First item is visible only from 25 pixels, not meeting the threshold.
|
|
185
|
+
// The last item is visible from 75 pixels, which is exactly the threshold (300 / 4 = 75 where 300 is height of the list)
|
|
186
|
+
updateViewableItems({
|
|
187
|
+
viewabilityHelper,
|
|
188
|
+
scrollOffset: 75,
|
|
189
|
+
getLayout,
|
|
190
|
+
});
|
|
191
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3], [3], [0]);
|
|
192
|
+
viewableIndicesChanged.mockReset();
|
|
193
|
+
|
|
194
|
+
// User scrolled by 110 pixels, making the last small item only partially visible, not meeting the threshold.
|
|
195
|
+
viewabilityHelper.possiblyViewableIndices = [1, 2, 3, 4];
|
|
196
|
+
updateViewableItems({
|
|
197
|
+
viewabilityHelper,
|
|
198
|
+
scrollOffset: 110,
|
|
199
|
+
getLayout,
|
|
200
|
+
});
|
|
201
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
202
|
+
|
|
203
|
+
// User scrolled by 125 pixels, making the last small item completely visible, even when it is not meeting the threshold.
|
|
204
|
+
viewabilityHelper.possiblyViewableIndices = [1, 2, 3, 4];
|
|
205
|
+
updateViewableItems({
|
|
206
|
+
viewabilityHelper,
|
|
207
|
+
scrollOffset: 125,
|
|
208
|
+
getLayout,
|
|
209
|
+
});
|
|
210
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith([1, 2, 3, 4], [4], []);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
it("reports viewable items only after interaction if waitForInteraction is set to true", () => {
|
|
214
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
215
|
+
{ waitForInteraction: true },
|
|
216
|
+
viewableIndicesChanged
|
|
217
|
+
);
|
|
218
|
+
// Even when elements are visible, viewableIndicesChanged will not be called since interaction has not been recorded, yet
|
|
219
|
+
viewabilityHelper.possiblyViewableIndices = [0, 1, 2, 3];
|
|
220
|
+
updateViewableItems({
|
|
221
|
+
viewabilityHelper,
|
|
222
|
+
});
|
|
223
|
+
// View is scrolled but programatically - not resulting in an interaction
|
|
224
|
+
updateViewableItems({
|
|
225
|
+
viewabilityHelper,
|
|
226
|
+
scrollOffset: 50,
|
|
227
|
+
});
|
|
228
|
+
expect(viewableIndicesChanged).not.toHaveBeenCalled();
|
|
229
|
+
|
|
230
|
+
// Interaction is recorded, leading to trigger of viewableIndicesChanged
|
|
231
|
+
viewabilityHelper.hasInteracted = true;
|
|
232
|
+
updateViewableItems({
|
|
233
|
+
viewabilityHelper,
|
|
234
|
+
scrollOffset: 50,
|
|
235
|
+
});
|
|
236
|
+
expect(viewableIndicesChanged).toHaveBeenCalledWith(
|
|
237
|
+
[0, 1, 2, 3],
|
|
238
|
+
[0, 1, 2, 3],
|
|
239
|
+
[]
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("throws multipleViewabilityThresholdTypesNotSupported exception when both viewAreaCoveragePercentThreshold and itemVisiblePercentThreshold are defined", () => {
|
|
244
|
+
const viewabilityHelper = new ViewabilityHelper(
|
|
245
|
+
{ viewAreaCoveragePercentThreshold: 1, itemVisiblePercentThreshold: 1 },
|
|
246
|
+
viewableIndicesChanged
|
|
247
|
+
);
|
|
248
|
+
expect(() => updateViewableItems({ viewabilityHelper })).toThrow(
|
|
249
|
+
new CustomError(
|
|
250
|
+
ExceptionList.multipleViewabilityThresholdTypesNotSupported
|
|
251
|
+
)
|
|
252
|
+
);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const updateViewableItems = ({
|
|
256
|
+
viewabilityHelper,
|
|
257
|
+
horizontal,
|
|
258
|
+
scrollOffset,
|
|
259
|
+
listSize,
|
|
260
|
+
getLayout,
|
|
261
|
+
runAllTimers,
|
|
262
|
+
}: {
|
|
263
|
+
viewabilityHelper: ViewabilityHelper;
|
|
264
|
+
horizontal?: boolean;
|
|
265
|
+
scrollOffset?: number;
|
|
266
|
+
listSize?: Dimension;
|
|
267
|
+
getLayout?: (index: number) => Layout | undefined;
|
|
268
|
+
runAllTimers?: boolean;
|
|
269
|
+
}) => {
|
|
270
|
+
viewabilityHelper.updateViewableItems(
|
|
271
|
+
horizontal ?? false,
|
|
272
|
+
scrollOffset ?? 0,
|
|
273
|
+
listSize ?? { height: 300, width: 300 },
|
|
274
|
+
getLayout ??
|
|
275
|
+
((index) => {
|
|
276
|
+
return { x: 0, y: index * 100, height: 100, width: 300 } as Layout;
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
if (runAllTimers ?? true) {
|
|
280
|
+
jest.runAllTimers();
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import "@quilted/react-testing/matchers";
|
|
4
|
+
import { mount, Root } from "@quilted/react-testing";
|
|
5
|
+
|
|
6
|
+
import FlashList from "../../FlashList";
|
|
7
|
+
import { FlashListProps, ListRenderItem } from "../../FlashListProps";
|
|
8
|
+
|
|
9
|
+
jest.mock("../../FlashList", () => {
|
|
10
|
+
const ActualFlashList = jest.requireActual("../../FlashList").default;
|
|
11
|
+
class MockFlashList extends ActualFlashList {
|
|
12
|
+
componentDidMount() {
|
|
13
|
+
super.componentDidMount();
|
|
14
|
+
this.rlvRef?._scrollComponent?._scrollViewRef?.props.onLayout({
|
|
15
|
+
nativeEvent: { layout: { height: 900, width: 400 } },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return MockFlashList;
|
|
20
|
+
});
|
|
21
|
+
export type MockFlashListProps = Omit<
|
|
22
|
+
FlashListProps<string>,
|
|
23
|
+
"estimatedItemSize" | "data" | "renderItem"
|
|
24
|
+
> & {
|
|
25
|
+
estimatedItemSize?: number;
|
|
26
|
+
data?: string[];
|
|
27
|
+
renderItem?: ListRenderItem<string>;
|
|
28
|
+
disableDefaultEstimatedItemSize?: boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Helper to mount FlashList for testing.
|
|
32
|
+
*/
|
|
33
|
+
export const mountFlashList = (
|
|
34
|
+
props?: MockFlashListProps,
|
|
35
|
+
ref?: React.RefObject<FlashList<string>>
|
|
36
|
+
) => {
|
|
37
|
+
const flashList = mount(renderFlashList(props, ref)) as Omit<
|
|
38
|
+
Root<FlashListProps<string>>,
|
|
39
|
+
"instance"
|
|
40
|
+
> & {
|
|
41
|
+
instance: FlashList<string>;
|
|
42
|
+
};
|
|
43
|
+
return flashList;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export function renderFlashList(
|
|
47
|
+
props?: MockFlashListProps,
|
|
48
|
+
ref?: React.RefObject<FlashList<string> | null>|any
|
|
49
|
+
) {
|
|
50
|
+
return (
|
|
51
|
+
<FlashList
|
|
52
|
+
{...props}
|
|
53
|
+
ref={ref}
|
|
54
|
+
renderItem={props?.renderItem || (({ item }) => <Text>{item}</Text>)}
|
|
55
|
+
estimatedItemSize={
|
|
56
|
+
props?.estimatedItemSize ??
|
|
57
|
+
(props?.disableDefaultEstimatedItemSize ? undefined : 200)
|
|
58
|
+
}
|
|
59
|
+
data={props?.data || ["One", "Two", "Three", "Four"]}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import "@quilted/react-testing/matchers";
|
|
4
|
+
import { mount, Root } from "@quilted/react-testing";
|
|
5
|
+
|
|
6
|
+
import { ListRenderItem } from "../../FlashListProps";
|
|
7
|
+
import {
|
|
8
|
+
MasonryFlashList,
|
|
9
|
+
MasonryFlashListProps,
|
|
10
|
+
MasonryFlashListRef,
|
|
11
|
+
} from "../../MasonryFlashList";
|
|
12
|
+
|
|
13
|
+
jest.mock("../../FlashList", () => {
|
|
14
|
+
const ActualFlashList = jest.requireActual("../../FlashList").default;
|
|
15
|
+
class MockFlashList extends ActualFlashList {
|
|
16
|
+
componentDidMount() {
|
|
17
|
+
super.componentDidMount();
|
|
18
|
+
this.rlvRef?._scrollComponent?._scrollViewRef?.props.onLayout({
|
|
19
|
+
nativeEvent: {
|
|
20
|
+
layout: {
|
|
21
|
+
height: this.props.estimatedListSize?.height ?? 900,
|
|
22
|
+
width: this.props.estimatedListSize?.width ?? 400,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return MockFlashList;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export type MockMasonryFlashListProps = Omit<
|
|
32
|
+
MasonryFlashListProps<string>,
|
|
33
|
+
"estimatedItemSize" | "data" | "renderItem"
|
|
34
|
+
> & {
|
|
35
|
+
estimatedItemSize?: number;
|
|
36
|
+
data?: string[];
|
|
37
|
+
renderItem?: ListRenderItem<string>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Helper to mount MasonryFlashList for testing.
|
|
42
|
+
*/
|
|
43
|
+
export const mountMasonryFlashList = (
|
|
44
|
+
props?: MockMasonryFlashListProps,
|
|
45
|
+
ref?: React.RefObject<MasonryFlashListRef<string> | null>
|
|
46
|
+
) => {
|
|
47
|
+
const flashList = mount(renderMasonryFlashList(props, ref)) as Omit<
|
|
48
|
+
Root<MasonryFlashListProps<string>>,
|
|
49
|
+
"instance"
|
|
50
|
+
> & {
|
|
51
|
+
instance: MasonryFlashListRef<string>;
|
|
52
|
+
};
|
|
53
|
+
return flashList;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export function renderMasonryFlashList(
|
|
57
|
+
props?: MockMasonryFlashListProps,
|
|
58
|
+
ref?: React.RefObject<MasonryFlashListRef<string> | null>
|
|
59
|
+
) {
|
|
60
|
+
return (
|
|
61
|
+
<MasonryFlashList
|
|
62
|
+
{...props}
|
|
63
|
+
ref={ref}
|
|
64
|
+
numColumns={props?.numColumns ?? 2}
|
|
65
|
+
renderItem={props?.renderItem || (({ item }) => <Text>{item}</Text>)}
|
|
66
|
+
estimatedItemSize={props?.estimatedItemSize ?? 200}
|
|
67
|
+
data={props?.data || ["One", "Two", "Three", "Four"]}
|
|
68
|
+
/>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
import { mount } from "@quilted/react-testing";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
BlankAreaTrackerConfig,
|
|
6
|
+
BlankAreaTrackerResult,
|
|
7
|
+
useBlankAreaTracker,
|
|
8
|
+
} from "../benchmark/useBlankAreaTracker";
|
|
9
|
+
import FlashList from "../FlashList";
|
|
10
|
+
|
|
11
|
+
import { MockFlashListProps, renderFlashList } from "./helpers/mountFlashList";
|
|
12
|
+
|
|
13
|
+
type BlankTrackingFlashListProps = MockFlashListProps & {
|
|
14
|
+
onCumulativeBlankAreaResult?: (result: BlankAreaTrackerResult) => void;
|
|
15
|
+
onCumulativeBlankAreaChange?: (updatedResult: BlankAreaTrackerResult) => void;
|
|
16
|
+
blankAreaTrackerConfig?: BlankAreaTrackerConfig;
|
|
17
|
+
instance?: React.RefObject<FlashList<any> | null>;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const BlankTrackingFlashList = (props?: BlankTrackingFlashListProps) => {
|
|
21
|
+
const ref = props?.instance!;
|
|
22
|
+
const [blankAreaTrackerResult, onBlankArea] = useBlankAreaTracker(
|
|
23
|
+
ref,
|
|
24
|
+
props?.onCumulativeBlankAreaChange,
|
|
25
|
+
{
|
|
26
|
+
startDelayInMs: props?.blankAreaTrackerConfig?.startDelayInMs ?? 500,
|
|
27
|
+
sumNegativeValues:
|
|
28
|
+
props?.blankAreaTrackerConfig?.sumNegativeValues ?? false,
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
return () => {
|
|
33
|
+
props?.onCumulativeBlankAreaResult?.(blankAreaTrackerResult);
|
|
34
|
+
};
|
|
35
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
36
|
+
}, []);
|
|
37
|
+
return renderFlashList(
|
|
38
|
+
{ ...props, onBlankArea, estimatedItemSize: 400 },
|
|
39
|
+
ref
|
|
40
|
+
);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const mountBlankTrackingFlashList = (props?: BlankTrackingFlashListProps) => {
|
|
44
|
+
const flashListRef = React.createRef<FlashList<any>>();
|
|
45
|
+
const blankTrackingFlashList = mount(
|
|
46
|
+
<BlankTrackingFlashList {...props} instance={flashListRef} />
|
|
47
|
+
);
|
|
48
|
+
return {
|
|
49
|
+
root: blankTrackingFlashList,
|
|
50
|
+
instance: flashListRef.current!,
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
describe("useBlankAreaTracker Hook", () => {
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
jest.clearAllMocks();
|
|
57
|
+
jest.useFakeTimers();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("ignores blank events for 500ms on mount and if content is not enough to fill the list", () => {
|
|
61
|
+
const onCumulativeBlankAreaChange = jest.fn();
|
|
62
|
+
const flashList = mountBlankTrackingFlashList({
|
|
63
|
+
onCumulativeBlankAreaChange,
|
|
64
|
+
});
|
|
65
|
+
flashList.instance.props.onBlankArea!({
|
|
66
|
+
blankArea: 100,
|
|
67
|
+
offsetEnd: 100,
|
|
68
|
+
offsetStart: 0,
|
|
69
|
+
});
|
|
70
|
+
flashList.instance.props.onBlankArea!({
|
|
71
|
+
blankArea: 100,
|
|
72
|
+
offsetEnd: 100,
|
|
73
|
+
offsetStart: 0,
|
|
74
|
+
});
|
|
75
|
+
jest.advanceTimersByTime(400);
|
|
76
|
+
flashList.instance.props.onBlankArea!({
|
|
77
|
+
blankArea: 100,
|
|
78
|
+
offsetEnd: 100,
|
|
79
|
+
offsetStart: 0,
|
|
80
|
+
});
|
|
81
|
+
expect(onCumulativeBlankAreaChange).toHaveBeenCalledTimes(0);
|
|
82
|
+
jest.advanceTimersByTime(100);
|
|
83
|
+
flashList.instance.props.onBlankArea!({
|
|
84
|
+
blankArea: 100,
|
|
85
|
+
offsetEnd: 100,
|
|
86
|
+
offsetStart: 0,
|
|
87
|
+
});
|
|
88
|
+
expect(onCumulativeBlankAreaChange).toHaveBeenCalledTimes(1);
|
|
89
|
+
onCumulativeBlankAreaChange.mockClear();
|
|
90
|
+
|
|
91
|
+
flashList.root.setProps({ data: ["1"] });
|
|
92
|
+
flashList.instance.props.onBlankArea!({
|
|
93
|
+
blankArea: 100,
|
|
94
|
+
offsetEnd: 100,
|
|
95
|
+
offsetStart: 0,
|
|
96
|
+
});
|
|
97
|
+
expect(onCumulativeBlankAreaChange).toHaveBeenCalledTimes(0);
|
|
98
|
+
flashList.root.unmount();
|
|
99
|
+
});
|
|
100
|
+
it("keeps result object updated with correct values on unmount", () => {
|
|
101
|
+
let resultObjectCopy: BlankAreaTrackerResult | undefined;
|
|
102
|
+
const onCumulativeBlankAreaChange = jest.fn(
|
|
103
|
+
(result: BlankAreaTrackerResult) => {
|
|
104
|
+
if (!resultObjectCopy) {
|
|
105
|
+
resultObjectCopy = result;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
const onCumulativeBlankAreaResult = jest.fn();
|
|
110
|
+
const flashList = mountBlankTrackingFlashList({
|
|
111
|
+
onCumulativeBlankAreaResult,
|
|
112
|
+
onCumulativeBlankAreaChange,
|
|
113
|
+
blankAreaTrackerConfig: { startDelayInMs: 300 },
|
|
114
|
+
});
|
|
115
|
+
flashList.instance.props.onBlankArea!({
|
|
116
|
+
blankArea: 100,
|
|
117
|
+
offsetEnd: 100,
|
|
118
|
+
offsetStart: 0,
|
|
119
|
+
});
|
|
120
|
+
jest.advanceTimersByTime(300);
|
|
121
|
+
flashList.instance.props.onBlankArea!({
|
|
122
|
+
blankArea: 100,
|
|
123
|
+
offsetEnd: 100,
|
|
124
|
+
offsetStart: 0,
|
|
125
|
+
});
|
|
126
|
+
expect(resultObjectCopy!.cumulativeBlankArea).toBe(100);
|
|
127
|
+
expect(resultObjectCopy!.maxBlankArea).toBe(100);
|
|
128
|
+
|
|
129
|
+
flashList.instance.props.onBlankArea!({
|
|
130
|
+
blankArea: 200,
|
|
131
|
+
offsetEnd: 200,
|
|
132
|
+
offsetStart: 0,
|
|
133
|
+
});
|
|
134
|
+
flashList.instance.props.onBlankArea!({
|
|
135
|
+
blankArea: -200,
|
|
136
|
+
offsetEnd: -200,
|
|
137
|
+
offsetStart: 0,
|
|
138
|
+
});
|
|
139
|
+
expect(resultObjectCopy!.cumulativeBlankArea).toBe(300);
|
|
140
|
+
expect(resultObjectCopy!.maxBlankArea).toBe(200);
|
|
141
|
+
|
|
142
|
+
flashList.root.unmount();
|
|
143
|
+
expect(onCumulativeBlankAreaResult).toHaveBeenCalledWith(resultObjectCopy!);
|
|
144
|
+
});
|
|
145
|
+
it("can track negative values on demand", () => {
|
|
146
|
+
const onCumulativeBlankAreaResult = jest.fn();
|
|
147
|
+
const flashList = mountBlankTrackingFlashList({
|
|
148
|
+
onCumulativeBlankAreaResult,
|
|
149
|
+
blankAreaTrackerConfig: { sumNegativeValues: true },
|
|
150
|
+
});
|
|
151
|
+
flashList.instance.props.onBlankArea!({
|
|
152
|
+
blankArea: -200,
|
|
153
|
+
offsetEnd: -200,
|
|
154
|
+
offsetStart: 0,
|
|
155
|
+
});
|
|
156
|
+
jest.advanceTimersByTime(500);
|
|
157
|
+
flashList.instance.props.onBlankArea!({
|
|
158
|
+
blankArea: -200,
|
|
159
|
+
offsetEnd: -200,
|
|
160
|
+
offsetStart: 0,
|
|
161
|
+
});
|
|
162
|
+
flashList.instance.props.onBlankArea!({
|
|
163
|
+
blankArea: -200,
|
|
164
|
+
offsetEnd: -200,
|
|
165
|
+
offsetStart: 0,
|
|
166
|
+
});
|
|
167
|
+
flashList.root.unmount();
|
|
168
|
+
expect(onCumulativeBlankAreaResult).toHaveBeenCalledWith({
|
|
169
|
+
cumulativeBlankArea: -400,
|
|
170
|
+
maxBlankArea: 0,
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
it("only calls onCumulativeBlankAreaChange when values have a change", () => {
|
|
174
|
+
const onCumulativeBlankAreaChange = jest.fn();
|
|
175
|
+
const flashList = mountBlankTrackingFlashList({
|
|
176
|
+
onCumulativeBlankAreaChange,
|
|
177
|
+
});
|
|
178
|
+
flashList.instance.props.onBlankArea!({
|
|
179
|
+
blankArea: -200,
|
|
180
|
+
offsetEnd: -200,
|
|
181
|
+
offsetStart: 0,
|
|
182
|
+
});
|
|
183
|
+
jest.advanceTimersByTime(500);
|
|
184
|
+
flashList.instance.props.onBlankArea!({
|
|
185
|
+
blankArea: -200,
|
|
186
|
+
offsetEnd: -200,
|
|
187
|
+
offsetStart: 0,
|
|
188
|
+
});
|
|
189
|
+
expect(onCumulativeBlankAreaChange).toHaveBeenCalledTimes(0);
|
|
190
|
+
flashList.instance.props.onBlankArea!({
|
|
191
|
+
blankArea: -100,
|
|
192
|
+
offsetEnd: -100,
|
|
193
|
+
offsetStart: 0,
|
|
194
|
+
});
|
|
195
|
+
expect(onCumulativeBlankAreaChange).toHaveBeenCalledTimes(0);
|
|
196
|
+
flashList.instance.props.onBlankArea!({
|
|
197
|
+
blankArea: 100,
|
|
198
|
+
offsetEnd: 100,
|
|
199
|
+
offsetStart: 0,
|
|
200
|
+
});
|
|
201
|
+
expect(onCumulativeBlankAreaChange).toHaveBeenCalledTimes(1);
|
|
202
|
+
flashList.root.unmount();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This method can be used to trigger scroll events that can be forwarded to an element. Anything that implements scrollable can leverage this.
|
|
3
|
+
* @param scroll The scrollable element
|
|
4
|
+
* @param fromX The x offset to start from
|
|
5
|
+
* @param fromY The y offset to start from
|
|
6
|
+
* @param toX the x offset to end scroll at
|
|
7
|
+
* @param toY the y offset to end scroll at
|
|
8
|
+
* @param speedMultiplier the speed multiplier to use
|
|
9
|
+
* @param cancellable can be used to cancel the scroll
|
|
10
|
+
* @returns Promise that resolves when the scroll is complete
|
|
11
|
+
*/
|
|
12
|
+
export function autoScroll(
|
|
13
|
+
scroll: (x: number, y: number, animated: boolean) => void,
|
|
14
|
+
fromX: number,
|
|
15
|
+
fromY: number,
|
|
16
|
+
toX: number,
|
|
17
|
+
toY: number,
|
|
18
|
+
speedMultiplier = 1,
|
|
19
|
+
cancellable: Cancellable = new Cancellable()
|
|
20
|
+
): Promise<boolean> {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
scroll(fromX, fromY, false);
|
|
23
|
+
// Very fast scrolls on Android/iOS typically move content 7px every millisecond.
|
|
24
|
+
const incrementPerMs = 7 * speedMultiplier;
|
|
25
|
+
const directionMultiplierX = toX > fromX ? 1 : -1;
|
|
26
|
+
const directionMultiplierY = toY > fromY ? 1 : -1;
|
|
27
|
+
const comparatorX = toX > fromX ? Math.min : Math.max;
|
|
28
|
+
const comparatorY = toY > fromY ? Math.min : Math.max;
|
|
29
|
+
let startTime = Date.now();
|
|
30
|
+
let startX = fromX;
|
|
31
|
+
let startY = fromY;
|
|
32
|
+
// Computes the number of pixels to scroll in the given time
|
|
33
|
+
// Also invokes the scrollable to update the scroll position
|
|
34
|
+
const animationLoop = () => {
|
|
35
|
+
requestAnimationFrame(() => {
|
|
36
|
+
if (cancellable.isCancelled()) {
|
|
37
|
+
resolve(false);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const currentTime = Date.now();
|
|
41
|
+
const timeElapsed = currentTime - startTime;
|
|
42
|
+
const distanceToCover = incrementPerMs * timeElapsed;
|
|
43
|
+
startX += distanceToCover * directionMultiplierX;
|
|
44
|
+
startY += distanceToCover * directionMultiplierY;
|
|
45
|
+
scroll(comparatorX(toX, startX), comparatorY(toY, startY), false);
|
|
46
|
+
startTime = currentTime;
|
|
47
|
+
if (
|
|
48
|
+
comparatorX(toX, startX) !== toX ||
|
|
49
|
+
comparatorY(toY, startY) !== toY
|
|
50
|
+
) {
|
|
51
|
+
return animationLoop();
|
|
52
|
+
}
|
|
53
|
+
resolve(true);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
animationLoop();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class Cancellable {
|
|
61
|
+
public cancel() {
|
|
62
|
+
this._isCancelled = true;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public isCancelled(): boolean {
|
|
66
|
+
return this._isCancelled;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
public _isCancelled = false;
|
|
70
|
+
}
|