@applicaster/zapp-react-native-ui-components 14.0.0-alpha.8387612031 → 14.0.0-alpha.8557119261

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 (124) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/mobile/Layout.tsx +7 -4
  3. package/Components/AudioPlayer/mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap +1 -1
  4. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +1 -1
  5. package/Components/AudioPlayer/mobile/index.tsx +7 -12
  6. package/Components/AudioPlayer/tv/Artwork.tsx +3 -2
  7. package/Components/AudioPlayer/tv/Channel.tsx +7 -7
  8. package/Components/AudioPlayer/tv/Layout.tsx +100 -93
  9. package/Components/AudioPlayer/tv/Runtime.tsx +7 -1
  10. package/Components/AudioPlayer/tv/Summary.tsx +6 -2
  11. package/Components/AudioPlayer/tv/Title.tsx +6 -2
  12. package/Components/AudioPlayer/tv/__tests__/__snapshots__/Runtime.test.js.snap +2 -2
  13. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +21 -27
  14. package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +8 -17
  15. package/Components/AudioPlayer/tv/__tests__/__snapshots__/summary.test.js.snap +1 -2
  16. package/Components/AudioPlayer/tv/__tests__/__snapshots__/title.test.js.snap +1 -2
  17. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +4 -0
  18. package/Components/AudioPlayer/tv/helpers.tsx +10 -3
  19. package/Components/AudioPlayer/tv/index.tsx +9 -11
  20. package/Components/BaseFocusable/index.tsx +23 -12
  21. package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
  22. package/Components/Cell/index.js +7 -3
  23. package/Components/ComponentResolver/index.ts +1 -1
  24. package/Components/FeedLoader/FeedLoader.tsx +6 -15
  25. package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
  26. package/Components/FeedLoader/index.js +2 -8
  27. package/Components/Focusable/Focusable.tsx +5 -3
  28. package/Components/Focusable/FocusableTvOS.tsx +3 -3
  29. package/Components/Focusable/FocusableiOS.tsx +2 -2
  30. package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
  31. package/Components/Focusable/index.android.tsx +12 -8
  32. package/Components/Focusable/index.tsx +1 -1
  33. package/Components/FocusableList/index.tsx +4 -0
  34. package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
  35. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
  36. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +13 -10
  37. package/Components/HandlePlayable/HandlePlayable.tsx +25 -9
  38. package/Components/Layout/TV/LayoutBackground.tsx +1 -1
  39. package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
  40. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  41. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
  42. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -27
  43. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  44. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
  45. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
  46. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
  47. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
  48. package/Components/MasterCell/elementMapper.tsx +1 -2
  49. package/Components/MasterCell/index.tsx +1 -1
  50. package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
  51. package/Components/MasterCell/utils/index.ts +11 -5
  52. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  53. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  54. package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
  55. package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
  56. package/Components/PlayerContainer/PlayerContainer.tsx +41 -28
  57. package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
  58. package/Components/PlayerContainer/index.ts +1 -1
  59. package/Components/River/ComponentsMap/ComponentsMap.tsx +0 -1
  60. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
  61. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
  62. package/Components/River/RefreshControl.tsx +11 -17
  63. package/Components/River/TV/River.tsx +2 -17
  64. package/Components/River/TV/index.tsx +3 -1
  65. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  66. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  67. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  68. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  69. package/Components/River/__tests__/river.test.js +12 -26
  70. package/Components/River/index.tsx +1 -1
  71. package/Components/Screen/__tests__/Screen.test.tsx +28 -29
  72. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  73. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  74. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  75. package/Components/ScreenRevealManager/index.ts +1 -0
  76. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  77. package/Components/Tabs/TV/Tabs.android.tsx +0 -2
  78. package/Components/Tabs/Tabs.tsx +2 -3
  79. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  80. package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
  81. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  82. package/Components/VideoLive/animationUtils.ts +3 -3
  83. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +3 -9
  84. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -8
  85. package/Components/VideoModal/PlayerDetails.tsx +24 -2
  86. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  87. package/Components/VideoModal/VideoModal.tsx +3 -17
  88. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
  89. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  90. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
  91. package/Components/VideoModal/hooks/index.ts +0 -2
  92. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  93. package/Components/VideoModal/utils.ts +6 -0
  94. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  95. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  96. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  97. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  98. package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
  99. package/Contexts/ScreenContext/index.tsx +46 -6
  100. package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
  101. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  102. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
  103. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
  104. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
  105. package/Decorators/RiverFeedLoader/index.tsx +22 -4
  106. package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
  107. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
  108. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  109. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  110. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  111. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  112. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  113. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  114. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  115. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  116. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  117. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +266 -0
  118. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  119. package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
  120. package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
  121. package/events/index.ts +1 -0
  122. package/package.json +5 -10
  123. package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
  124. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
