@applicaster/zapp-react-native-ui-components 15.1.4 → 16.0.0-alpha.1042942411

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 (202) hide show
  1. package/Components/BackgroundImage/index.ts +1 -8
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/FocusableWrapper.tsx +3 -0
  4. package/Components/Cell/TvOSCellComponent.tsx +6 -3
  5. package/Components/CellRendererResolver/index.ts +1 -1
  6. package/Components/Focusable/Focusable.tsx +4 -2
  7. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  8. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  9. package/Components/FocusableGroup/FocusableTvOS.tsx +30 -1
  10. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
  11. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  12. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  15. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
  16. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  17. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  18. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -0
  20. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  22. package/Components/GeneralContentScreen/index.ts +2 -0
  23. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  24. package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
  25. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  26. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  27. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  28. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  29. package/Components/Layout/TV/index.tsx +3 -4
  30. package/Components/Layout/TV/index.web.tsx +3 -4
  31. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  32. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +27 -48
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +213 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  39. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  40. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  41. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  42. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +198 -0
  43. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  44. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  45. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  46. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +217 -46
  47. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +50 -119
  48. package/Components/MasterCell/DefaultComponents/Button.tsx +50 -58
  49. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  50. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.android.tv.tsx +58 -0
  51. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  52. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  53. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  54. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  55. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/__snapshots__/index.test.tsx.snap +18 -0
  56. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/index.test.tsx +90 -0
  57. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +13 -14
  58. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  59. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  60. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  61. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
  62. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  63. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  64. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  65. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  66. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
  67. package/Components/MasterCell/DefaultComponents/PressableView.tsx +54 -0
  68. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +46 -50
  69. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +47 -39
  70. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  71. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +19 -6
  72. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +15 -0
  73. package/Components/MasterCell/DefaultComponents/Text/index.tsx +45 -15
  74. package/Components/MasterCell/DefaultComponents/UIKitAdapters/Badge.tsx +49 -0
  75. package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +122 -0
  76. package/Components/MasterCell/DefaultComponents/__tests__/Button.test.tsx +186 -0
  77. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  78. package/Components/MasterCell/DefaultComponents/__tests__/DynamicBadge.test.tsx +156 -0
  79. package/Components/MasterCell/DefaultComponents/__tests__/__snapshots__/ActionButton.test.tsx.snap +60 -0
  80. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +196 -67
  81. package/Components/MasterCell/DefaultComponents/index.ts +13 -3
  82. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +189 -0
  83. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  84. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +33 -0
  85. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +159 -0
  86. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +18 -0
  87. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  88. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +39 -0
  89. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +402 -0
  90. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  91. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +354 -0
  92. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  93. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +129 -0
  94. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +123 -0
  95. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  96. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  97. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  98. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  99. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  100. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  101. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  102. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
  103. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  104. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  105. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
  106. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  107. package/Components/MasterCell/README.md +4 -0
  108. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  109. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  110. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  111. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  112. package/Components/MasterCell/dataAdapter.ts +4 -1
  113. package/Components/MasterCell/elementMapper.tsx +54 -7
  114. package/Components/MasterCell/hooks/index.ts +2 -0
  115. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  116. package/Components/MasterCell/index.tsx +20 -4
  117. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  118. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  119. package/Components/MasterCell/utils/index.ts +82 -16
  120. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -69
  121. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +25 -47
  122. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +122 -39
  123. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +671 -0
  124. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +257 -88
  125. package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
  126. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  127. package/Components/ModalComponent/BottomSheetModalContent.tsx +17 -10
  128. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  129. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  130. package/Components/OfflineHandler/__tests__/index.test.tsx +101 -32
  131. package/Components/OfflineHandler/hooks.ts +123 -0
  132. package/Components/OfflineHandler/index.tsx +51 -73
  133. package/Components/OfflineHandler/utils/index.ts +1 -13
  134. package/Components/PlayerContainer/PlayerContainer.tsx +6 -5
  135. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +2 -4
  136. package/Components/PreloaderWrapper/index.tsx +1 -1
  137. package/Components/River/ComponentsMap/ComponentsMap.tsx +8 -21
  138. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  139. package/Components/River/RefreshControl.tsx +19 -105
  140. package/Components/River/River.tsx +9 -82
  141. package/Components/River/TV/River.tsx +31 -14
  142. package/Components/River/TV/index.tsx +8 -4
  143. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  144. package/Components/River/TV/utils/index.ts +4 -0
  145. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  146. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +4 -0
  147. package/Components/River/__tests__/componentsMap.test.js +38 -0
  148. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  149. package/Components/River/hooks/index.ts +1 -0
  150. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  151. package/Components/Screen/TV/index.web.tsx +4 -2
  152. package/Components/Screen/__tests__/Screen.test.tsx +65 -42
  153. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  154. package/Components/Screen/hooks.ts +2 -3
  155. package/Components/Screen/index.tsx +2 -3
  156. package/Components/Screen/orientationHandler.ts +3 -3
  157. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  158. package/Components/ScreenResolver/index.tsx +9 -5
  159. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  160. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  161. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  162. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +1 -1
  163. package/Components/Tabs/TabContent.tsx +7 -4
  164. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  165. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  166. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  167. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  168. package/Components/TopCutoffOverlay/index.tsx +55 -0
  169. package/Components/Transitioner/index.js +3 -3
  170. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  171. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  172. package/Components/VideoModal/utils.ts +12 -10
  173. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  174. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  175. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  176. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  177. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  178. package/Contexts/ScreenContext/index.tsx +25 -18
  179. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  180. package/Decorators/Analytics/index.tsx +6 -5
  181. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  182. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  183. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  184. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  185. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  186. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  187. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  188. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  189. package/Helpers/DataSourceHelper/index.ts +19 -0
  190. package/package.json +5 -5
  191. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  192. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  193. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  194. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  195. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  196. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -67
  197. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  198. package/Components/River/TV/withTVEventHandler.tsx +0 -36
  199. package/Helpers/DataSourceHelper/index.js +0 -19
  200. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  201. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  202. /package/Components/BackgroundImage/{BackgroundImage.tv.web.tsx → BackgroundImage.tsx} +0 -0
