@applicaster/zapp-react-native-ui-components 16.0.0-rc.1 → 16.0.0-rc.100

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 (169) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/BackgroundImage/BackgroundImage.android.tv.tsx +28 -0
  5. package/Components/BackgroundImage/BackgroundImage.ios.tv.tsx +24 -0
  6. package/Components/BackgroundImage/BackgroundImage.tsx +42 -0
  7. package/Components/BackgroundImage/index.ts +1 -0
  8. package/Components/Cell/Cell.tsx +3 -6
  9. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  10. package/Components/CellRendererResolver/index.ts +2 -2
  11. package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
  12. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  13. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  14. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  15. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  16. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  17. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  18. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  19. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  20. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  21. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  22. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  23. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  24. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
  25. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  26. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  27. package/Components/GeneralContentScreen/index.ts +2 -0
  28. package/Components/Layout/FullWidthRow.tsx +38 -0
  29. package/Components/Layout/TV/ScreenContainer.tsx +5 -0
  30. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +0 -1
  31. package/Components/Layout/TV/index.tsx +3 -4
  32. package/Components/Layout/TV/index.web.tsx +2 -3
  33. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  34. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +21 -6
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  39. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  40. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +1 -1
  41. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  42. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  43. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  44. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  45. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  46. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  47. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  48. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  49. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  50. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  51. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  52. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  53. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  54. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  55. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  56. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
  57. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  58. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  59. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  60. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  61. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  62. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  63. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  64. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  65. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  66. package/Components/MasterCell/dataAdapter.ts +15 -5
  67. package/Components/MasterCell/hooks/index.ts +2 -0
  68. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  69. package/Components/MasterCell/index.tsx +30 -5
  70. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  71. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  72. package/Components/MasterCell/utils/index.ts +85 -46
  73. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  74. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  75. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  76. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  77. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  78. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  79. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  80. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  81. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1007 -0
  82. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  83. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  84. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  85. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  86. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  87. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  88. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  89. package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -50
  90. package/Components/ModalComponent/Button/index.tsx +5 -3
  91. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  92. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  93. package/Components/ModalComponent/Header/index.tsx +20 -7
  94. package/Components/ModalComponent/Header/utils.ts +1 -1
  95. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  96. package/Components/ModalComponent/SortableList.tsx +156 -0
  97. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  98. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  99. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  100. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  101. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  102. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  103. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  104. package/Components/ModalComponent/index.tsx +18 -9
  105. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  106. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  107. package/Components/ModalComponent/presets/index.ts +27 -0
  108. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  109. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  110. package/Components/ModalComponent/presets/types.ts +35 -0
  111. package/Components/ModalComponent/showTextInput.ts +52 -0
  112. package/Components/ModalComponent/utils.ts +82 -21
  113. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  114. package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
  115. package/Components/OfflineHandler/hooks.ts +111 -0
  116. package/Components/OfflineHandler/index.tsx +51 -73
  117. package/Components/OfflineHandler/utils/index.ts +1 -13
  118. package/Components/PlayerContainer/PlayerContainer.tsx +25 -17
  119. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  120. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  121. package/Components/PreloaderWrapper/index.tsx +1 -1
  122. package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -61
  123. package/Components/River/River.tsx +4 -0
  124. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -89
  125. package/Components/River/__tests__/componentsMap.test.js +8 -3
  126. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  127. package/Components/Screen/TV/hooks/__tests__/useAfterPaint.test.ts +60 -0
  128. package/Components/Screen/TV/hooks/index.ts +2 -0
  129. package/Components/Screen/TV/hooks/useAfterPaint.ts +23 -0
  130. package/Components/Screen/TV/index.web.tsx +16 -7
  131. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  132. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  133. package/Components/Screen/index.tsx +13 -4
  134. package/Components/Screen/navigationHandler.ts +8 -8
  135. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  136. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  137. package/Components/ScreenRevealManager/Overlay.tsx +34 -0
  138. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  139. package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
  140. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  141. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  142. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +29 -23
  143. package/Components/TopCutoffOverlay/index.tsx +2 -2
  144. package/Components/Transitioner/Transitioner.tsx +28 -17
  145. package/Components/VideoLive/LiveImageManager.ts +65 -48
  146. package/Components/VideoLive/PlayerLiveImageComponent.tsx +4 -2
  147. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  148. package/Components/VideoModal/utils.ts +6 -1
  149. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  150. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  151. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  152. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  153. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  154. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  155. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  156. package/Helpers/index.js +7 -40
  157. package/Hooks/useEntryActionsExpander.ts +63 -0
  158. package/assets/empty-Info.png +0 -0
  159. package/package.json +6 -5
  160. package/Components/Layout/TV/LayoutBackground.tsx +0 -31
  161. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  162. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  163. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  164. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  165. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  166. package/Components/Screen/utils.ts +0 -16
  167. package/Helpers/Analytics/index.js +0 -95
  168. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  169. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -10,103 +10,129 @@ exports[`componentsMap renders renders components map correctly 1`] = `
10
10
  >
11
11
  <View
12
12
  onLayout={[Function]}
13
- style={
14
- {
15
- "flex": 1,
16
- }
17
- }
13
+ style={{}}
18
14
  >
19
15
  <RCTScrollView
20
16
  ListFooterComponent={
21
- <Memo(RiverFooterComponent)
22
- flatListHeight={null}
23
- loadingState={
24
- BehaviorSubject {
25
- "_value": {
26
- "done": false,
27
- "index": -1,
28
- "waitForAllComponents": false,
29
- },
30
- "closed": false,
31
- "currentObservers": null,
32
- "hasError": false,
33
- "isStopped": false,
34
- "observers": [
35
- SafeSubscriber {
36
- "_finalizers": [
37
- Subscription {
38
- "_finalizers": null,
39
- "_parentage": [Circular],
40
- "closed": false,
41
- "initialTeardown": [Function],
42
- },
43
- ],
44
- "_parentage": null,
45
- "closed": false,
46
- "destination": ConsumerObserver {
47
- "partialObserver": {
48
- "complete": undefined,
49
- "error": undefined,
50
- "next": [Function],
51
- },
52
- },
53
- "initialTeardown": undefined,
54
- "isStopped": false,
17
+ <React.Fragment>
18
+ <Memo(RiverFooterComponent)
19
+ flatListHeight={null}
20
+ loadingState={
21
+ BehaviorSubject {
22
+ "_value": {
23
+ "done": false,
24
+ "index": -1,
25
+ "waitForAllComponents": false,
55
26
  },
56
- SafeSubscriber {
57
- "_finalizers": [
58
- Subscription {
59
- "_finalizers": null,
60
- "_parentage": [Circular],
61
- "closed": false,
62
- "initialTeardown": [Function],
63
- },
64
- ],
65
- "_parentage": null,
66
- "closed": false,
67
- "destination": ConsumerObserver {
68
- "partialObserver": {
69
- "complete": undefined,
70
- "error": undefined,
71
- "next": [Function],
27
+ "closed": false,
28
+ "currentObservers": null,
29
+ "hasError": false,
30
+ "isStopped": false,
31
+ "observers": [
32
+ SafeSubscriber {
33
+ "_finalizers": [
34
+ Subscription {
35
+ "_finalizers": null,
36
+ "_parentage": [Circular],
37
+ "closed": false,
38
+ "initialTeardown": [Function],
39
+ },
40
+ ],
41
+ "_parentage": null,
42
+ "closed": false,
43
+ "destination": ConsumerObserver {
44
+ "partialObserver": {
45
+ "complete": undefined,
46
+ "error": undefined,
47
+ "next": [Function],
48
+ },
72
49
  },
50
+ "initialTeardown": undefined,
51
+ "isStopped": false,
73
52
  },
74
- "initialTeardown": undefined,
75
- "isStopped": false,
76
- },
77
- SafeSubscriber {
78
- "_finalizers": [
79
- Subscription {
80
- "_finalizers": null,
81
- "_parentage": [Circular],
82
- "closed": false,
83
- "initialTeardown": [Function],
53
+ SafeSubscriber {
54
+ "_finalizers": [
55
+ Subscription {
56
+ "_finalizers": null,
57
+ "_parentage": [Circular],
58
+ "closed": false,
59
+ "initialTeardown": [Function],
60
+ },
61
+ ],
62
+ "_parentage": null,
63
+ "closed": false,
64
+ "destination": ConsumerObserver {
65
+ "partialObserver": {
66
+ "complete": undefined,
67
+ "error": undefined,
68
+ "next": [Function],
69
+ },
84
70
  },
85
- ],
86
- "_parentage": null,
87
- "closed": false,
88
- "destination": ConsumerObserver {
89
- "partialObserver": {
90
- "complete": undefined,
91
- "error": undefined,
92
- "next": [Function],
71
+ "initialTeardown": undefined,
72
+ "isStopped": false,
73
+ },
74
+ SafeSubscriber {
75
+ "_finalizers": [
76
+ Subscription {
77
+ "_finalizers": null,
78
+ "_parentage": [Circular],
79
+ "closed": false,
80
+ "initialTeardown": [Function],
81
+ },
82
+ ],
83
+ "_parentage": null,
84
+ "closed": false,
85
+ "destination": ConsumerObserver {
86
+ "partialObserver": {
87
+ "complete": undefined,
88
+ "error": undefined,
89
+ "next": [Function],
90
+ },
93
91
  },
92
+ "initialTeardown": undefined,
93
+ "isStopped": false,
94
94
  },
95
- "initialTeardown": undefined,
96
- "isStopped": false,
97
- },
98
- ],
99
- "thrownError": null,
95
+ ],
96
+ "thrownError": null,
97
+ }
98
+ }
99
+ />
100
+ <View
101
+ edges={
102
+ {
103
+ "bottom": "off",
104
+ }
105
+ }
106
+ mode="margin"
107
+ style={
108
+ {
109
+ "height": 0.5,
110
+ "paddingBottom": undefined,
111
+ }
112
+ }
113
+ />
114
+ </React.Fragment>
115
+ }
116
+ ListHeaderComponent={
117
+ <View
118
+ edges={
119
+ {
120
+ "top": "off",
121
+ }
122
+ }
123
+ mode="margin"
124
+ style={
125
+ {
126
+ "height": 0.5,
127
+ "paddingTop": undefined,
100
128
  }
101
129
  }
102
130
  />
103
131
  }
104
132
  contentContainerStyle={
105
133
  {
106
- "paddingBottom": undefined,
107
134
  "paddingLeft": undefined,
108
135
  "paddingRight": undefined,
109
- "paddingTop": undefined,
110
136
  }
111
137
  }
112
138
  data={
@@ -161,17 +187,32 @@ exports[`componentsMap renders renders components map correctly 1`] = `
161
187
  >
