@applicaster/zapp-react-native-ui-components 13.0.0-rc.11 → 13.0.0-rc.111

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 (212) hide show
  1. package/Components/AppContainer/__tests__/AppContainer.test.js +4 -6
  2. package/Components/AppContainer/__tests__/__snapshots__/AppContainer.test.js.snap +1 -1
  3. package/Components/AudioPlayer/AudioPlayer.tsx +7 -4
  4. package/Components/AudioPlayer/__tests__/Runtime.test.js +5 -8
  5. package/Components/AudioPlayer/__tests__/__snapshots__/Runtime.test.js.snap +4 -4
  6. package/Components/AudioPlayer/__tests__/__snapshots__/artWork.test.js.snap +3 -3
  7. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +56 -113
  8. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +39 -23
  9. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +3 -3
  10. package/Components/AudioPlayer/__tests__/__snapshots__/summary.test.js.snap +2 -2
  11. package/Components/AudioPlayer/__tests__/__snapshots__/title.test.js.snap +2 -2
  12. package/Components/AudioPlayer/__tests__/artWork.test.js +3 -5
  13. package/Components/AudioPlayer/__tests__/audioPlayer.test.js +3 -5
  14. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +7 -8
  15. package/Components/AudioPlayer/__tests__/channel.test.js +3 -5
  16. package/Components/AudioPlayer/__tests__/summary.test.js +3 -5
  17. package/Components/AudioPlayer/__tests__/title.test.js +3 -5
  18. package/Components/AudioPlayer/helpers.tsx +1 -1
  19. package/Components/BaseFocusable/index.ios.ts +1 -1
  20. package/Components/BaseFocusable/index.tsx +67 -31
  21. package/Components/Cell/Cell.tsx +18 -22
  22. package/Components/Cell/CellWithFocusable.tsx +51 -60
  23. package/Components/Cell/CellWrapper.tsx +18 -0
  24. package/Components/Cell/TvOSCellComponent.tsx +23 -30
  25. package/Components/Cell/__tests__/CellWIthFocusable.test.js +13 -6
  26. package/Components/Cell/index.js +3 -1
  27. package/Components/Cell/styles.ts +17 -0
  28. package/Components/CellRendererResolver/__tests__/cellRendererResolver.test.js +50 -50
  29. package/Components/CellRendererResolver/index.ts +2 -1
  30. package/Components/ComponentResolver/__tests__/__snapshots__/componentResolver.test.js.snap +1 -42
  31. package/Components/ComponentResolver/__tests__/componentResolver.test.js +26 -30
  32. package/Components/ContentScreen/__tests__/contentScreen.test.js +3 -3
  33. package/Components/DisplayState/__tests__/__snapshots__/displayState.test.js.snap +2 -2
  34. package/Components/ErrorScreen/index.tsx +14 -6
  35. package/Components/Focusable/Focusable.tsx +1 -54
  36. package/Components/Focusable/FocusableTvOS.tsx +6 -2
  37. package/Components/Focusable/Touchable.tsx +19 -20
  38. package/Components/Focusable/index.android.tsx +10 -3
  39. package/Components/FocusableCell/index.tsx +1 -1
  40. package/Components/FocusableGroup/FocusableTvOS.tsx +34 -86
  41. package/Components/FocusableGroup/index.tsx +0 -3
  42. package/Components/FocusableList/index.tsx +26 -10
  43. package/Components/FocusableScrollView/index.tsx +43 -13
  44. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +3 -0
  45. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +9 -0
  46. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  47. package/Components/GeneralContentScreen/utils/useEventAlerts.ts +30 -0
  48. package/Components/HandlePlayable/HandlePlayable.tsx +21 -27
  49. package/Components/Layout/TV/LayoutBackground.tsx +28 -0
  50. package/Components/Layout/TV/NavBarContainer.tsx +19 -23
  51. package/Components/Layout/TV/ScreenContainer.tsx +8 -2
  52. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  53. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +3 -1
  54. package/Components/Layout/TV/__tests__/__snapshots__/LayoutContainer.test.tsx.snap +1 -1
  55. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +6 -5
  56. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +12 -11
  57. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +15 -10
  58. package/Components/Layout/TV/__tests__/index.test.tsx +14 -4
  59. package/Components/Layout/TV/index.tsx +6 -20
  60. package/Components/Layout/TV/index.web.tsx +4 -1
  61. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -3
  62. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +66 -0
  63. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -1
  64. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +10 -15
  65. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +11 -7
  66. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +93 -0
  67. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -2
  68. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +3 -3
  69. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
  70. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  71. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/const.ts +3 -0
  72. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -6
  73. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/__snapshots__/index.test.tsx.snap +1 -1
  74. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +2 -8
  75. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +180 -180
  76. package/Components/MasterCell/__tests__/__snapshots__/elementMapper.test.js.snap +11 -19
  77. package/Components/MasterCell/__tests__/__snapshots__/masterCell.test.js.snap +6 -235
  78. package/Components/MasterCell/__tests__/elementMapper.test.js +18 -5
  79. package/Components/MasterCell/__tests__/masterCell.test.js +26 -26
  80. package/Components/MasterCell/hooks/useAsyncRendering/MasterCellAsyncRenderManager.ts +2 -2
  81. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +2 -2
  82. package/Components/MasterCell/utils/behaviorProvider.ts +136 -0
  83. package/Components/MasterCell/utils/index.ts +9 -135
  84. package/Components/ModalComponent/BottomSheetModalContent.tsx +59 -43
  85. package/Components/ModalComponent/Button/Item.tsx +6 -5
  86. package/Components/ModalComponent/Button/assets.ts +1 -1
  87. package/Components/ModalComponent/Button/index.tsx +45 -62
  88. package/Components/ModalComponent/Header/index.tsx +12 -11
  89. package/Components/ModalComponent/utils.ts +55 -7
  90. package/Components/NativeFocusables/{index.js → index.ts} +4 -2
  91. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +2 -2
  92. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +2 -2
  93. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -5
  94. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +7 -7
  95. package/Components/OfflineHandler/__tests__/index.test.tsx +28 -25
  96. package/Components/OfflineHandler/utils/index.ts +1 -1
  97. package/Components/PlayerContainer/PlayerContainer.tsx +45 -25
  98. package/Components/PlayerContainer/PlayerContainerContext.tsx +9 -0
  99. package/Components/PlayerContainer/ProgramInfo/index.tsx +2 -2
  100. package/Components/PlayerImageBackground/index.tsx +1 -1
  101. package/Components/River/ComponentsMap/ComponentsMap.tsx +42 -65
  102. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +78 -51
  103. package/Components/River/RefreshControl.tsx +1 -1
  104. package/Components/River/RiverFooter.tsx +39 -9
  105. package/Components/River/RiverItem.tsx +39 -3
  106. package/Components/River/TV/index.tsx +6 -1
  107. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +114 -45
  108. package/Components/River/__tests__/__snapshots__/river.test.js.snap +21 -83
  109. package/Components/River/__tests__/componentsMap.test.js +64 -17
  110. package/Components/River/__tests__/river.test.js +75 -40
  111. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +12 -5
  112. package/Components/RouteManager/__tests__/routeManager.test.js +29 -14
  113. package/Components/RouteManager/__tests__/testId.test.js +8 -6
  114. package/Components/Screen/TV/__tests__/index.web.test.tsx +26 -0
  115. package/Components/Screen/TV/index.web.tsx +5 -6
  116. package/Components/Screen/__tests__/Screen.test.tsx +53 -31
  117. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +6 -6
  118. package/Components/Screen/hooks.ts +53 -0
  119. package/Components/Screen/index.tsx +16 -42
  120. package/Components/ScreenResolver/__tests__/screenResolver.test.js +95 -70
  121. package/Components/Tabs/Tab.tsx +12 -9
  122. package/Components/TextInputTv/__tests__/TextInputTv.test.js +42 -42
  123. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +7 -0
  124. package/Components/TextInputTv/index.tsx +6 -5
  125. package/Components/TopMarginApplicator/TopMarginApplicator.tsx +2 -3
  126. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +2 -2
  127. package/Components/TrackedView/index.tsx +1 -0
  128. package/Components/Transitioner/AnimationManager.js +15 -15
  129. package/Components/Transitioner/Scene.tsx +53 -24
  130. package/Components/Transitioner/Transitioner.tsx +3 -3
  131. package/Components/Transitioner/__tests__/Scene.test.js +25 -14
  132. package/Components/Transitioner/__tests__/__snapshots__/AnimationManager.test.js.snap +26 -26
  133. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +10 -50
  134. package/Components/Transitioner/__tests__/__snapshots__/config.test.js.snap +20 -20
  135. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +4 -4
  136. package/Components/Transitioner/__tests__/transitioner.test.js +13 -4
  137. package/Components/Transitioner/index.js +8 -4
  138. package/Components/VideoLive/LiveImageManager.ts +27 -1
  139. package/Components/VideoLive/PlayerLiveImageComponent.tsx +36 -24
  140. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +63 -8
  141. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +6 -10
  142. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  143. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +51 -31
  144. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +8 -4
  145. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +48 -22
  146. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +15 -15
  147. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +108 -0
  148. package/Components/VideoModal/ModalAnimation/const.ts +5 -0
  149. package/Components/VideoModal/ModalAnimation/index.ts +4 -1
  150. package/Components/VideoModal/ModalAnimation/useModalAnimationContext.ts +6 -0
  151. package/Components/VideoModal/ModalAnimation/utils.ts +38 -2
  152. package/Components/VideoModal/OpaqueLayer.tsx +33 -0
  153. package/Components/VideoModal/PlayerDetails.tsx +18 -10
  154. package/Components/VideoModal/PlayerWrapper.tsx +43 -39
  155. package/Components/VideoModal/VideoModal.tsx +17 -11
  156. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -2
  157. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -10
  158. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -40
  159. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +66 -156
  160. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +89 -0
  161. package/Components/VideoModal/hooks/index.ts +7 -0
  162. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +49 -0
  163. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +91 -0
  164. package/Components/VideoModal/hooks/utils/index.ts +33 -0
  165. package/Components/VideoModal/utils.ts +6 -9
  166. package/Components/Viewport/ViewportAware/__tests__/__snapshots__/viewportAware.test.js.snap +24 -36
  167. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -18
  168. package/Components/Viewport/ViewportTracker/__tests__/__snapshots__/viewportTracker.test.js.snap +1 -1
  169. package/Components/Viewport/__tests__/utils.test.js +5 -4
  170. package/Components/ZappUIComponent/Placeholder.tsx +3 -1
  171. package/Components/ZappUIComponent/__tests__/{ZappUIComponent.test.js → ZappUIComponent.test.tsx} +26 -10
  172. package/Components/ZappUIComponent/index.tsx +4 -17
  173. package/Components/default-cell-renderer/__tests__/defaultCellRenderer.test.tsx +20 -17
  174. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/__tests__/DefaultCell.test.tsx +15 -11
  175. package/Contexts/ComponentsMapOffsetContext/index.tsx +49 -0
  176. package/Contexts/ConfigutaionContext/__tests__/__snapshots__/ConfigurationProvider.test.tsx.snap +1 -1
  177. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  178. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  179. package/Contexts/ScreenContext/index.tsx +6 -12
  180. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  181. package/Decorators/Analytics/{index.js → index.tsx} +11 -11
  182. package/Decorators/Navigator/__tests__/navigator.test.js +16 -16
  183. package/Decorators/Navigator/index.tsx +1 -1
  184. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +77 -77
  185. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +34 -39
  186. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -9
  187. package/Decorators/RiverFeedLoader/index.tsx +1 -1
  188. package/Decorators/RiverFeedLoader/utils/getDatasourceUrl.ts +39 -0
  189. package/Decorators/RiverFeedLoader/utils/index.ts +29 -0
  190. package/Decorators/RiverFeedLoader/utils/usePipesContexts.ts +40 -0
  191. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +27 -27
  192. package/Decorators/RiverResolver/index.tsx +6 -8
  193. package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
  194. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +29 -22
  195. package/Decorators/ZappPipesDataConnector/index.tsx +7 -3
  196. package/package.json +13 -11
  197. package/tsconfig.json +2 -3
  198. package/.babelrc +0 -8
  199. package/Components/Cell/CellStyles/FallbackCellStyle/index.js +0 -157
  200. package/Components/Cell/CellStyles/Hero/index.js +0 -111
  201. package/Components/Cell/CellStyles/ScreenSelector/index.js +0 -68
  202. package/Components/Cell/CellStyles/cellStylesResolver.ts +0 -19
  203. package/Components/Cell/CellStyles/colors.js +0 -40
  204. package/Components/Cell/CellStyles/index.js +0 -15
  205. package/Components/CellRendererResolver/__tests__/__snapshots__/cellRendererResolver.test.js.snap +0 -53
  206. package/Components/RouteManager/__tests__/__snapshots__/testId.test.js.snap +0 -17
  207. package/Components/ScreenResolver/__tests__/__snapshots__/screenResolver.test.js.snap +0 -17
  208. package/Components/ZappUIComponent/__tests__/__snapshots__/ZappUIComponent.test.js.snap +0 -91
  209. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +0 -338
  210. package/Decorators/Navigator/__tests__/__snapshots__/navigator.test.js.snap +0 -18
  211. package/Decorators/RiverFeedLoader/utils.ts +0 -100
  212. package/Decorators/RiverResolver/__tests__/__snapshots__/riverResolver.test.tsx.snap +0 -28