@@ -1,8 +1 @@
1
- import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
2
-
3
- export const BackgroundImage = platformSelect({
4
- tvos: require("./BackgroundImage.tv.ios").BackgroundImage,
5
- android_tv: require("./BackgroundImage.tv.android").BackgroundImage,
6
- amazon: require("./BackgroundImage.tv.android").BackgroundImage,
7
- web: require("./BackgroundImage.tv.web").BackgroundImage,
8
- });
1
+ export { BackgroundImage } from "./BackgroundImage";
@@ -22,6 +22,7 @@ type Props = {
22
22
  onFocus?: FocusManager.FocusEventCB;
23
23
  onBlur?: FocusManager.FocusEventCB;
24
24
  selected?: boolean;
25
+ skipFocusManagerRegistration?: boolean;
25
26
  };
26
27
 
27
28
  export class BaseFocusable<
@@ -61,10 +62,14 @@ export class BaseFocusable<
61
62
  }
62
63
 
63
64
  componentDidMount() {
64
- const { id } = this.props;
65
+ const { id, skipFocusManagerRegistration } = this.props;
65
66
  const component = this;
66
67
  this.node = this.ref.current;
67
68
 
69
+ if (skipFocusManagerRegistration) {
70
+ return;
71
+ }
72
+
68
73
  focusManager.register({
69
74
  id,
70
75
  component: component,
@@ -118,7 +123,12 @@ export class BaseFocusable<
118
123
 
119
124
  componentWillUnmount() {
120
125
  this._isMounted = false;
121
- const { id } = this.props;
126
+ const { id, skipFocusManagerRegistration } = this.props;
127
+
128
+ if (skipFocusManagerRegistration) {
129
+ return;
130
+ }
131
+
122
132
  focusManager.unregister(id, { group: this.isGroup || false });
123
133
  }
124
134
 
@@ -10,6 +10,7 @@ type Props = {
10
10
  children: (focused: boolean) => React.ReactNode;
11
11
  onFocus: (arg1: any, index?: number) => void;
12
12
  onBlur: Callback;
13
+ skipFocusManagerRegistration?: boolean;
13
14
  };
14
15
 
15
16
  export const FocusableWrapper = ({
@@ -20,6 +21,7 @@ export const FocusableWrapper = ({
20
21
  applyWrapper,
21
22
  onFocus,
22
23
  onBlur,
24
+ skipFocusManagerRegistration,
23
25
  }: Props) => {
24
26
  if (applyWrapper) {
25
27
  return (
@@ -34,6 +36,7 @@ export const FocusableWrapper = ({
34
36
  // @ts-ignore
35
37
  offsetUpdater={noop}
36
38
  isFocusable
39
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
37
40
  >
38
41
  {(focused) => children(focused)}
39
42
  </Focusable>
@@ -80,6 +80,7 @@ type Props = {
80
80
  componentsMapOffset: number;
81
81
  applyFocusableWrapper: boolean;
82
82
  hasFocusableInside: boolean;
83
+ skipFocusManagerRegistration?: boolean;
83
84
  };
84
85
 
85
86
  type State = {
@@ -194,7 +195,6 @@ class TvOSCell extends React.Component<Props, State> {
194
195
  groupId,
195
196
  component,
196
197
  index,
197
- componentsMapOffset,
198
198
  } = this.props;
199
199
 
200
200
  this.setScreenLayout(componentAnchorPointY, screenLayout);
@@ -221,8 +221,7 @@ class TvOSCell extends React.Component<Props, State> {
221
221
  const totalOffset =
222
222
  headerOffset +
223
223
  toNumberWithDefaultZero(componentAnchorPointY) +
224
- extraAnchorPointYOffset -
225
- toNumberWithDefaultZero(componentsMapOffset) +
224
+ extraAnchorPointYOffset +
226
225
  componentMarginTop +
227
226
  componentPaddingTop;
228
227
 
@@ -266,6 +265,7 @@ class TvOSCell extends React.Component<Props, State> {
266
265
  behavior,
267
266
  applyFocusableWrapper,
268
267
  hasFocusableInside,
268
+ skipFocusManagerRegistration,
269
269
  } = this.props;
270
270
 
271
271
  const { id } = item;
@@ -291,6 +291,7 @@ class TvOSCell extends React.Component<Props, State> {
291
291
  onFocus={handleFocus}
292
292
  onBlur={onBlur || this.onBlur}
293
293
  applyWrapper={applyFocusableWrapper}
294
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
294
295
  >
295
296
  {(focused) => (
296
297
  <CellWithFocusable
@@ -305,6 +306,7 @@ class TvOSCell extends React.Component<Props, State> {
305
306
  focused={focused || this.props.focused}
306
307
  behavior={behavior}
307
308
  isFocusable={isFocusable}
309
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
308
310
  />
309
311
  )}
310
312
  </FocusableWrapper>
@@ -327,6 +329,7 @@ class TvOSCell extends React.Component<Props, State> {
327
329
  offsetUpdater={offsetUpdater}
328
330
  style={baseCellStyles}
329
331
  isFocusable={isFocusable}
332
+ skipFocusManagerRegistration={skipFocusManagerRegistration}
330
333
  >
331
334
  {(focused) => (
332
335
  <FocusableCell
@@ -92,7 +92,7 @@ export function CellRendererResolver({
92
92
  component,
93
93
  styles,
94
94
  cellStyles,
95
- cellOptions,
95
+ cellOptions: { ...cellOptions, component },
96
96
  ...cellOptions, // fallback for existing plugins
97
97
  });
98
98
  }
@@ -8,6 +8,8 @@ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withF
8
8
  import { StyleSheet, ViewStyle } from "react-native";
9
9
  import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
10
10
 
11
+ import { isSearchInputId } from "@applicaster/zapp-react-native-utils/searchUtils";
12
+
11
13
  type Props = {
12
14
  initialFocus?: boolean;
13
15
  id: string;
@@ -106,7 +108,7 @@ class Focusable extends BaseFocusable<Props> {
106
108
  onMouseEnter() {
107
109
  const { id } = this.props;
108
110
 
109
- if (id !== "search_input_group_id") {
111
+ if (!isSearchInputId(id)) {
110
112
  this.mouse = true;
111
113
  this.props?.handleFocus?.({ mouse: true });
112
114
 
@@ -120,7 +122,7 @@ class Focusable extends BaseFocusable<Props> {
120
122
  onMouseLeave() {
121
123
  const { id } = this.props;
122
124
 
123
- if (id !== "search_input_group_id") {
125
+ if (!isSearchInputId(id)) {
124
126
  this.mouse = false;
125
127
  this.blur(null);
126
128
  }
@@ -10,8 +10,12 @@ import {
10
10
  forceFocusableFocus,
11
11
  } from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
12
12
  import { findNodeHandle, ViewStyle } from "react-native";
13
+ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
13
14
 
14
- function noop() {}
15
+ import {
16
+ emitFocused,
17
+ emitNativeRegistered,
18
+ } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
15
19
 
16
20
  type Props = {
17
21
  id: string;
@@ -39,6 +43,7 @@ type Props = {
39
43
  hasReceivedFocus: () => void;
40
44
  offsetUpdater: (arg1: string, arg2: number) => number;
41
45
  style: ViewStyle;
46
+ skipFocusManagerRegistration?: boolean;
42
47
  };
43
48
 
44
49
  export class Focusable extends BaseFocusable<Props> {
@@ -53,6 +58,7 @@ export class Focusable extends BaseFocusable<Props> {
53
58
  this.nextFocusableReactTags = {};
54
59
  this.preferredFocus = this.preferredFocus.bind(this);
55
60
  this.measureView = this.measureView.bind(this);
61
+ this.onRegistered = this.onRegistered.bind(this);
56
62
  }
57
63
 
58
64
  /**
@@ -84,6 +90,9 @@ export class Focusable extends BaseFocusable<Props> {
84
90
  });
85
91
  }
86
92
 
93
+ const id: string = nativeEvent.itemID;
94
+ emitFocused(id);
95
+
87
96
  onFocus(nativeEvent);
88
97
  }
89
98
 
@@ -169,6 +178,13 @@ export class Focusable extends BaseFocusable<Props> {
169
178
  });
170
179
  }
171
180
 
181
+ onRegistered({ nativeEvent }) {
182
+ const groupId = nativeEvent?.groupId;
183
+ const id = nativeEvent?.itemId;
184
+
185
+ emitNativeRegistered({ id, groupId, isGroup: false });
186
+ }
187
+
172
188
  render() {
173
189
  const {
174
190
  children,
@@ -203,6 +219,7 @@ export class Focusable extends BaseFocusable<Props> {
203
219
  focusable={isFocusable}
204
220
  {...this.nextFocusableReactTags}
205
221
  {...otherProps}
222
+ onRegistered={this.onRegistered}
206
223
  >
207
224
  {typeof children === "function" ? children(focused) : children}
208
225
  </FocusableItemNative>
@@ -5,6 +5,7 @@ exports[`FocusableTvOS should render correctly 1`] = `
5
5
  groupId={null}
6
6
  itemId={null}
7
7
  onLayout={[Function]}
8
+ onRegistered={[Function]}
8
9
  onViewBlur={[Function]}
9
10
  onViewFocus={[Function]}
10
11
  onViewPress={[Function]}
@@ -3,6 +3,10 @@ import { FocusableGroupNative } from "@applicaster/zapp-react-native-ui-componen
3
3
  import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable";
4
4
  // TODO: Enable when will be feature flags
5
5
  // import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
6
+ import { LayoutContext } from "@applicaster/zapp-react-native-tvos-app/Context/LayoutContext";
7
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
8
+ import { isScreenPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
9
+ import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
6
10
 
7
11
  // TODO: Enable when will be feature flags
8
12
  // const { log_verbose } = createLogger({
@@ -35,9 +39,16 @@ type Props = {
35
39
  screenData: { screenId: string; parentScreenId: string };
36
40
  };
37
41
 
38
- export class FocusableGroup extends BaseFocusable<Props> {
42
+ class FocusableGroupComponent extends BaseFocusable<Props> {
39
43
  public readonly isGroup: boolean = true;
40
44
 
45
+ onRegistered = ({ nativeEvent }) => {
46
+ const groupId = nativeEvent?.groupId;
47
+ const id = nativeEvent?.itemId;
48
+
49
+ emitNativeRegistered({ id, groupId, isGroup: true });
50
+ };
51
+
41
52
  render() {
42
53
  const {
43
54
  children,
@@ -72,9 +83,27 @@ export class FocusableGroup extends BaseFocusable<Props> {
72
83
  onGroupBlur={onGroupBlur}
73
84
  style={style}
74
85
  {...otherProps}
86
+ onRegistered={this.onRegistered}
75
87
  >
76
88
  {children}
77
89
  </FocusableGroupNative>
78
90
  );
79
91
  }
80
92
  }
93
+
94
+ export const withFocusDisabled = (Component) => {
95
+ return function WithFocusDisabled(props) {
96
+ // @ts-ignore
97
+ const { screenFocusBlocked } = React.useContext(LayoutContext.ReactContext);
98
+
99
+ const { pathname } = useRoute();
100
+
101
+ const isPlayerPresented = isScreenPlayable(pathname);
102
+
103
+ const blockScreenFocus = isPlayerPresented === false && screenFocusBlocked;
104
+
105
+ return <Component {...props} isFocusDisabled={blockScreenFocus} />;
106
+ };
107
+ };
108
+
109
+ export const FocusableGroup = withFocusDisabled(FocusableGroupComponent);
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+ import { GeneralContentScreen } from "./GeneralContentScreen";
3
+ import { HookContentFocusGroup } from "./hookFocus";
4
+ import { runInBackground } from "./hookAdapter/runInBackground";
5
+ import { log_error } from "./hookAdapter/logger";
6
+
7
+ type HookComponentProps = HookPluginProps & {
8
+ configuration?: unknown;
9
+ };
10
+
11
+ function GeneralContentScreenHookComponent(props: HookComponentProps) {
12
+ const { hookPlugin, focused, parentFocus } = props;
13
+
14
+ if (!hookPlugin?.screen_id) {
15
+ log_error(
16
+ "GeneralContentScreenHookAdapter: This component should only be used as a hook, but no hookPlugin was provided."
17
+ );
18
+
19
+ return null;
20
+ }
21
+
22
+ return (
23
+ <HookContentFocusGroup>
24
+ <GeneralContentScreen
25
+ screenId={hookPlugin.screen_id}
26
+ isScreenWrappedInContainer={false}
27
+ focused={focused}
28
+ parentFocus={parentFocus}
29
+ />
30
+ </HookContentFocusGroup>
31
+ );
32
+ }
33
+
34
+ export const GeneralContentScreenHookAdapter = {
35
+ isFlowBlocker: () => true,
36
+ presentFullScreen: true,
37
+ Component: GeneralContentScreenHookComponent,
38
+ runInBackground,
39
+ };
@@ -0,0 +1,64 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+ import { GeneralContentScreenHookAdapter } from "../GeneralContentScreenHookAdapter";
4
+ import { log_error } from "../hookAdapter/logger";
5
+
6
+ const mockScreenSpy = jest.fn();
7
+
8
+ jest.mock("../GeneralContentScreen", () => ({
9
+ GeneralContentScreen: (props) => {
10
+ const React = require("react");
11
+ const { View } = require("react-native");
12
+
13
+ mockScreenSpy(props);
14
+
15
+ return <View testID="general-content-screen" />;
16
+ },
17
+ }));
18
+
19
+ jest.mock("../hookAdapter/runInBackground", () => ({
20
+ runInBackground: jest.fn(),
21
+ }));
22
+
23
+ jest.mock("../hookAdapter/logger", () => ({
24
+ log_debug: jest.fn(),
25
+ log_error: jest.fn(),
26
+ log_info: jest.fn(),
27
+ }));
28
+
29
+ const { Component } = GeneralContentScreenHookAdapter;
30
+
31
+ describe("GeneralContentScreenHookAdapter", () => {
32
+ beforeEach(() => {
33
+ jest.clearAllMocks();
34
+ });
35
+
36
+ it("renders GeneralContentScreen with the hook plugin screen id", () => {
37
+ const { getByTestId } = render(
38
+ <Component hookPlugin={{ screen_id: "screen-1" }} focused />
39
+ );
40
+
41
+ expect(getByTestId("general-content-screen")).toBeDefined();
42
+
43
+ expect(mockScreenSpy).toHaveBeenCalledWith(
44
+ expect.objectContaining({
45
+ screenId: "screen-1",
46
+ isScreenWrappedInContainer: false,
47
+ })
48
+ );
49
+ });
50
+
51
+ it("renders nothing and logs an error when hookPlugin is missing", () => {
52
+ const { toJSON } = render(<Component />);
53
+
54
+ expect(toJSON()).toBeNull();
55
+ expect(mockScreenSpy).not.toHaveBeenCalled();
56
+ expect(log_error).toHaveBeenCalled();
57
+ });
58
+
59
+ it("renders nothing when hookPlugin has no screen_id", () => {
60
+ const { toJSON } = render(<Component hookPlugin={{}} />);
61
+
62
+ expect(toJSON()).toBeNull();
63
+ });
64
+ });
@@ -0,0 +1,91 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+ import { View } from "react-native";
4
+
5
+ import { HookContentFocusGroup } from "../hookFocus/index.web";
6
+
7
+ const mockFocusableGroupSpy = jest.fn();
8
+ const mockUseInitialFocusSpy = jest.fn();
9
+
10
+ const mockModalState = { isRunningInBackground: false };
11
+
12
+ jest.mock("../../FocusableGroup", () => ({
13
+ FocusableGroup: (props) => {
14
+ const React = require("react");
15
+ const { View } = require("react-native");
16
+
17
+ mockFocusableGroupSpy(props);
18
+
19
+ return <View testID="focusable-group">{props.children}</View>;
20
+ },
21
+ }));
22
+
23
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
24
+ useContentId: () => "quick-brick-content___route",
25
+ useNavbarId: () => "quick-brick-navbar___route",
26
+ usePathname: () => "hooks-modal/profile-select",
27
+ }));
28
+
29
+ jest.mock("../../Screen/TV/hooks", () => ({
30
+ useInitialFocus: () => mockUseInitialFocusSpy(),
31
+ }));
32
+
33
+ jest.mock("../../../Contexts/ZappHookModalContext", () => ({
34
+ useZappHookModalStore: (selector) => selector(mockModalState),
35
+ }));
36
+
37
+ describe("HookContentFocusGroup (web)", () => {
38
+ beforeEach(() => {
39
+ jest.clearAllMocks();
40
+ mockModalState.isRunningInBackground = false;
41
+ });
42
+
43
+ it("wraps content in a preferred-focus content group and triggers initial focus when presented full screen", () => {
44
+ const { getByTestId } = render(
45
+ <HookContentFocusGroup>
46
+ <View testID="child" />
47
+ </HookContentFocusGroup>
48
+ );
49
+
50
+ expect(getByTestId("focusable-group")).toBeDefined();
51
+ expect(getByTestId("child")).toBeDefined();
52
+ expect(mockUseInitialFocusSpy).toHaveBeenCalledTimes(1);
53
+
54
+ expect(mockFocusableGroupSpy).toHaveBeenCalledWith(
55
+ expect.objectContaining({
56
+ id: "quick-brick-content___route",
57
+ groupId: "hooks-modal/profile-select",
58
+ nextFocusUp: "quick-brick-navbar___route",
59
+ preferredFocus: true,
60
+ shouldUsePreferredFocus: true,
61
+ })
62
+ );
63
+ });
64
+
65
+ it("injects the content group id into the wrapped child so its cells register under it", () => {
66
+ render(
67
+ <HookContentFocusGroup>
68
+ <View testID="child" />
69
+ </HookContentFocusGroup>
70
+ );
71
+
72
+ const child = mockFocusableGroupSpy.mock.calls[0][0].children;
73
+
74
+ expect(child.props.groupId).toBe("quick-brick-content___route");
75
+ });
76
+
77
+ it("renders the child without focus handling when running in background (not full screen)", () => {
78
+ mockModalState.isRunningInBackground = true;
79
+
80
+ const { getByTestId, queryByTestId } = render(
81
+ <HookContentFocusGroup>
82
+ <View testID="child" />
83
+ </HookContentFocusGroup>
84
+ );
85
+
86
+ expect(getByTestId("child")).toBeDefined();
87
+ expect(queryByTestId("focusable-group")).toBeNull();
88
+ expect(mockUseInitialFocusSpy).not.toHaveBeenCalled();
89
+ expect(mockFocusableGroupSpy).not.toHaveBeenCalled();
90
+ });
91
+ });
@@ -0,0 +1,74 @@
1
+ import { requestToSkipHook } from "../networkService";
2
+
3
+ const mockCall = jest.fn();
4
+ const mockBuildAxiosRequest = jest.fn();
5
+ let mockHelper: Record<string, any>;
6
+
7
+ jest.mock("@applicaster/zapp-pipes-v2-client", () => ({
8
+ RequestBuilder: jest.fn().mockImplementation(() => ({
9
+ setEntryContext: jest.fn().mockReturnThis(),
10
+ setScreenContext: jest.fn().mockReturnThis(),
11
+ setUrl: jest.fn().mockReturnThis(),
12
+ buildAxiosRequest: (...args) => mockBuildAxiosRequest(...args),
13
+ call: (...args) => mockCall(...args),
14
+ })),
15
+ PipesClientResponseHelper: jest.fn().mockImplementation(() => mockHelper),
16
+ }));
17
+
18
+ jest.mock("../logger", () => ({
19
+ log_debug: jest.fn(),
20
+ log_error: jest.fn(),
21
+ log_info: jest.fn(),
22
+ }));
23
+
24
+ const dataSource = { source: "https://skip-endpoint", mapping: {} };
25
+ const payload = { id: "entry-1" };
26
+
27
+ describe("requestToSkipHook", () => {
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ mockBuildAxiosRequest.mockResolvedValue({ url: "https://skip-endpoint" });
31
+ mockCall.mockResolvedValue({});
32
+
33
+ mockHelper = {
34
+ error: null,
35
+ statusCode: 200,
36
+ responseData: null,
37
+ getLogsData: jest.fn(() => ({})),
38
+ };
39
+ });
40
+
41
+ it("returns true when the endpoint returns a non-empty body", async () => {
42
+ mockHelper.responseData = true;
43
+
44
+ await expect(requestToSkipHook(dataSource, payload)).resolves.toBe(true);
45
+ });
46
+
47
+ it("treats any truthy payload as skip (hook-screen-wrapper contract)", async () => {
48
+ mockHelper.responseData = { entry: [] };
49
+
50
+ await expect(requestToSkipHook(dataSource, payload)).resolves.toBe(true);
51
+ });
52
+
53
+ it("returns false when the endpoint returns an empty body", async () => {
54
+ mockHelper.responseData = null;
55
+
56
+ await expect(requestToSkipHook(dataSource, payload)).resolves.toBe(false);
57
+ });
58
+
59
+ it("throws when the response contains an error", async () => {
60
+ mockHelper.error = new Error("server error");
61
+
62
+ await expect(requestToSkipHook(dataSource, payload)).rejects.toThrow(
63
+ "server error"
64
+ );
65
+ });
66
+
67
+ it("throws when the request itself fails", async () => {
68
+ mockCall.mockRejectedValue(new Error("network down"));
69
+
70
+ await expect(requestToSkipHook(dataSource, payload)).rejects.toThrow(
71
+ "network down"
72
+ );
73
+ });
74
+ });