162
188
  <View>
163
189
  <View
164
- onFocusCapture={[Function]}
190
+ collapsable={false}
165
191
  onLayout={[Function]}
166
- style={null}
167
192
  >
168
193
  <View
169
- onLayout={[Function]}
194
+ edges={
195
+ {
196
+ "top": "off",
197
+ }
198
+ }
199
+ mode="margin"
170
200
  style={
171
201
  {
172
- "flex": 1,
202
+ "height": 0.5,
203
+ "paddingTop": undefined,
173
204
  }
174
205
  }
206
+ />
207
+ </View>
208
+ <View
209
+ onFocusCapture={[Function]}
210
+ onLayout={[Function]}
211
+ style={null}
212
+ >
213
+ <View
214
+ onLayout={[Function]}
215
+ style={{}}
175
216
  >
176
217
  <View />
177
218
  </View>
@@ -183,11 +224,7 @@ exports[`componentsMap renders renders components map correctly 1`] = `
183
224
  >
184
225
  <View
185
226
  onLayout={[Function]}
186
- style={
187
- {
188
- "flex": 1,
189
- }
190
- }
227
+ style={{}}
191
228
  />
192
229
  </View>
193
230
  <View
@@ -212,6 +249,20 @@ exports[`componentsMap renders renders components map correctly 1`] = `
212
249
  size="large"