@@ -1,13 +1,12 @@
1
1
  import * as React from "react";
2
+ import * as R from "ramda";
2
3
 
3
4
  import {
4
5
  BaseFocusable as BaseFocusableInterface,
5
6
  BoundingRect,
6
7
  } from "./BaseFocusable.interface";
7
8
 
8
- import { createReactRef } from "@applicaster/zapp-react-native-utils/reactUtils";
9
9
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
10
- import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
11
10
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
12
11
 
13
12
  type ScrollDirection = FocusManager.IOS.Direction | FocusManager.Web.Direction;
@@ -35,7 +34,7 @@ type Props = {
35
34
  };
36
35
 
37
36
  export class BaseFocusable<
38
- T extends Props = Props
37
+ T extends Props = Props,
39
38
  > extends BaseFocusableInterface<T> {
40
39
  constructor(props) {
41
40
  super(props);
@@ -45,7 +44,7 @@ export class BaseFocusable<
45
44
  focused: initialFocus || false,
46
45
  };
47
46
 
48
- this.ref = createReactRef();
47
+ this.ref = React.createRef();
49
48
  }
50
49
 
51
50
  componentDidMount() {
@@ -131,12 +130,6 @@ export class BaseFocusable<
131
130
  willLoseFocus: FocusManager.FocusEventCB = (focusable, scrollDirection) => {
132
131
  const { willLoseFocus = noop } = this.props;
133
132
  willLoseFocus(focusable, scrollDirection);
134
-
135
- focusManager.invokeHandler?.(
136
- FOCUS_EVENTS.WILL_LOSE_FOCUS,
137
- this,
138
- scrollDirection
139
- );
140
133
  };
141
134
 
142
135
  willReceiveFocus: FocusManager.FocusEventCB = (
@@ -145,12 +138,6 @@ export class BaseFocusable<
145
138
  ) => {
146
139
  const { willReceiveFocus = noop } = this.props;
147
140
  willReceiveFocus(focusable, scrollDirection);
148
-
149
- focusManager.invokeHandler?.(
150
- FOCUS_EVENTS.WILL_RECEIVE_FOCUS,
151
- this,
152
- scrollDirection
153
- );
154
141
  };
155
142
 
156
143
  /**
@@ -163,8 +150,6 @@ export class BaseFocusable<
163
150
  const { onFocus = noop } = this.props;
164
151
  this.setFocusedState(true);
165
152
  onFocus(focusable, scrollDirection);
166
-
167
- focusManager.invokeHandler?.(FOCUS_EVENTS.FOCUS, this, scrollDirection);
168
153
  };
169
154
 
170
155
  /**
@@ -179,12 +164,6 @@ export class BaseFocusable<
179
164
  ) => {
180
165
  const { hasReceivedFocus = noop } = this.props;
181
166
  hasReceivedFocus(focusable, scrollDirection);
182
-
183
- focusManager.invokeHandler?.(
184
- FOCUS_EVENTS.HAS_RECEIVED_FOCUS,
185
- this,
186
- scrollDirection
187
- );
188
167
  };
189
168
 
190
169
  /**
@@ -196,12 +175,6 @@ export class BaseFocusable<
196
175
  hasLostFocus: FocusManager.FocusEventCB = (focusable, scrollDirection) => {
197
176
  const { hasLostFocus = noop } = this.props;
198
177
  hasLostFocus(focusable, scrollDirection);
199
-
200
- focusManager.invokeHandler?.(
201
- FOCUS_EVENTS.HAS_LOST_FOCUS,
202
- this,
203
- scrollDirection
204
- );
205
178
  };
206
179
 
207
180
  /**
@@ -228,7 +201,6 @@ export class BaseFocusable<
228
201
  const { onBlur = noop } = this.props;
229
202
  this.setFocusedState(false);
230
203
  onBlur(focusable, scrollDirection);
231
- focusManager.invokeHandler?.(FOCUS_EVENTS.BLUR, this, scrollDirection);
232
204
  };
233
205
 
234
206
  onPress = (keyEvent?: Nullable<React.MouseEvent>) => {
@@ -269,4 +241,68 @@ export class BaseFocusable<
269
241
  this.setState({ focused });
270
242
  }
271
243
  };
244
+
245
+ /**
246
+ * will invoke the underlying component's focus method
247
+ * @param {Object} scrollDirection
248
+ * @returns {Promise}
249
+ */
250
+ focus(_, scrollDirection) {
251
+ return this.onFocus(this, scrollDirection); // invokeComponentMethod(this, "onFocus", scrollDirection);
252
+ }
253
+
254
+ /**
255
+ * will invoke the underlying component's blur method
256
+ * @param {Object} scrollDirection
257
+ * @returns {Promise}
258
+ */
259
+ blur(
260
+ _,
261
+ scrollDirection?: FocusManager.Web.Direction | FocusManager.IOS.Direction
262
+ ) {
263
+ return this.onBlur(this, scrollDirection);
264
+ }
265
+
266
+ /**
267
+ * Sets the focus on this item. Will trigger sequentially a sequence of
268
+ * functions (willReceiveFocus, focus, hasReceivedFocus). If these functions (defined in the Focusable
269
+ * Item underlying component) return promises, execution will wait before it proceeds to the next. This
270
+ * is useful for triggering sequential operations that specifically require to fully run before or after
271
+ * focus is actually set on that item.
272
+ * @param {string} scrollDirection string representation of the direction of the navigation which landed
273
+ * to this item being focused
274
+ */
275
+ _executeFocusSequence(methodNames, scrollDirection) {
276
+ return R.reduce(
277
+ (sequence, methodName) => {
278
+ const method = this[methodName]; // Access the method by name
279
+
280
+ if (typeof method !== "function") {
281
+ throw new Error(
282
+ `Method '${methodName}' not found or not a function.`
283
+ );
284
+ }
285
+
286
+ return sequence
287
+ .then(() => method.call(this, scrollDirection))
288
+ .catch((e) => {
289
+ throw e; // Re-throw for consistent error handling
290
+ });
291
+ },
292
+ Promise.resolve(),
293
+ methodNames
294
+ );
295
+ }
296
+
297
+ setFocus(scrollDirection) {
298
+ const focusMethods = ["willReceiveFocus", "focus", "hasReceivedFocus"];
299
+
300
+ return this._executeFocusSequence(focusMethods, scrollDirection);
301
+ }
302
+
303
+ setBlur(scrollDirection) {
304
+ const blurMethods = ["willLoseFocus", "blur", "hasLostFocus"];
305
+
306
+ return this._executeFocusSequence(blurMethods, scrollDirection);
307
+ }
272
308
  }