@@ -4,10 +4,6 @@ import * as React from "react";
4
4
  import { Text } from "react-native";
5
5
  import * as R from "ramda";
6
6
 
7
- import {
8
- useFeedLoader,
9
- useLayoutVersion,
10
- } from "@applicaster/zapp-react-native-utils/reactHooks";
11
7
  import { GeneralContentScreen } from "../../GeneralContentScreen";
12
8
  import { ScreenResolver } from "@applicaster/zapp-react-native-ui-components/Components/ScreenResolver";
13
9
  import { utilsLogger } from "@applicaster/zapp-react-native-utils/logger";
@@ -21,6 +17,7 @@ type Props = {
21
17
  screenId: string;
22
18
  screenData: ZappRiver | ZappEntry;
23
19
  feedUrl?: string;
20
+ feedData?: PipesDataObject["data"];
24
21
  extraProps?: any;
25
22
  screenResolverExtraProps?: any;
26
23
  componentsMapExtraProps?: any;
@@ -32,7 +29,7 @@ type Props = {
32
29
  export const River = (props: Props) => {
33
30
  const {
34
31
  screenId,
35
- feedUrl,
32
+ feedData,
36
33
  extraProps,
37
34
  screenResolverExtraProps,
38
35
  componentsMapExtraProps,
@@ -46,7 +43,6 @@ export const River = (props: Props) => {
46
43
  useSetNavbarState();
47
44
 
48
45
  const rivers = useRivers();
49
- const isV2 = useLayoutVersion({ isV2: true });
50
46
 
51
47
  const river = React.useMemo(() => rivers?.[screenId], [screenId]);
52
48
 
@@ -55,17 +51,6 @@ export const River = (props: Props) => {
55
51
  [screenId]
56
52
  );
57
53
 
58
- const connectedScreenUrl = React.useMemo(() => {
59
- // Avoid using feedUrl or content.src on layouts v2
60
- if (isV2) return null;
61
-
62
- return feedUrl || R.path(["content", "src"], screenData);
63
- }, [feedUrl, screenData]);
64
-
65
- const { data: feedData } = useFeedLoader({
66
- feedUrl: connectedScreenUrl,
67
- });
68
-
69
54
  const stringOrEmpty = (value: string | number | undefined): string =>
70
55
  R.isNil(value) ? "" : String(value);
71
56
 
@@ -2,8 +2,10 @@ import { compose } from "ramda";
2
2
  import { River as RiverComponent } from "./River";
3
3
  import { withTvEventHandler } from "./withTVEventHandler";
4
4
  import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
5
+ import { withRiverDataLoader } from "./withRiverDataLoader";
5
6
 
6
7
  export const River = compose(
7
8
  withTvEventHandler,
8
- withComponentsMapOffsetContext
9
+ withComponentsMapOffsetContext,
10
+ withRiverDataLoader
9
11
  )(RiverComponent);
@@ -0,0 +1,43 @@
1
+ import React, { useMemo } from "react";
2
+ import { path } from "ramda";
3
+
4
+ import {
5
+ useFeedLoader,
6
+ useRivers,
7
+ } from "@applicaster/zapp-react-native-utils/reactHooks";
8
+
9
+ type Props = {
10
+ screenId: string;
11
+ screenData: ZappRiver | ZappEntry;
12
+ feedUrl?: string;
13
+ river?: ZappRiver | ZappEntry;
14
+ };
15
+
16
+ export const withPipesV1DataLoader = (
17
+ WrappedComponent: React.ComponentType<any>
18
+ ) => {
19
+ return function WithPipesV1DataLoaderComponent(props: Props) {
20
+ const { screenId, feedUrl } = props;
21
+
22
+ const rivers = useRivers();
23
+
24
+ const river = React.useMemo(() => rivers?.[screenId], [screenId]);
25
+
26
+ const screenData = React.useMemo(
27
+ () => props.screenData || props.river || river,
28
+ [screenId]
29
+ );
30
+
31
+ const connectedScreenUrl = useMemo(() => {
32
+ // Avoid using feedUrl or content.src on layouts v2
33
+
34
+ return feedUrl || path(["content", "src"], screenData);
35
+ }, [feedUrl, screenData]);
36
+
37
+ const { data: feedData } = useFeedLoader({
38
+ feedUrl: connectedScreenUrl,
39
+ });
40
+
41
+ return <WrappedComponent {...props} feedData={feedData} />;
42
+ };
43
+ };
@@ -0,0 +1,17 @@
1
+ import { useLayoutVersion } from "@applicaster/zapp-react-native-utils/reactHooks";
2
+ import React from "react";
3
+ import { withPipesV1DataLoader } from "./withPipesV1DataLoader";
4
+
5
+ export const withRiverDataLoader = (
6
+ WrappedComponent: React.ComponentType<any>
7
+ ) => {
8
+ return function WithRiverDataLoaderComponent(props) {
9
+ const isV2 = useLayoutVersion({ isV2: true });
10
+
11
+ if (isV2) {
12
+ return <WrappedComponent {...props} />;
13
+ }
14
+
15
+ return withPipesV1DataLoader(WrappedComponent)(props);
16
+ };
17
+ };
@@ -8,7 +8,7 @@ export const withTvEventHandler = (Component) => {
8
8
  return function WithTVEventHandler(props) {
9
9
  const navigator = useNavigation();
10
10
 
11
- const remoteHandler = (_, event) => {
11
+ const remoteHandler = (event) => {
12
12
  const { eventType } = event;
13
13
 
14
14
  const canGoBack = navigator.canGoBack();
@@ -159,6 +159,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
159
159
  >
160
160
  <View>
161
161
  <View
162
+ onFocusCapture={[Function]}
162
163
  onLayout={[Function]}
163
164
  style={null}
164
165
  >
@@ -174,6 +175,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
174
175
  </View>
175
176
  </View>
176
177
  <View
178
+ onFocusCapture={[Function]}
177
179
  onLayout={[Function]}
178
180
  style={null}
179
181
  >
@@ -1,20 +1,10 @@
1
1
  import React from "react";
2
- import { render, screen } from "@testing-library/react-native";
3
- import configureStore from "redux-mock-store";
2
+ import { screen } from "@testing-library/react-native";
4
3
  import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
5
- import { Provider } from "react-redux";
6
4
 
7
- import { River } from "../";
8
5
  import { ScreenResolver } from "../../ScreenResolver";
9
6
  import { RiverComponent } from "../River";
10
7
 
11
- jest.mock(
12
- "@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive",
13
- () => ({
14
- useIsScreenActive: jest.fn(() => true),
15
- })
16
- );
17
-
18
8
  jest.mock(
19
9
  "@applicaster/zapp-react-native-ui-components/Components/GeneralContentScreen",
20
10
  () => {
@@ -46,6 +36,13 @@ jest.mock(
46
36
  })
47
37
  );
48
38
 
39
+ jest.mock(
40
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive",
41
+ () => ({
42
+ useIsScreenActive: jest.fn(() => true),
43
+ })
44
+ );
45
+
49
46
  const river = {
50
47
  home: true,
51
48
  id: "A1234",
@@ -89,28 +86,17 @@ const riverProps = {
89
86
 
90
87
  const appData = { layoutVersion: "v1" };
91
88
 
92
- const store = configureStore()({ rivers, appData });
89
+ const store = { rivers, appData };
93
90
 
94
91
  describe("When River has a general_content type", () => {
95
92
  it("renders GeneralContentScreen correctly", () => {
96
- const { getByTestId } = render(
97
- <Provider store={store}>
98
- <RiverComponent {...riverProps} />
99
- </Provider>
93
+ const { getByTestId } = renderWithProviders(
94
+ <RiverComponent {...riverProps} />,
95
+ store
100
96
  );
101
97
 
102
98
  expect(getByTestId("general-content-screen")).toBeTruthy();
103
99
  });
104
-
105
- it("renders River component correctly", () => {
106
- const wrapper = render(
107
- <Provider store={store}>
108
- <River screenId="A1234" />
109
- </Provider>
110
- );
111
-
112
- expect(wrapper.toJSON()).toMatchSnapshot();
113
- });
114
100
  });
115
101
 
116
102
  describe("When River has any other type other than general_content", () => {
@@ -1,5 +1,5 @@
1
1
  import * as R from "ramda";
2
- import { connectToStore } from "@applicaster/zapp-react-native-redux";
2
+ import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
3
3
 
4
4
  import { withRiver } from "../../Decorators/RiverResolver";
5
5
  import { withNavigator } from "../../Decorators/Navigator";
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { View } from "react-native";
3
- import { act, create } from "react-test-renderer";
3
+ import { render } from "@testing-library/react-native";
4
4
 
5
5
  const Mocked_RouteManager = jest.fn((props) => (
6
6
  <View testID="routeManager" {...props} />
@@ -68,12 +68,33 @@ jest.mock(
68
68
  })
69
69
  );
70
70
 
71
+ jest.mock(
72
+ "@applicaster/zapp-react-native-utils/reactHooks/state/useRivers",
73
+ () => ({
74
+ useRivers: jest.fn(() => ({
75
+ rivers: [{ id: "testId", name: "Test River" }],
76
+ })),
77
+ })
78
+ );
79
+
71
80
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
72
81
  isNavBarVisible: mockIsNavBarVisible,
73
82
  useIsScreenActive: jest.fn().mockReturnValue(true),
83
+ useNavigation: jest.fn(() => ({
84
+ canGoBack: () => false,
85
+ currentRoute: "/river/testId",
86
+ activeRiver: { id: "testId" },
87
+ screenData: { id: "testId" },
88
+ data: { screen: { id: "testId" } },
89
+ })),
90
+ useRoute: jest.fn(() => ({
91
+ pathname: "/river/testId",
92
+ screenData: { id: "testId" },
93
+ })),
74
94
  }));
75
95
 
76
96
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
97
+ ...jest.requireActual("@applicaster/zapp-react-native-utils/reactHooks"),
77
98
  useCurrentScreenData: jest.fn(() => ({
78
99
  id: "testId",
79
100
  })),
@@ -84,17 +105,6 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
84
105
  id: "testId",
85
106
  navigations: [{ id: "testId", category: "nav_bar" }],
86
107
  })),
87
- useNavigation: jest.fn(() => ({
88
- canGoBack: () => false,
89
- currentRoute: "/river/testId",
90
- activeRiver: { id: "testId" },
91
- screenData: { id: "testId" },
92
- data: { screen: { id: "testId" } },
93
- })),
94
- useRoute: jest.fn(() => ({
95
- pathname: "/river/testId",
96
- screenData: { id: "testId" },
97
- })),
98
108
  useDimensions: jest.fn(() => ({
99
109
  width: 1920,
100
110
  height: 1080,
@@ -102,10 +112,11 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
102
112
  useIsTablet: jest.fn(() => false),
103
113
  }));
104
114
 
105
- jest.mock("@applicaster/zapp-react-native-redux/hooks/usePickFromState", () => {
115
+ jest.mock("@applicaster/zapp-react-native-redux/hooks", () => {
106
116
  const View = jest.requireActual("react-native").View;
107
117
 
108
118
  return {
119
+ ...jest.requireActual("@applicaster/zapp-react-native-redux/hooks"),
109
120
  usePickFromState: () => ({
110
121
  plugins: [
111
122
  {
@@ -141,8 +152,6 @@ const screenProps = {
141
152
  const { Screen } = require("..");
142
153
 
143
154
  describe("<Screen Component />", () => {
144
- let wrapper;
145
-
146
155
  beforeEach(() => {
147
156
  allowedOrientationsForScreen.mockClear();
148
157
  getOrientation.mockClear();
@@ -150,26 +159,16 @@ describe("<Screen Component />", () => {
150
159
  });
151
160
 
152
161
  describe("when the navbar should show", () => {
153
- act(() => {
154
- wrapper = create(<Screen {...screenProps} />);
155
- });
156
-
157
162
  it("renders correctly", () => {
158
- expect(wrapper.toJSON()).toMatchSnapshot();
163
+ const { toJSON } = render(<Screen {...screenProps} />);
164
+ expect(toJSON()).toMatchSnapshot();
159
165
  });
160
166
  });
161
167
 
162
168
  describe("when the navbar should be hidden", () => {
163
- beforeEach(() => {
164
- wrapper = create(<Screen {...screenProps} />);
165
- });
166
-
167
169
  it("renders correctly", () => {
168
- act(() => {
169
- wrapper = create(<Screen {...screenProps} />);
170
- });
171
-
172
- expect(wrapper.toJSON()).toMatchSnapshot();
170
+ const { toJSON } = render(<Screen {...screenProps} />);
171
+ expect(toJSON()).toMatchSnapshot();
173
172
  });
174
173
  });
175
174
  });
@@ -0,0 +1,76 @@
1
+ import { makeListOf } from "@applicaster/zapp-react-native-utils/arrayUtils";
2
+ import { isFirstComponentGallery } from "@applicaster/zapp-react-native-utils/componentsUtils";
3
+ import { once } from "ramda";
4
+
5
+ const INITIAL_NUMBER_TO_LOAD = 3;
6
+
7
+ // Infer the values of COMPONENT_LOADING_STATE as a type
8
+ type ComponentLoadingState =
9
+ (typeof COMPONENT_LOADING_STATE)[keyof typeof COMPONENT_LOADING_STATE];
10
+
11
+ export const COMPONENT_LOADING_STATE = {
12
+ UNKNOWN: "UNKNOWN",
13
+ LOADED_WITH_SUCCESS: "LOADED_WITH_SUCCESS",
14
+ LOADED_WITH_FAILURE: "LOADED_WITH_FAILURE",
15
+ } as const;
16
+
17
+ // Function to get the number of loaded components
18
+ const getNumberOfLoaded = (states: ComponentLoadingState[]): number => {
19
+ return states.filter((value) => value !== COMPONENT_LOADING_STATE.UNKNOWN)
20
+ .length;
21
+ };
22
+
23
+ const getNumberOfComponentsWaitToLoadBeforePresent = (
24
+ componentsToRender: ZappUIComponent[]
25
+ ): number => {
26
+ // when Gallery is the first component, no need to wait the others
27
+ if (isFirstComponentGallery(componentsToRender)) {
28
+ return 1;
29
+ }
30
+
31
+ return Math.min(INITIAL_NUMBER_TO_LOAD, componentsToRender.length);
32
+ };
33
+
34
+ export class ScreenRevealManager {
35
+ public numberOfComponentsWaitToLoadBeforePresent: number;
36
+ private renderingState: Array<ComponentLoadingState>;
37
+ private callback: Callback;
38
+
39
+ constructor(componentsToRender: ZappUIComponent[], callback: Callback) {
40
+ this.numberOfComponentsWaitToLoadBeforePresent =
41
+ getNumberOfComponentsWaitToLoadBeforePresent(componentsToRender);
42
+
43
+ this.renderingState = makeListOf<ComponentLoadingState>(
44
+ COMPONENT_LOADING_STATE.UNKNOWN,
45
+ this.numberOfComponentsWaitToLoadBeforePresent
46
+ );
47
+
48
+ this.callback = once(callback);
49
+ }
50
+
51
+ onLoadFinished = (index: number): void => {
52
+ this.renderingState[index] = COMPONENT_LOADING_STATE.LOADED_WITH_SUCCESS;
53
+
54
+ if (
55
+ getNumberOfLoaded(this.renderingState) >=
56
+ this.numberOfComponentsWaitToLoadBeforePresent
57
+ ) {
58
+ this.setIsReadyToShow();
59
+ }
60
+ };
61
+
62
+ onLoadFailed = (index: number): void => {
63
+ this.renderingState[index] = COMPONENT_LOADING_STATE.LOADED_WITH_FAILURE;
64
+
65
+ if (
66
+ getNumberOfLoaded(this.renderingState) >=
67
+ this.numberOfComponentsWaitToLoadBeforePresent
68
+ ) {
69
+ this.setIsReadyToShow();
70
+ }
71
+ };
72
+
73
+ setIsReadyToShow = (): void => {
74
+ this.callback();
75
+ };
76
+ }
@@ -0,0 +1,107 @@
1
+ import {
2
+ ScreenRevealManager,
3
+ COMPONENT_LOADING_STATE,
4
+ } from "../ScreenRevealManager";
5
+
6
+ describe("ScreenRevealManager", () => {
7
+ const mockCallback = jest.fn();
8
+
9
+ beforeEach(() => {
10
+ jest.clearAllMocks();
11
+ });
12
+
13
+ it("should initialize with the correct number of components to wait for", () => {
14
+ const componentsToRender: ZappUIComponent[] = [
15
+ { component_type: "component1" },
16
+ { component_type: "component2" },
17
+ { component_type: "component3" },
18
+ ];
19
+
20
+ const manager = new ScreenRevealManager(componentsToRender, mockCallback);
21
+
22
+ expect(manager["numberOfComponentsWaitToLoadBeforePresent"]).toBe(3);
23
+
24
+ expect(manager["renderingState"]).toEqual([
25
+ COMPONENT_LOADING_STATE.UNKNOWN,
26
+ COMPONENT_LOADING_STATE.UNKNOWN,
27
+ COMPONENT_LOADING_STATE.UNKNOWN,
28
+ ]);
29
+ });
30
+
31
+ it("should call the callback when the required number of components are loaded successfully", () => {
32
+ const componentsToRender: ZappUIComponent[] = [
33
+ { component_type: "component1" },
34
+ { component_type: "component2" },
35
+ { component_type: "component3" },
36
+ ];
37
+
38
+ const manager = new ScreenRevealManager(componentsToRender, mockCallback);
39
+
40
+ manager.onLoadFinished(0);
41
+ manager.onLoadFinished(1);
42
+ manager.onLoadFinished(2);
43
+
44
+ expect(mockCallback).toHaveBeenCalledTimes(1);
45
+ });
46
+
47
+ it("should call the callback when the required number of components fail to load", () => {
48
+ const componentsToRender: ZappUIComponent[] = [
49
+ { component_type: "component1" },
50
+ { component_type: "component2" },
51
+ { component_type: "component3" },
52
+ ];
53
+
54
+ const manager = new ScreenRevealManager(componentsToRender, mockCallback);
55
+
56
+ manager.onLoadFailed(0);
57
+ manager.onLoadFailed(1);
58
+ manager.onLoadFailed(2);
59
+
60
+ expect(mockCallback).toHaveBeenCalledTimes(1);
61
+ });
62
+
63
+ it("should call the callback when a mix of successful and failed loads meet the required number", () => {
64
+ const componentsToRender: ZappUIComponent[] = [
65
+ { component_type: "component1" },
66
+ { component_type: "component2" },
67
+ { component_type: "component3" },
68
+ ];
69
+
70
+ const manager = new ScreenRevealManager(componentsToRender, mockCallback);
71
+
72
+ manager.onLoadFinished(0);
73
+ manager.onLoadFailed(1);
74
+ manager.onLoadFinished(2);
75
+
76
+ expect(mockCallback).toHaveBeenCalledTimes(1);
77
+ });
78
+
79
+ it("should not call the callback if the required number of components are not loaded", () => {
80
+ const componentsToRender: ZappUIComponent[] = [
81
+ { component_type: "component1" },
82
+ { component_type: "component2" },
83
+ { component_type: "component3" },
84
+ ];
85
+
86
+ const manager = new ScreenRevealManager(componentsToRender, mockCallback);
87
+
88
+ manager.onLoadFinished(0);
89
+ manager.onLoadFailed(1);
90
+
91
+ expect(mockCallback).not.toHaveBeenCalled();
92
+ });
93
+
94
+ it("should call the callback when the when first component is gallery and it was loaded successfully", () => {
95
+ const componentsToRender: ZappUIComponent[] = [
96
+ { component_type: "gallery-qb" },
97
+ { component_type: "component2" },
98
+ { component_type: "component3" },
99
+ ];
100
+
101
+ const manager = new ScreenRevealManager(componentsToRender, mockCallback);
102
+
103
+ manager.onLoadFinished(0);
104
+
105
+ expect(mockCallback).toHaveBeenCalledTimes(1);
106
+ });
107
+ });
@@ -0,0 +1,96 @@
1
+ /* eslint-disable react/prop-types */
2
+
3
+ import * as React from "react";
4
+ import { render, screen, act } from "@testing-library/react-native";
5
+ import { View } from "react-native";
6
+ import {
7
+ withScreenRevealManager,
8
+ SHOWN,
9
+ TIMEOUT,
10
+ } from "../withScreenRevealManager";
11
+
12
+ // jest.mock("react-native/Libraries/Animated/NativeAnimatedHelper");
13
+
14
+ const MockComponent = ({
15
+ initialNumberToLoad,
16
+ onLoadFinishedFromScreenRevealManager,
17
+ onLoadFailedFromScreenRevealManager,
18
+ }) => {
19
+ React.useEffect(() => {
20
+ // Simulate loading components
21
+ for (let i = 0; i < initialNumberToLoad; i++) {
22
+ onLoadFinishedFromScreenRevealManager(i);
23
+ }
24
+ }, [initialNumberToLoad, onLoadFinishedFromScreenRevealManager]);
25
+
26
+ return (
27
+ <View
28
+ testID="mock-component"
29
+ initialNumberToLoad={initialNumberToLoad}
30
+ onLoadFinishedFromScreenRevealManager={
31
+ onLoadFinishedFromScreenRevealManager
32
+ }
33
+ onLoadFailedFromScreenRevealManager={onLoadFailedFromScreenRevealManager}
34
+ />
35
+ );
36
+ };
37
+
38
+ const WrappedComponent = withScreenRevealManager(MockComponent);
39
+
40
+ describe.skip("withScreenRevealManager", () => {
41
+ beforeEach(() => {
42
+ jest.clearAllMocks();
43
+ jest.useFakeTimers();
44
+ });
45
+
46
+ afterEach(() => {
47
+ jest.runOnlyPendingTimers();
48
+ jest.useRealTimers();
49
+ });
50
+
51
+ it("should render the wrapped component", () => {
52
+ render(
53
+ <WrappedComponent
54
+ componentsToRender={[{ id: "1" }, { id: "2" }, { id: "3" }]}
55
+ />
56
+ );
57
+
58
+ expect(screen.getByTestId("mock-component")).toBeTruthy();
59
+ });
60
+
61
+ it("should animate opacity when ready to show", () => {
62
+ render(
63
+ <WrappedComponent
64
+ componentsToRender={[{ id: "1" }, { id: "2" }, { id: "3" }]}
65
+ />
66
+ );
67
+
68
+ const animatedView = screen.getByTestId("animated-component");
69
+
70
+ act(() => {
71
+ jest.advanceTimersByTime(TIMEOUT + 100);
72
+ });
73
+
74
+ expect(animatedView.props.style.opacity).toBe(SHOWN);
75
+ });
76
+
77
+ it("should pass initialNumberToLoad, onLoadFinishedFromScreenRevealManager, and onLoadFailedFromScreenRevealManager to the wrapped component", () => {
78
+ render(
79
+ <WrappedComponent
80
+ componentsToRender={[{ id: "1" }, { id: "2" }, { id: "3" }]}
81
+ />
82
+ );
83
+
84
+ const mockComponent = screen.getByTestId("mock-component");
85
+
86
+ expect(mockComponent.props.initialNumberToLoad).toBe(3);
87
+
88
+ expect(
89
+ mockComponent.props.onLoadFinishedFromScreenRevealManager
90
+ ).toBeInstanceOf(Function);
91
+
92
+ expect(
93
+ mockComponent.props.onLoadFailedFromScreenRevealManager
94
+ ).toBeInstanceOf(Function);
95
+ });
96
+ });
@@ -0,0 +1 @@
1
+ export { withScreenRevealManager } from "./withScreenRevealManager";