213
250
  />
214
251
  </View>
252
+ <View
253
+ edges={
254
+ {
255
+ "bottom": "off",
256
+ }
257
+ }
258
+ mode="margin"
259
+ style={
260
+ {
261
+ "height": 0.5,
262
+ "paddingBottom": undefined,
263
+ }
264
+ }
265
+ />
215
266
  </View>
216
267
  </View>
217
268
  </RCTScrollView>
@@ -11,9 +11,14 @@ jest.mock(
11
11
  () => () => null
12
12
  );
13
13
 
14
- jest.mock("react-native-safe-area-context", () => ({
15
- useSafeAreaInsets: () => ({ top: 50 }),
16
- }));
14
+ jest.mock("react-native-safe-area-context", () => {
15
+ const { View } = jest.requireActual("react-native");
16
+
17
+ return {
18
+ SafeAreaView: View,
19
+ useSafeAreaInsets: () => ({ top: 50 }),
20
+ };
21
+ });
17
22
 
18
23
  jest.mock("react-native", () => {
19
24
  const RN = jest.genMockFromModule("react-native");
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+
4
+ import { RiverComponent } from "../River";
5
+
6
+ const mockResolverProps: any[] = [];
7
+
8
+ jest.mock("../../ScreenResolver", () => ({
9
+ ScreenResolver: (props: any) => {
10
+ mockResolverProps.push(props);
11
+
12
+ return null;
13
+ },
14
+ }));
15
+
16
+ const river = { id: "river-parent-lock", type: "parent_lock" } as any;
17
+
18
+ const baseProps = {
19
+ river,
20
+ screenData: {},
21
+ groupId: "group-1",
22
+ scrollViewExtraProps: {},
23
+ setIsScreenWrappedInContainer: jest.fn(),
24
+ screenContext: { navBar: { setTitle: jest.fn(), setSummary: jest.fn() } },
25
+ } as any;
26
+
27
+ const resolved = () => mockResolverProps[mockResolverProps.length - 1];
28
+
29
+ describe("RiverComponent", () => {
30
+ beforeEach(() => {
31
+ mockResolverProps.length = 0;
32
+ });
33
+
34
+ it("forwards resultCallback to the screen it resolves", () => {
35
+ const resultCallback = jest.fn();
36
+
37
+ render(<RiverComponent {...baseProps} resultCallback={resultCallback} />);
38
+
39
+ expect(resolved().resultCallback).toBe(resultCallback);
40
+ });
41
+
42
+ it("resolves the screen without a resultCallback when none was given", () => {
43
+ render(<RiverComponent {...baseProps} />);
44
+
45
+ expect(resolved().resultCallback).toBeUndefined();
46
+ });
47
+ });
@@ -0,0 +1,60 @@
1
+ import { renderHook, act } from "@testing-library/react-native";
2
+
3
+ import { useAfterPaint } from "../useAfterPaint";
4
+
5
+ describe("useAfterPaint", () => {
6
+ let frameCallbacks: FrameRequestCallback[];
7
+ let originalRaf: typeof requestAnimationFrame;
8
+ let originalCancelRaf: typeof cancelAnimationFrame;
9
+
10
+ const flushFrame = () => {
11
+ const callbacks = frameCallbacks;
12
+ frameCallbacks = [];
13
+
14
+ act(() => {
15
+ callbacks.forEach((cb) => cb(0));
16
+ });
17
+ };
18
+
19
+ beforeEach(() => {
20
+ frameCallbacks = [];
21
+ originalRaf = global.requestAnimationFrame;
22
+ originalCancelRaf = global.cancelAnimationFrame;
23
+
24
+ global.requestAnimationFrame = jest.fn((cb: FrameRequestCallback) => {
25
+ frameCallbacks.push(cb);
26
+
27
+ return frameCallbacks.length;
28
+ }) as unknown as typeof requestAnimationFrame;
29
+
30
+ global.cancelAnimationFrame = jest.fn();
31
+ });
32
+
33
+ afterEach(() => {
34
+ global.requestAnimationFrame = originalRaf;
35
+ global.cancelAnimationFrame = originalCancelRaf;
36
+ });
37
+
38
+ it("returns false on the initial render", () => {
39
+ const { result } = renderHook(() => useAfterPaint());
40
+
41
+ expect(result.current).toBe(false);
42
+ });
43
+
44
+ it("stays false after only one animation frame (before paint completes)", () => {
45
+ const { result } = renderHook(() => useAfterPaint());
46
+
47
+ flushFrame();
48
+
49
+ expect(result.current).toBe(false);
50
+ });
51
+
52
+ it("returns true after two animation frames (a real post-paint boundary)", () => {
53
+ const { result } = renderHook(() => useAfterPaint());
54
+
55
+ flushFrame();
56
+ flushFrame();
57
+
58
+ expect(result.current).toBe(true);
59
+ });
60
+ });
@@ -1 +1,3 @@
1
1
  export { useInitialFocus } from "./useInitialFocus";
2
+
3
+ export { useAfterPaint } from "./useAfterPaint";
@@ -0,0 +1,23 @@
1
+ import * as React from "react";
2
+
3
+ export const useAfterPaint = (): boolean => {
4
+ const [painted, setPainted] = React.useState(false);
5
+
6
+ React.useEffect(() => {
7
+ let secondFrame: number | undefined;
8
+
9
+ const firstFrame = requestAnimationFrame(() => {
10
+ secondFrame = requestAnimationFrame(() => setPainted(true));
11
+ });
12
+
13
+ return () => {
14
+ cancelAnimationFrame(firstFrame);
15
+
16
+ if (secondFrame !== undefined) {
17
+ cancelAnimationFrame(secondFrame);
18
+ }
19
+ };
20
+ }, []);
21
+
22
+ return painted;
23
+ };
@@ -21,7 +21,7 @@ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/he
21
21
 
22
22
  import { NavBarContainer } from "../../Layout/TV/NavBarContainer";
23
23
  import { ScreenResolver } from "../../ScreenResolver";
24
- import { useInitialFocus } from "./hooks";
24
+ import { useInitialFocus, useAfterPaint } from "./hooks";
25
25
  import { isPlayerPlugin } from "@applicaster/zapp-react-native-utils/pluginUtils";
26
26
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils";
27
27
  import { FreezeWithCallback } from "../../FreezeWithCallback";
@@ -157,6 +157,13 @@ export const Screen = ({ route, Components }: Props) => {
157
157
 
158
158
  const isScreenActive = useIsScreenActive();
159
159
 
160
+ // We need to render menu first and then proceed with screen content,
161
+ // otherwise screen will stay black until everything is loaded and screen
162
+ // rendering put huge load the CPU pushing rendering even further.
163
+ // With this approach, menu will be rendered immediately and screen content
164
+ // will be rendered after paint, which makes it more responsive and prevents black screen.
165
+ const isContentReady = useAfterPaint();
166
+
160
167
  return (
161
168
  <FreezeWithCallback freeze={!isScreenActive} onRelease={onRelease}>
162
169
  <View style={[styles.container, { backgroundColor }]}>
@@ -167,12 +174,14 @@ export const Screen = ({ route, Components }: Props) => {
167
174
  navigationProps={navigationProps}
168
175
  />
169
176
  </NavBarContainer>
170
- <ScreenResolver
171
- screenType={screenType}
172
- screenId={screenId}
173
- screenData={screenData}
174
- groupId={route}
175
- />
177
+ {isContentReady ? (
178
+ <ScreenResolver
179
+ screenType={screenType}
180
+ screenId={screenId}
181
+ screenData={screenData}
182
+ groupId={route}
183
+ />
184
+ ) : null}
176
185
  </View>
177
186
  </FreezeWithCallback>
178
187
  );
@@ -141,6 +141,10 @@ const {
141
141
  useGetScreenOrientation,
142
142
  } = require("@applicaster/zapp-react-native-utils/appUtils/orientationHelper");
143
143
 
144
+ const {
145
+ useIsScreenActive,
146
+ } = require("@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive");
147
+
144
148
  const navigator = {};
145
149
  const navBarProps = { nav_items: [] };
146
150
  const screenData = {};
@@ -195,4 +199,24 @@ describe("<Screen Component />", () => {
195
199
  expect(toJSON()).toMatchSnapshot();
196
200
  });
197
201
  });
202
+
203
+ describe("when the screen is inactive", () => {
204
+ afterEach(() => {
205
+ useIsScreenActive.mockReturnValue(true);
206
+ });
207
+
208
+ it("hides the screen from VoiceOver (iOS) and TalkBack (Android)", () => {
209
+ useIsScreenActive.mockReturnValue(false);
210
+
211
+ const { UNSAFE_root } = renderWithProviders(
212
+ <Screen {...screenProps} />,
213
+ store
214
+ );
215
+
216
+ expect(UNSAFE_root.findByType(View).props).toMatchObject({
217
+ accessibilityElementsHidden: true,
218
+ importantForAccessibility: "no-hide-descendants",
219
+ });
220
+ });
221
+ });
198
222
  });