@@ -12,12 +12,10 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
12
12
  import { CellWithFocusable } from "./CellWithFocusable";
13
13
  import { BaseFocusable } from "../BaseFocusable";
14
14
  import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
15
+ import { styles } from "./styles";
15
16
 
16
17
  type Props = {
17
- item: {
18
- id: string;
19
- title: string;
20
- };
18
+ item: ZappEntry;
21
19
  index: number;
22
20
  shouldScrollHorizontally: (arg1: [any]) => boolean | null | undefined;
23
21
  shouldScrollVertically: (...args: any) => boolean | null | undefined;
@@ -33,10 +31,9 @@ type Props = {
33
31
  id: string;
34
32
  };
35
33
  selected?: boolean;
36
- CellRenderer: React.ComponentType<{
37
- item: any;
38
- state: string;
39
- }> & { hasFocusableInside: (item: Record<string, string>) => boolean };
34
+ CellRenderer: React.FunctionComponent<any> & {
35
+ hasFocusableInside: (item: ZappEntry) => boolean;
36
+ };
40
37
  preferredFocus: boolean;
41
38
  navigator: {
42
39
  push: (arg1: Record<any, any>) => void;
@@ -65,23 +62,13 @@ type Props = {
65
62
  skipFocusManagerRegistration?: boolean;
66
63
  shouldUpdate: boolean;
67
64
  behavior: Behavior;
65
+ componentsMapOffset: number;
68
66
  };
69
67
 
70
68
  type State = {
71
69
  hasFocusableInside: boolean;
72
70
  };
73
71
 
74
- const baseCellStyles = {
75
- height: "100%",
76
- display: "flex",
77
- flex: 1,
78
- } as const;
79
-
80
- const touchableStyles = {
81
- flex: 1,
82
- height: "100%",
83
- } as const;
84
-
85
72
  export class CellComponent extends React.Component<Props, State> {
86
73
  constructor(props) {
87
74
  super(props);
@@ -255,7 +242,7 @@ export class CellComponent extends React.Component<Props, State> {
255
242
  <View
256
243
  testID={`${component?.id}-${id}`}
257
244
  accessible={false}
258
- style={touchableStyles}
245
+ style={styles.touchableCell}
259
246
  >
260
247
  <Focusable
261
248
  id={focusableId}
@@ -267,7 +254,7 @@ export class CellComponent extends React.Component<Props, State> {
267
254
  hasReceivedFocus={hasReceivedFocus || this.hasReceivedFocus}
268
255
  preferredFocus={preferredFocus}
269
256
  offsetUpdater={offsetUpdater}
270
- style={baseCellStyles}
257
+ style={styles.baseCell}
271
258
  isFocusable={isFocusable}
272
259
  skipFocusManagerRegistration={skipFocusManagerRegistration}
273
260
  >
@@ -276,7 +263,16 @@ export class CellComponent extends React.Component<Props, State> {
276
263
 
277
264
  if (isFocused) {
278
265
  const accessibilityManager = AccessibilityManager.getInstance();
279
- accessibilityManager.readText({ text: item.title });
266
+
267
+ const accessibilityTitle =
268
+ item?.extensions?.accessibility?.label || item?.title || "";
269
+
270
+ const accessibilityHint =
271
+ item?.extensions?.accessibility?.hint || "";
272
+
273
+ accessibilityManager.readText({
274
+ text: `${accessibilityTitle} ${accessibilityHint}`,
275
+ });
280
276
  }
281
277
 
282
278
  return (
@@ -1,52 +1,16 @@
1
1
  import * as React from "react";
2
2
 
3
- import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
4
- import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
5
3
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
6
4
  import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
7
5
 
8
- import { isAppleTV } from "../../Helpers/Platform";
9
6
  import { useCellState } from "../MasterCell/utils";
10
-
11
- const useCellFocusedState = (
12
- skipFocusManagerRegistration: boolean,
13
- groupId: string,
14
- id: string
15
- ) => {
16
- const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
17
-
18
- React.useEffect(() => {
19
- const isGroupItemFocused = () => {
20
- if (!skipFocusManagerRegistration) {
21
- const isFocused = focusManager.isGroupItemFocused(groupId, id);
22
- setCurrentCellFocused(isFocused);
23
- }
24
- };
25
-
26
- const handler = () => {
27
- // tvOS hack for properly checking focus
28
- if (isAppleTV()) {
29
- setTimeout(() => {
30
- isGroupItemFocused();
31
- }, 0);
32
- } else {
33
- isGroupItemFocused();
34
- }
35
- };
36
-
37
- focusManager.on(FOCUS_EVENTS.FOCUS, handler);
38
-
39
- return () => {
40
- focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
41
- };
42
- }, [groupId, skipFocusManagerRegistration]);
43
-
44
- return currentCellFocused;
45
- };
7
+ import { FocusableGroup } from "../FocusableGroup";
8
+ import { CellWrapper } from "./CellWrapper";
9
+ import { styles } from "./styles";
46
10
 
47
11
  type Props = {
48
12
  item: ZappEntry;
49
- CellRenderer: React.ComponentType<{ item: Object; state: string }>;
13
+ CellRenderer: React.FunctionComponent<any>;
50
14
  id: string;
51
15
  groupId: string;
52
16
  onFocus: Function;
@@ -59,6 +23,8 @@ type Props = {
59
23
  focused?: boolean;
60
24
  };
61
25
 
26
+ const addPrefix = (id: string) => `focusable-cell-wrapper-${id}`;
27
+
62
28
  export function CellWithFocusable(props: Props) {
63
29
  const {
64
30
  index,
@@ -75,14 +41,10 @@ export function CellWithFocusable(props: Props) {
75
41
  focused,
76
42
  } = props;
77
43
 
78
- const isFocused = useCellFocusedState(
79
- skipFocusManagerRegistration,
80
- groupId,
81
- id
82
- );
44
+ const [isFocused, setIsFocused] = React.useState(false);
83
45
 
84
46
  const state = useCellState({
85
- id: item.id,
47
+ item,
86
48
  behavior,
87
49
  focused: isFocused || toBooleanWithDefaultFalse(focused),
88
50
  });
@@ -96,26 +58,55 @@ export function CellWithFocusable(props: Props) {
96
58
  }
97
59
  }, [focusedButtonId]);
98
60
 
99
- const handleToggleFocus = (value) => {
100
- setFocusedButtonId(value.focusedButtonId);
61
+ const handleToggleFocus = React.useCallback(
62
+ (value) => {
63
+ setFocusedButtonId(value.focusedButtonId);
64
+
65
+ if (value.focusable) {
66
+ onFocus(value.focusable, value.mouse);
67
+ }
68
+ },
69
+ [onFocus]
70
+ );
71
+
72
+ const onGroupFocus = React.useCallback(() => {
73
+ if (!skipFocusManagerRegistration) {
74
+ setIsFocused(true);
75
+ }
76
+ }, [skipFocusManagerRegistration]);
101
77
 
102
- if (value.focusable) {
103
- onFocus(value.focusable, value.mouse);
78
+ const onGroupBlur = React.useCallback(() => {
79
+ if (!skipFocusManagerRegistration) {
80
+ setIsFocused(false);
104
81
  }
105
- };
82
+ }, [skipFocusManagerRegistration]);
106
83
 
107
84
  return (
108
- <CellRenderer
109
- item={item}
85
+ <FocusableGroup
86
+ id={addPrefix(id)}
87
+ testID={"cell-with-focusable-cell-renderer-focusable-group"}
110
88
  groupId={groupId}
111
- onToggleFocus={handleToggleFocus}
112
- state={state}
113
- prefixId={id}
114
- focusedButtonId={focusedButtonId}
115
89
  preferredFocus={preferredFocus}
90
+ shouldUsePreferredFocus
91
+ onFocus={onGroupFocus}
92
+ onBlur={onGroupBlur}
116
93
  skipFocusManagerRegistration={skipFocusManagerRegistration}
117
- isFocusable={isFocusable}
118
- focused={focused}
119
- />
94
+ >
95
+ <CellWrapper style={styles.cellWrapper}>
96
+ <CellRenderer
97
+ testID={"cell-with-focusable-cell-renderer"}
98
+ item={item}
99
+ groupId={addPrefix(id)}
100
+ onToggleFocus={handleToggleFocus}
101
+ state={state}
102
+ prefixId={id}
103
+ focusedButtonId={focusedButtonId}
104
+ preferredFocus={true}
105
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
106
+ isFocusable={isFocusable}
107
+ focused={focused}
108
+ />
109
+ </CellWrapper>
110
+ </FocusableGroup>
120
111
  );
121
112
  }
@@ -0,0 +1,18 @@
1
+ import * as React from "react";
2
+ import { View, ViewStyle } from "react-native";
3
+ import { isTvOSPlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
4
+
5
+ const isTvOS = isTvOSPlatform();
6
+
7
+ type Props = {
8
+ style: ViewStyle;
9
+ children: React.ReactNode;
10
+ };
11
+
12
+ export const CellWrapper = ({ style, children }: Props) => {
13
+ if (isTvOS) {
14
+ return <View style={style}>{children}</View>;
15
+ }
16
+
17
+ return <>{children}</>;
18
+ };
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { View, StyleSheet } from "react-native";
4
4
 
5
- import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
5
+ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
6
6
  import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
7
7
  import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtils";
8
8
  import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
@@ -10,10 +10,6 @@ import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focu
10
10
  import { sendSelectCellEvent } from "@applicaster/zapp-react-native-utils/analyticsUtils";
11
11
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
12
12
  import { CellWithFocusable } from "./CellWithFocusable";
13
- import { getCellState } from "./utils";
14
-
15
- const _getCellState = (focused, selected) =>
16
- getCellState({ focused, selected });
17
13
 
18
14
  type Props = {
19
15
  item: ZappEntry;
@@ -36,10 +32,9 @@ type Props = {
36
32
  component_type: string;
37
33
  };
38
34
  selected: boolean;
39
- CellRenderer: React.ComponentType<{
40
- item: any;
41
- state: string;
42
- }> & { hasFocusableInside: (item: any) => boolean };
35
+ CellRenderer: React.FunctionComponent<any> & {
36
+ hasFocusableInside: (item: any) => boolean;
37
+ };
43
38
  preferredFocus: boolean;
44
39
  navigator: {
45
40
  push: (arg1: Record<any, any>) => void;
@@ -70,6 +65,7 @@ type Props = {
70
65
  isFocusable: boolean;
71
66
  shouldUpdate: boolean;
72
67
  behavior: Behavior;
68
+ componentsMapOffset: number;
73
69
  };
74
70
 
75
71
  type State = {
@@ -184,6 +180,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
184
180
  groupId,
185
181
  component,
186
182
  index,
183
+ componentsMapOffset,
187
184
  } = this.props;
188
185
 
189
186
  this.setScreenLayout(componentAnchorPointY, screenLayout);
@@ -198,13 +195,11 @@ export class TvOSCellComponent extends React.Component<Props, State> {
198
195
  const extraAnchorPointYOffset =
199
196
  screenLayout?.extraAnchorPointYOffset || 0;
200
197
 
201
- const marginTop = screenLayout?.screenMarginTop || 0;
202
-
203
198
  const totalOffset =
204
199
  headerOffset +
205
- (componentAnchorPointY || 0) +
206
- extraAnchorPointYOffset -
207
- marginTop;
200
+ (componentAnchorPointY || 0) +
201
+ extraAnchorPointYOffset -
202
+ componentsMapOffset || 0;
208
203
 
209
204
  mainOffsetUpdater?.(
210
205
  { tag: this.target },
@@ -252,7 +247,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
252
247
 
253
248
  const focusableId = R.join("-", [component?.id, id, index]);
254
249
 
255
- const handleFocus = (arg1: any, index: number) => {
250
+ const handleFocus = (arg1: any, index?: number) => {
256
251
  const focusFn = onFocus || noop;
257
252
  focusFn(arg1, index);
258
253
 
@@ -285,7 +280,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
285
280
  <View onLayout={this.onLayout} style={styles.container}>
286
281
  <Focusable
287
282
  id={focusableId}
288
- groupId={groupId || component?.id}
283
+ groupId={String(groupId || component?.id)}
289
284
  onFocus={handleFocus}
290
285
  onBlur={onBlur || this.onBlur}
291
286
  isParallaxDisabled={this.layout?.width > 1740}
@@ -297,20 +292,18 @@ export class TvOSCellComponent extends React.Component<Props, State> {
297
292
  style={baseCellStyles}
298
293
  isFocusable={isFocusable}
299
294
  >
300
- {(focused) => {
301
- return (
302
- <FocusableCell
303
- {...{
304
- index,
305
- CellRenderer,
306
- item,
307
- focused: this.props.focused || selectedCell || focused,
308
- scrollTo: this.scrollTo,
309
- behavior,
310
- }}
311
- />
312
- );
313
- }}
295
+ {(focused) => (
296
+ <FocusableCell
297
+ {...{
298
+ index,
299
+ CellRenderer,
300
+ item,
301
+ focused: !!(this.props.focused || selectedCell || focused),
302
+ scrollTo: this.scrollTo,
303
+ behavior,
304
+ }}
305
+ />
306
+ )}
314
307
  </Focusable>
315
308
  </View>
316
309
  );
@@ -1,7 +1,7 @@
1
1
  import { View } from "react-native";
2
2
  import React from "react";
3
3
  import { act, render } from "@testing-library/react-native";
4
- import { CellWithFocusable } from "../CellWithFocusable";
4
+ import { CellWithFocusable } from "../CellWithFocusable.tsx";
5
5
 
6
6
  import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
7
7
 
@@ -23,7 +23,9 @@ describe("CellWithFocusable", () => {
23
23
 
24
24
  const wrapper = renderWith(props);
25
25
 
26
- expect(wrapper.UNSAFE_getByType("View").props.state).toBe("default");
26
+ const element = wrapper.getByTestId("cell-with-focusable-cell-renderer");
27
+
28
+ expect(element.props.state).toBe("default");
27
29
  });
28
30
 
29
31
  it("should render in default state", () => {
@@ -40,8 +42,9 @@ describe("CellWithFocusable", () => {
40
42
  focusManager.isGroupItemFocused = jest.fn(() => true);
41
43
 
42
44
  const wrapper = renderWith(props);
45
+ const element = wrapper.getByTestId("cell-with-focusable-cell-renderer");
43
46
 
44
- expect(wrapper.UNSAFE_getByType("View").props.state).toBe("default");
47
+ expect(element.props.state).toBe("default");
45
48
  });
46
49
 
47
50
  it("should render in focused state", () => {
@@ -55,13 +58,17 @@ describe("CellWithFocusable", () => {
55
58
  scrollTo: jest.fn(),
56
59
  };
57
60
 
58
- focusManager.isGroupItemFocused = jest.fn(() => true);
59
61
  const wrapper = renderWith(props);
60
62
 
63
+ const focusableGroupComponent = wrapper.getByTestId(
64
+ "cell-with-focusable-cell-renderer-focusable-group"
65
+ );
66
+
61
67
  act(() => {
62
- focusManager.on.mock.calls[focusManager.on.mock.calls.length - 1][1]();
68
+ focusableGroupComponent.props.onFocus();
63
69
  });
64
70
 
65
- expect(wrapper.UNSAFE_getByType("View").props.state).toBe("focused");
71
+ const element = wrapper.getByTestId("cell-with-focusable-cell-renderer");
72
+ expect(element.props.state).toBe("focused");
66
73
  });
67
74
  });
@@ -11,6 +11,7 @@ import { ScreenScrollingContext } from "../../Contexts/ScreenScrollingContext";
11
11
 
12
12
  import { ScreenLayoutContextConsumer } from "../../Contexts/ScreenLayoutContext";
13
13
  import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
14
+ import { withComponentsMapOffsetContextConsumer } from "../../Contexts/ComponentsMapOffsetContext";
14
15
 
15
16
  // TODO | Gallery QB | Extract this
16
17
  export const ScrollInterceptorContext = createContext(
@@ -30,5 +31,6 @@ export const Cell = R.compose(
30
31
  RiverOffsetContext.withConsumer,
31
32
  HorizontalScrollContext.withConsumer,
32
33
  withConsumer,
33
- ScreenLayoutContextConsumer
34
+ ScreenLayoutContextConsumer,
35
+ withComponentsMapOffsetContextConsumer
34
36
  )(Component);
@@ -0,0 +1,17 @@
1
+ import { StyleSheet } from "react-native";
2
+
3
+ export const styles = StyleSheet.create({
4
+ cellWrapper: {
5
+ flex: 0,
6
+ height: "100%",
7
+ },
8
+ baseCell: {
9
+ height: "100%",
10
+ display: "flex",
11
+ flex: 1,
12
+ },
13
+ touchableCell: {
14
+ flex: 1,
15
+ height: "100%",
16
+ },
17
+ });