@@ -13,6 +13,7 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
13
13
  }
14
14
  >
15
15
  <View
16
+ accessibilityElementsHidden={false}
16
17
  importantForAccessibility="yes"
17
18
  style={
18
19
  {
@@ -58,6 +59,7 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
58
59
  }
59
60
  >
60
61
  <View
62
+ accessibilityElementsHidden={false}
61
63
  importantForAccessibility="yes"
62
64
  style={
63
65
  {
@@ -18,10 +18,13 @@ import {
18
18
  useScreenData,
19
19
  } from "@applicaster/zapp-react-native-utils/reactHooks";
20
20
  import { getNavigationPluginModule } from "@applicaster/zapp-react-native-app/App/Layout/layoutHelpers";
21
+ import {
22
+ isValidColor,
23
+ isTransparentColor,
24
+ } from "@applicaster/zapp-react-native-utils/colorUtils";
21
25
 
22
26
  import { RouteManager } from "../RouteManager";
23
27
  import { useScreenConfiguration } from "../River/useScreenConfiguration";
24
- import { isValidColor } from "./utils";
25
28
  import { useWaitForValidOrientation } from "./hooks";
26
29
 
27
30
  const screenStyles = {
@@ -82,9 +85,10 @@ export function Screen(_props: Props) {
82
85
  const style = React.useMemo(
83
86
  () => ({
84
87
  ...screenStyles,
85
- backgroundColor: isValidColor(backgroundColor)
86
- ? backgroundColor
87
- : theme.app_background_color,
88
+ backgroundColor:
89
+ isValidColor(backgroundColor) && !isTransparentColor(backgroundColor)
90
+ ? backgroundColor
91
+ : theme.app_background_color,
88
92
  }),
89
93
  [theme.app_background_color, backgroundColor]
90
94
  );
@@ -117,6 +121,11 @@ export function Screen(_props: Props) {
117
121
  ref={ref}
118
122
  style={style}
119
123
  importantForAccessibility={!isActive ? "no-hide-descendants" : "yes"}
124
+ // `importantForAccessibility` only affects Android (see RN docs).
125
+ // Without the iOS counterpart below, VoiceOver keeps traversing and
126
+ // focusing elements on an inactive/underlying screen (e.g. Albums In
127
+ // Order) even after a new screen (e.g. an Album) is pushed on top.
128
+ accessibilityElementsHidden={!isActive}
120
129
  >
121
130
  {isReady ? (
122
131
  <>
@@ -18,10 +18,9 @@ function shouldShowNavigation(route, screenData) {
18
18
  return false;
19
19
  }
20
20
 
21
- if (
22
- screenData.type === "qb_search_screen" ||
23
- screenData?.targetScreen?.type === "qb_search_screen"
24
- ) {
21
+ const currentScreenData = screenData?.targetScreen || screenData;
22
+
23
+ if (currentScreenData.type === "qb_search_screen") {
25
24
  return false;
26
25
  }
27
26
 
@@ -31,10 +30,11 @@ function shouldShowNavigation(route, screenData) {
31
30
  return hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true;
32
31
  }
33
32
 
34
- if (
35
- screenData?.general?.allow_screen_plugin_presentation ||
36
- screenData?.targetScreen?.general?.allow_screen_plugin_presentation
37
- ) {
33
+ if (currentScreenData?.general?.hide_menu) {
34
+ return false;
35
+ }
36
+
37
+ if (currentScreenData?.general?.allow_screen_plugin_presentation) {
38
38
  return false;
39
39
  }
40
40
 
@@ -8,9 +8,11 @@ import { componentsLogger } from "../../Helpers/logger";
8
8
  const logger = componentsLogger.addSubsystem("ScreenFeedLoader");
9
9
 
10
10
  /** Loads and provides `feedData` and store to */
11
- export const ScreenFeedLoader: React.FC<
12
- React.PropsWithChildren<{ id: string; feedData: any }>
13
- > = ({ id, feedData, children }) => {
11
+ export const ScreenFeedLoader: React.FC<{
12
+ id: string;
13
+ feedData: any;
14
+ children: React.ReactElement | null;
15
+ }> = ({ id, feedData, children }) => {
14
16
  const { source: feedUrl, mapping } = feedData;
15
17
 
16
18
  const { data, loading, error } = useFeedLoader({
@@ -8,7 +8,7 @@ export const ScreenResolverFeedProvider = ({
8
8
  useFeedData,
9
9
  }: {
10
10
  id: string;
11
- children: React.ReactNode;
11
+ children: React.ReactElement | null;
12
12
  useFeedData: (id: string) => Option<ZappDataSource>;
13
13
  }) => {
14
14
  const feedData = useFeedData(id);
@@ -0,0 +1,34 @@
1
+ import * as React from "react";
2
+ import { Animated, StyleSheet } from "react-native";
3
+
4
+ import { BackgroundImage } from "@applicaster/zapp-react-native-ui-components/Components/BackgroundImage";
5
+
6
+ const styles = StyleSheet.create({
7
+ container: {
8
+ ...StyleSheet.absoluteFillObject,
9
+ position: "absolute",
10
+ },
11
+ });
12
+
13
+ export const Overlay = ({
14
+ opacity,
15
+ backgroundColor,
16
+ }: {
17
+ opacity: Animated.Value;
18
+ backgroundColor: string;
19
+ }) => {
20
+ return (
21
+ <Animated.View
22
+ style={[
23
+ styles.container,
24
+ {
25
+ opacity,
26
+ backgroundColor,
27
+ },
28
+ ]}
29
+ testID="animated-component"
30
+ >
31
+ <BackgroundImage />
32
+ </Animated.View>
33
+ );
34
+ };