@applicaster/zapp-react-native-ui-components 13.0.0-alpha.2040752175 → 13.0.0-alpha.2101002115

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 (183) 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 -0
  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 -0
  19. package/Components/BaseFocusable/index.ios.ts +1 -1
  20. package/Components/BaseFocusable/index.tsx +67 -31
  21. package/Components/Cell/Cell.tsx +15 -9
  22. package/Components/Cell/CellWithFocusable.tsx +44 -59
  23. package/Components/Cell/TvOSCellComponent.tsx +23 -30
  24. package/Components/Cell/__tests__/CellWIthFocusable.test.js +13 -6
  25. package/Components/Cell/index.js +3 -1
  26. package/Components/CellRendererResolver/__tests__/cellRendererResolver.test.js +50 -50
  27. package/Components/ComponentResolver/__tests__/__snapshots__/componentResolver.test.js.snap +1 -42
  28. package/Components/ComponentResolver/__tests__/componentResolver.test.js +26 -30
  29. package/Components/ContentScreen/__tests__/contentScreen.test.js +3 -3
  30. package/Components/DisplayState/__tests__/__snapshots__/displayState.test.js.snap +2 -2
  31. package/Components/ErrorScreen/index.tsx +6 -6
  32. package/Components/Focusable/Focusable.tsx +1 -54
  33. package/Components/Focusable/FocusableTvOS.tsx +6 -2
  34. package/Components/Focusable/Touchable.tsx +19 -20
  35. package/Components/Focusable/index.android.tsx +10 -3
  36. package/Components/FocusableGroup/FocusableTvOS.tsx +34 -86
  37. package/Components/FocusableGroup/index.tsx +0 -3
  38. package/Components/FocusableList/index.tsx +26 -10
  39. package/Components/FocusableScrollView/index.tsx +43 -13
  40. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +9 -0
  41. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  42. package/Components/Layout/TV/NavBarContainer.tsx +19 -23
  43. package/Components/Layout/TV/ScreenContainer.tsx +8 -1
  44. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +5 -0
  45. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +3 -1
  46. package/Components/Layout/TV/__tests__/__snapshots__/LayoutContainer.test.tsx.snap +1 -1
  47. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +6 -5
  48. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +12 -11
  49. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +1 -1
  50. package/Components/Layout/TV/__tests__/index.test.tsx +7 -3
  51. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -3
  52. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +66 -0
  53. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -1
  54. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +6 -12
  55. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +93 -0
  56. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -2
  57. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +3 -3
  58. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
  59. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  60. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/__snapshots__/index.test.tsx.snap +1 -1
  61. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +2 -8
  62. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +180 -180
  63. package/Components/MasterCell/__tests__/__snapshots__/elementMapper.test.js.snap +11 -19
  64. package/Components/MasterCell/__tests__/__snapshots__/masterCell.test.js.snap +6 -235
  65. package/Components/MasterCell/__tests__/elementMapper.test.js +18 -5
  66. package/Components/MasterCell/__tests__/masterCell.test.js +26 -26
  67. package/Components/MasterCell/hooks/useAsyncRendering/MasterCellAsyncRenderManager.ts +2 -2
  68. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +2 -2
  69. package/Components/MasterCell/utils/behaviorProvider.ts +133 -0
  70. package/Components/MasterCell/utils/index.ts +4 -130
  71. package/Components/ModalComponent/Button/index.tsx +6 -6
  72. package/Components/ModalComponent/Header/index.tsx +3 -3
  73. package/Components/NativeFocusables/{index.js → index.ts} +4 -2
  74. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +2 -2
  75. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +2 -2
  76. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -5
  77. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +7 -7
  78. package/Components/OfflineHandler/__tests__/index.test.tsx +28 -25
  79. package/Components/PlayerContainer/PlayerContainer.tsx +22 -24
  80. package/Components/PlayerContainer/PlayerContainerContext.tsx +9 -0
  81. package/Components/PlayerContainer/ProgramInfo/index.tsx +2 -2
  82. package/Components/PlayerImageBackground/index.tsx +1 -1
  83. package/Components/River/ComponentsMap/ComponentsMap.tsx +42 -65
  84. package/Components/River/ComponentsMap/hooks/useLoadingState.ts +78 -51
  85. package/Components/River/RefreshControl.tsx +1 -1
  86. package/Components/River/RiverFooter.tsx +39 -9
  87. package/Components/River/RiverItem.tsx +37 -2
  88. package/Components/River/TV/index.tsx +6 -1
  89. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +114 -45
  90. package/Components/River/__tests__/__snapshots__/river.test.js.snap +21 -83
  91. package/Components/River/__tests__/componentsMap.test.js +64 -17
  92. package/Components/River/__tests__/river.test.js +75 -40
  93. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +12 -5
  94. package/Components/RouteManager/__tests__/routeManager.test.js +29 -14
  95. package/Components/RouteManager/__tests__/testId.test.js +8 -6
  96. package/Components/Screen/TV/index.web.tsx +5 -6
  97. package/Components/Screen/__tests__/Screen.test.tsx +41 -27
  98. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -4
  99. package/Components/Screen/hooks.ts +53 -0
  100. package/Components/Screen/index.tsx +16 -42
  101. package/Components/ScreenResolver/__tests__/screenResolver.test.js +95 -70
  102. package/Components/Tabs/Tab.tsx +12 -9
  103. package/Components/TextInputTv/__tests__/TextInputTv.test.js +42 -42
  104. package/Components/TextInputTv/__tests__/__snapshots__/TextInputTv.test.js.snap +7 -0
  105. package/Components/TextInputTv/index.tsx +6 -5
  106. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +2 -2
  107. package/Components/TrackedView/index.tsx +1 -0
  108. package/Components/Transitioner/AnimationManager.js +15 -15
  109. package/Components/Transitioner/Scene.tsx +53 -24
  110. package/Components/Transitioner/Transitioner.tsx +3 -3
  111. package/Components/Transitioner/__tests__/Scene.test.js +25 -14
  112. package/Components/Transitioner/__tests__/__snapshots__/AnimationManager.test.js.snap +26 -26
  113. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +10 -50
  114. package/Components/Transitioner/__tests__/__snapshots__/config.test.js.snap +20 -20
  115. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +4 -4
  116. package/Components/Transitioner/__tests__/transitioner.test.js +13 -4
  117. package/Components/Transitioner/index.js +8 -4
  118. package/Components/VideoLive/LiveImageManager.ts +27 -1
  119. package/Components/VideoLive/PlayerLiveImageComponent.tsx +36 -24
  120. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +63 -8
  121. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +6 -10
  122. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +1 -1
  123. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +49 -31
  124. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +8 -4
  125. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +32 -18
  126. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +8 -15
  127. package/Components/VideoModal/ModalAnimation/index.ts +2 -1
  128. package/Components/VideoModal/ModalAnimation/useModalAnimationContext.ts +6 -0
  129. package/Components/VideoModal/ModalAnimation/utils.ts +2 -2
  130. package/Components/VideoModal/OpaqueLayer.tsx +33 -0
  131. package/Components/VideoModal/PlayerDetails.tsx +9 -6
  132. package/Components/VideoModal/PlayerWrapper.tsx +17 -36
  133. package/Components/VideoModal/VideoModal.tsx +14 -23
  134. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -2
  135. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -10
  136. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +4 -4
  137. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +66 -156
  138. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +89 -0
  139. package/Components/VideoModal/hooks/index.ts +7 -0
  140. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +49 -0
  141. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +91 -0
  142. package/Components/VideoModal/hooks/utils/index.ts +33 -0
  143. package/Components/VideoModal/utils.ts +3 -5
  144. package/Components/Viewport/ViewportAware/__tests__/__snapshots__/viewportAware.test.js.snap +24 -36
  145. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -18
  146. package/Components/Viewport/ViewportTracker/__tests__/__snapshots__/viewportTracker.test.js.snap +1 -1
  147. package/Components/Viewport/__tests__/utils.test.js +5 -4
  148. package/Components/ZappUIComponent/Placeholder.tsx +3 -1
  149. package/Components/ZappUIComponent/__tests__/{ZappUIComponent.test.js → ZappUIComponent.test.tsx} +26 -10
  150. package/Components/ZappUIComponent/index.tsx +3 -16
  151. package/Components/default-cell-renderer/__tests__/defaultCellRenderer.test.tsx +20 -17
  152. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/__tests__/DefaultCell.test.tsx +15 -11
  153. package/Contexts/ComponentsMapOffsetContext/index.tsx +49 -0
  154. package/Contexts/ConfigutaionContext/__tests__/__snapshots__/ConfigurationProvider.test.tsx.snap +1 -1
  155. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +4 -10
  156. package/Contexts/HeaderOffsetContext/index.tsx +4 -6
  157. package/Contexts/ScreenContext/index.tsx +6 -12
  158. package/Contexts/ScreenLayoutContext/index.tsx +5 -3
  159. package/Decorators/Analytics/{index.js → index.tsx} +11 -11
  160. package/Decorators/Navigator/__tests__/navigator.test.js +16 -16
  161. package/Decorators/Navigator/index.tsx +1 -1
  162. package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +77 -77
  163. package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +34 -39
  164. package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -9
  165. package/Decorators/RiverFeedLoader/index.tsx +1 -1
  166. package/Decorators/RiverFeedLoader/utils/getDatasourceUrl.ts +39 -0
  167. package/Decorators/RiverFeedLoader/utils/index.ts +29 -0
  168. package/Decorators/RiverFeedLoader/utils/usePipesContexts.ts +40 -0
  169. package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +27 -27
  170. package/Decorators/RiverResolver/index.tsx +6 -8
  171. package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
  172. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +29 -22
  173. package/Decorators/ZappPipesDataConnector/index.tsx +5 -2
  174. package/package.json +13 -10
  175. package/tsconfig.json +2 -3
  176. package/Components/CellRendererResolver/__tests__/__snapshots__/cellRendererResolver.test.js.snap +0 -53
  177. package/Components/RouteManager/__tests__/__snapshots__/testId.test.js.snap +0 -17
  178. package/Components/ScreenResolver/__tests__/__snapshots__/screenResolver.test.js.snap +0 -17
  179. package/Components/ZappUIComponent/__tests__/__snapshots__/ZappUIComponent.test.js.snap +0 -91
  180. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +0 -338
  181. package/Decorators/Navigator/__tests__/__snapshots__/navigator.test.js.snap +0 -18
  182. package/Decorators/RiverFeedLoader/utils.ts +0 -100
  183. package/Decorators/RiverResolver/__tests__/__snapshots__/riverResolver.test.tsx.snap +0 -28
@@ -11,6 +11,13 @@ import {
11
11
  useInitialFocus,
12
12
  } from "@applicaster/zapp-react-native-utils/focusManager";
13
13
 
14
+ export type FocusableScrollViewRenderItem = (info: {
15
+ item: ZappUIComponent;
16
+ index: number;
17
+ focused: boolean;
18
+ parentFocus: ParentFocus;
19
+ }) => React.ReactElement;
20
+
14
21
  export type Props = ScrollViewProps & {
15
22
  id?: number | string;
16
23
  horizontal?: boolean;
@@ -32,17 +39,27 @@ export type Props = ScrollViewProps & {
32
39
  initialFocusDirection?: FocusManager.Android.FocusNavigationDirections;
33
40
  onAllComponentsLoaded?: () => void;
34
41
  omitPropsPropagation?: Array<keyof FlatListProps<any>>;
35
- renderItem: (info: {
36
- item: ZappUIComponent;
37
- index: number;
38
- focused: boolean;
39
- parentFocus: ParentFocus;
40
- }) => React.ReactElement;
42
+ renderItem: FocusableScrollViewRenderItem;
41
43
  } & ParentFocus;
42
44
 
43
45
  type State = boolean[];
44
46
  type Action = { index?: number; isFocusable: boolean; type?: string };
45
47
 
48
+ const unwrapArray = (maybeArray) =>
49
+ Array.isArray(maybeArray) ? maybeArray[0] : maybeArray;
50
+
51
+ const getEntry = <T extends unknown>(data: T | T[], index: unknown): T => {
52
+ let entry;
53
+
54
+ if (R.isNil(index)) {
55
+ entry = unwrapArray(data);
56
+ } else if (typeof index === "number") {
57
+ entry = unwrapArray(data[index]);
58
+ }
59
+
60
+ return entry;
61
+ };
62
+
46
63
  const focusableStateReducer = (
47
64
  state: State,
48
65
  { index = null, isFocusable, type = null }: Action
@@ -73,7 +90,7 @@ const extendScrollViewRef = (_ref, childCompsMeasurementsMap) => {
73
90
  };
74
91
 
75
92
  _ref.scrollToIndex = (params) => {
76
- const { index, viewOffset } = params;
93
+ const { index, viewOffset = 0 } = params;
77
94
 
78
95
  const itemByIndex = R.find(R.propEq("index", index))(
79
96
  R.values(childCompsMeasurementsMap)
@@ -129,8 +146,11 @@ function FocusableScrollViewComponent(props: Props, ref) {
129
146
  );
130
147
 
131
148
  const getFocusableEntryId = React.useCallback(
132
- (_entry: ZappEntry, index?: number) => {
133
- const entry = R.isNil(index) ? _entry : data[index];
149
+ (_entry: ZappEntry | null, index?: number) => {
150
+ const entry: { id?: string } = getEntry(
151
+ R.isNil(index) ? _entry : data,
152
+ index
153
+ );
134
154
 
135
155
  return entry ? `${props.id}--${entry?.id}` : undefined;
136
156
  },
@@ -273,16 +293,25 @@ function FocusableScrollViewComponent(props: Props, ref) {
273
293
  const renderItem = React.useCallback(
274
294
  (item, index) => {
275
295
  const renderArgs = { item, index };
276
- const id = `${props.id}--${renderArgs?.item?.id}`;
277
- const nextFocus = getNextFocus(renderArgs?.index);
296
+
297
+ const id = getFocusableEntryId(
298
+ Array.isArray(item) ? item[0] : item,
299
+ index
300
+ );
301
+
302
+ const nextFocus = getNextFocus(index);
278
303
 
279
304
  const onItemLayout = (event) => {
280
- childCompsMeasurementsMap.current[item.id] = {
305
+ childCompsMeasurementsMap.current[
306
+ Array.isArray(item) ? index : item.id
307
+ ] = {
281
308
  index,
282
309
  layout: event.nativeEvent.layout,
283
310
  };
284
311
  };
285
312
 
313
+ const onSetIsFocusable = setIsFocusable(renderArgs.index);
314
+
286
315
  return (
287
316
  <View key={id} onLayout={onItemLayout}>
288
317
  <Focusable
@@ -305,6 +334,7 @@ function FocusableScrollViewComponent(props: Props, ref) {
305
334
  id={id}
306
335
  disableFocus={shouldDisableFocus(renderArgs?.index)}
307
336
  blockFocus={!isRendered(renderArgs?.index)}
337
+ onSetIsFocusable={onSetIsFocusable}
308
338
  {...focusableItemProps}
309
339
  >
310
340
  {(focused, parentFocus, parentRef) => (
@@ -319,7 +349,7 @@ function FocusableScrollViewComponent(props: Props, ref) {
319
349
  isReadyToRender={isReadyToRender}
320
350
  onLoadFinished={onLoadFinished(renderArgs.index)}
321
351
  onLoadFailed={onLoadFailed(renderArgs.index)}
322
- setIsFocusable={setIsFocusable(renderArgs.index)}
352
+ setIsFocusable={onSetIsFocusable}
323
353
  />
324
354
  )}
325
355
  </Focusable>
@@ -16,6 +16,15 @@ import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Co
16
16
  import { Provider } from "react-redux";
17
17
  import configureStore from "redux-mock-store";
18
18
 
19
+ jest.mock(
20
+ "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute",
21
+ () => ({
22
+ useRoute: jest.fn().mockReturnValue({
23
+ pathname: "/home",
24
+ }),
25
+ })
26
+ );
27
+
19
28
  const homeStack = {
20
29
  route: "/home",
21
30
  state: {
@@ -9,7 +9,7 @@ import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
9
9
  import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
10
10
  import { Categories } from "./logger";
11
11
  import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
12
- import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
12
+ import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
13
13
  import {
14
14
  ZappPipesEntryContext,
15
15
  ZappPipesScreenContext,
@@ -1,9 +1,5 @@
1
1
  import * as React from "react";
2
- import { View, StyleSheet } from "react-native";
3
- import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
4
- import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
5
- import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
6
- import { useScreenConfiguration } from "../../River/useScreenConfiguration";
2
+ import { StyleSheet, View } from "react-native";
7
3
 
8
4
  type Props = {
9
5
  children: React.ReactNode;
@@ -12,33 +8,33 @@ type Props = {
12
8
  };
13
9
 
14
10
  const styles = StyleSheet.create({
15
- container: { position: "absolute", top: 0, zIndex: 10, width: 1920 },
11
+ container: {
12
+ position: "absolute",
13
+ top: 0,
14
+ zIndex: 10,
15
+ flex: 1,
16
+ width: "100%",
17
+ },
18
+ themeStyles: {
19
+ // limits the height of the focusable container of the TopMenuBarTV component
20
+ // to prevent it from being overlapped by the screen content,
21
+ // as it makes TopMenuBarTV unfocusable on tvOS
22
+ maxHeight: 1,
23
+ },
16
24
  });
17
25
 
18
26
  export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
19
- const screenLayout = React.useContext(ScreenLayoutContext);
20
- const screenMarginTop = screenLayout?.screenMarginTop || 0;
21
- const screen = useCurrentScreenData();
22
- const screenConfig = useScreenConfiguration(screen?.id);
23
-
24
- const themeStyles = React.useMemo(
25
- () => ({
26
- // limits the height of the focusable container of the TopMenuBarTV component
27
- // to prevent it from being overlapped by the screen content,
28
- // as it makes TopMenuBarTV unfocusable on tvOS
29
- maxHeight: ifEmptyUseFallback(screenConfig?.marginTop, screenMarginTop),
30
- }),
31
- [screenLayout, screenConfig]
32
- );
33
-
34
27
  React.useEffect(() => {
35
28
  setTimeout(() => {
36
29
  onReady?.(true);
37
30
  }, 0);
38
- }, []);
31
+ }, [onReady]);
39
32
 
40
33
  return isVisible ? (
41
- <View testID="nav-bar-container" style={[styles.container, themeStyles]}>
34
+ <View
35
+ testID="nav-bar-container"
36
+ style={[styles.container, styles.themeStyles]}
37
+ >
42
38
  {children}
43
39
  </View>
44
40
  ) : null;
@@ -55,8 +55,11 @@ export const ScreenContainer = React.memo(function ScreenContainer({
55
55
  ComponentsExtraProps,
56
56
  NavBar,
57
57
  }: Props) {
58
- const { screenMarginBottom } = React.useContext(ScreenLayoutContext);
58
+ const { screenMarginBottom, resetScreenLayout } =
59
+ React.useContext(ScreenLayoutContext);
60
+
59
61
  const { currentRoute, screenData } = useNavigation();
62
+
60
63
  const screen = useCurrentScreenData();
61
64
 
62
65
  const { marginBottom, backgroundColor, paddingTop, paddingBottom } =
@@ -65,6 +68,10 @@ export const ScreenContainer = React.memo(function ScreenContainer({
65
68
  const theme = useTheme();
66
69
  const getThemeValue = React.useCallback(R.propOr(0, R.__, theme), [theme]);
67
70
 
71
+ React.useEffect(() => {
72
+ resetScreenLayout();
73
+ }, [currentRoute]);
74
+
68
75
  const fullscreen = React.useCallback(
69
76
  displayFullScreen({ currentRoute, screenData }),
70
77
  [currentRoute, screenData]
@@ -60,10 +60,15 @@ export function ScreenLayoutContextProvider(props: {
60
60
  initialState
61
61
  );
62
62
 
63
+ const resetScreenLayout = React.useCallback(() => {
64
+ setScreenLayout(initialState);
65
+ }, [initialState]);
66
+
63
67
  const screenLayoutValue = React.useMemo(
64
68
  () => ({
65
69
  ...screenLayout,
66
70
  setScreenLayout,
71
+ resetScreenLayout,
67
72
  }),
68
73
  [screenLayout]
69
74
  );
@@ -39,6 +39,7 @@ jest.mock(
39
39
  );
40
40
 
41
41
  jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
42
+ ...jest.requireActual<any>("@applicaster/zapp-react-native-utils/theme"),
42
43
  useTheme: () => ({}),
43
44
  }));
44
45
 
@@ -55,11 +56,12 @@ describe("ScreenContainer", () => {
55
56
  };
56
57
 
57
58
  const setScreenLayout = () => {};
59
+ const resetScreenLayout = () => {};
58
60
 
59
61
  const wrapper = ({ children }) => (
60
62
  <WrappedWithProviders>
61
63
  <ScreenLayoutContext.Provider
62
- value={{ ...screenLayout, setScreenLayout }}
64
+ value={{ ...screenLayout, setScreenLayout, resetScreenLayout }}
63
65
  >
64
66
  <ScreenContainer NavBar={View}>{children}</ScreenContainer>
65
67
  </ScreenLayoutContext.Provider>
@@ -3,7 +3,7 @@
3
3
  exports[`LayoutContainer renders 1`] = `
4
4
  <View
5
5
  style={
6
- Object {
6
+ {
7
7
  "flex": 1,
8
8
  "height": 1080,
9
9
  "width": 1920,
@@ -3,15 +3,16 @@
3
3
  exports[`NavBarContainer renders 1`] = `
4
4
  <View
5
5
  style={
6
- Array [
7
- Object {
6
+ [
7
+ {
8
+ "flex": 1,
8
9
  "position": "absolute",
9
10
  "top": 0,
10
- "width": 1920,
11
+ "width": "100%",
11
12
  "zIndex": 10,
12
13
  },
13
- Object {
14
- "maxHeight": 10,
14
+ {
15
+ "maxHeight": 1,
15
16
  },
16
17
  ]
17
18
  }
@@ -4,8 +4,8 @@ exports[`ScreenContainer renders 1`] = `
4
4
  <RNCSafeAreaProvider
5
5
  onInsetsChange={[Function]}
6
6
  style={
7
- Array [
8
- Object {
7
+ [
8
+ {
9
9
  "flex": 1,
10
10
  },
11
11
  undefined,
@@ -14,15 +14,16 @@ exports[`ScreenContainer renders 1`] = `
14
14
  >
15
15
  <View
16
16
  style={
17
- Array [
18
- Object {
17
+ [
18
+ {
19
+ "flex": 1,
19
20
  "position": "absolute",
20
21
  "top": 0,
21
- "width": 1920,
22
+ "width": "100%",
22
23
  "zIndex": 10,
23
24
  },
24
- Object {
25
- "maxHeight": 10,
25
+ {
26
+ "maxHeight": 1,
26
27
  },
27
28
  ]
28
29
  }
@@ -30,7 +31,7 @@ exports[`ScreenContainer renders 1`] = `
30
31
  >
31
32
  <View
32
33
  isVisible={true}
33
- menuPlugin={Object {}}
34
+ menuPlugin={{}}
34
35
  navigationProps={null}
35
36
  />
36
37
  </View>
@@ -38,13 +39,13 @@ exports[`ScreenContainer renders 1`] = `
38
39
  `;
39
40
 
40
41
  exports[`ScreenContainer sets margins provided by the theme 1`] = `
41
- Array [
42
- Object {
42
+ [
43
+ {
43
44
  "flex": 1,
44
45
  "height": "100%",
45
46
  "width": "100%",
46
47
  },
47
- Object {
48
+ {
48
49
  "backgroundColor": "transparent",
49
50
  "marginBottom": 10,
50
51
  "paddingBottom": 0,
@@ -8,7 +8,7 @@ exports[`Layout TV renders 1`] = `
8
8
  >
9
9
  <View
10
10
  Components={
11
- Object {
11
+ {
12
12
  "Background": [Function],
13
13
  "NavBar": [Function],
14
14
  }
@@ -8,9 +8,13 @@ import configureStore from "redux-mock-store";
8
8
 
9
9
  const withoutChildren = omit(["children"]);
10
10
 
11
- jest.mock("../../../Screen/TV/index.web", () => ({
12
- Screen: View,
13
- }));
11
+ jest.mock("../../../Screen/TV/index.web", () => {
12
+ const View = jest.requireActual("react-native").View;
13
+
14
+ return {
15
+ Screen: View,
16
+ };
17
+ });
14
18
 
15
19
  const Layout = require("../index.web").default;
16
20
 
@@ -4,7 +4,6 @@ import { TouchableOpacity, ViewStyle } from "react-native";
4
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
5
 
6
6
  import Image from "./Image";
7
-
8
7
  type Props = {
9
8
  item: ZappEntry | ZappFeed;
10
9
  flavour?: "flavour_1" | "flavour_2";
@@ -69,7 +68,7 @@ export function ActionButton(props: Props) {
69
68
  if (!((actionDisabled || !actionContext) && actionState !== null)) {
70
69
  setActionState(actionContext.initialEntryState(item));
71
70
  }
72
- }, [actionDisabled, item?.id, actionContext, action]);
71
+ }, [actionDisabled, item?.id, actionContext, action, setActionState]);
73
72
 
74
73
  const onPress = useCallback(() => {
75
74
  actionContext.invokeAction(item, {
@@ -85,7 +84,7 @@ export function ActionButton(props: Props) {
85
84
  setActionState(state);
86
85
  });
87
86
  }
88
- }, [item?.id]);
87
+ }, [item?.id, setActionState, actionContext]);
89
88
 
90
89
  if (actionDisabled || !actionContext) return null;
91
90
  const AssetComponent = actionState?.asset;
@@ -0,0 +1,66 @@
1
+ import {
2
+ BorderContainerView,
3
+ getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
4
+ } from "../index";
5
+ import * as React from "react";
6
+ import { render } from "@testing-library/react-native";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+ import { View } from "react-native";
9
+
10
+ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
11
+ toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
12
+ }));
13
+
14
+ describe("BorderContainerView", () => {
15
+ describe("getBorderPadding", () => {
16
+ it("returns 0 for inside", () => {
17
+ expect(getBorderPadding("inside", 10)).toBe(0);
18
+ });
19
+
20
+ it("returns borderWidth / 2 for center", () => {
21
+ expect(getBorderPadding("center", 10)).toBe(5);
22
+ });
23
+
24
+ it("returns borderWidth for outside", () => {
25
+ expect(getBorderPadding("outside", 10)).toBe(10);
26
+ });
27
+
28
+ it("returns borderWidth for invalid position", () => {
29
+ // @ts-ignore
30
+ expect(getBorderPadding("other_value", 10)).toBe(10);
31
+ });
32
+ });
33
+
34
+ it("Border component renders null if no borderPosition", () => {
35
+ const style = { borderWidth: 5, borderRadius: 10, borderColor: "red" };
36
+
37
+ const padding = {
38
+ paddingTop: 2,
39
+ paddingRight: 3,
40
+ paddingBottom: 4,
41
+ paddingLeft: 5,
42
+ };
43
+
44
+ const borderPosition = null;
45
+
46
+ const { queryByTestId } = render(
47
+ <BorderContainerView
48
+ style={style}
49
+ testID="border-container"
50
+ borderPosition={borderPosition}
51
+ borderPaddingTop={toNumberWithDefaultZero(padding.paddingTop)}
52
+ borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
53
+ borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
54
+ borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
55
+ >
56
+ <View testID="child" />
57
+ </BorderContainerView>
58
+ );
59
+
60
+ const children = queryByTestId("border-container").children;
61
+
62
+ expect(children[1].props.testID).toBe("child");
63
+
64
+ expect(children[0].children.length).toBe(0);
65
+ });
66
+ });
@@ -28,7 +28,10 @@ const styles = StyleSheet.create({
28
28
  },
29
29
  });
30
30
 
31
- const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
31
+ export const getBorderPadding = (
32
+ borderPosition: BorderPosition,
33
+ borderWidth: number
34
+ ) => {
32
35
  switch (borderPosition) {
33
36
  case "inside":
34
37
  return 0;
@@ -38,22 +38,16 @@ function Image({
38
38
 
39
39
  const shouldRenderImg = source && !error;
40
40
 
41
- // Default placeholder image fallback is empty string because it won't rerender
42
- // in case of undefined or {uri: undefined} source
43
- // https://github.com/facebook/react-native/issues/9195
44
- const defaultPlaceHolderImage = React.useMemo(
45
- () => ({ uri: placeholderImage || "" }),
46
- [source, error]
47
- );
41
+ const _source = shouldRenderImg
42
+ ? withDimensions(source)
43
+ : { uri: placeholderImage };
48
44
 
49
45
  return (
50
- // @ts-ignore
51
46
  <MemoizedImage
52
47
  style={style as ImageStyle}
53
- onError={() => setErrorState(true)}
54
- source={
55
- shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
56
- }
48
+ onError={React.useCallback(() => setErrorState(true), [])}
49
+ // as we have defaults as "" for placeholder image, we need to pass undefined to source to not throw warnings
50
+ source={_source?.uri ? _source : undefined}
57
51
  {...R.omit(["source"], otherProps)}
58
52
  />
59
53
  );
@@ -0,0 +1,93 @@
1
+ import { ImageBorderContainer } from "../index";
2
+ import { getImageContainerMarginStyles } from "@applicaster/zapp-react-native-utils/cellUtils";
3
+
4
+ const mockValue = (key) => {
5
+ const values = {
6
+ cell_padding_top: 1,
7
+ cell_padding_left: 2,
8
+ cell_padding_right: 3,
9
+ cell_padding_bottom: 4,
10
+ image_border_size: 5,
11
+ image_focused_border_color: "red",
12
+ image_selected_border_color: "blue",
13
+ image_focused_selected_border_color: "green",
14
+ image_border_color: "black",
15
+ image_corner_radius: 10,
16
+ image_border_position: "outside",
17
+ image_border_padding_top: 6,
18
+ image_border_padding_right: 7,
19
+ image_border_padding_bottom: 8,
20
+ image_border_padding_left: 9,
21
+ image_margin_top: 11,
22
+ image_margin_left: 12,
23
+ image_margin_right: 13,
24
+ image_margin_bottom: 14,
25
+ };
26
+
27
+ return values[key] || 0;
28
+ };
29
+
30
+ describe("ImageBorderContainer", () => {
31
+ it("calculates style properties correctly", () => {
32
+ const marginStyles = getImageContainerMarginStyles({ value: mockValue });
33
+
34
+ expect(marginStyles.marginTop).toBe(11);
35
+ expect(marginStyles.marginLeft).toBe(12);
36
+ expect(marginStyles.marginRight).toBe(13);
37
+ expect(marginStyles.marginBottom).toBe(14);
38
+
39
+ const props = {
40
+ value: mockValue,
41
+ state: "default",
42
+ imageStyles: {},
43
+ };
44
+
45
+ const result = ImageBorderContainer(props);
46
+
47
+ expect(result.style.marginTop).toBe(12); // 11 + 1
48
+ expect(result.style.marginLeft).toBe(14); // 12 + 2
49
+ expect(result.style.marginRight).toBe(16); // 13 + 3
50
+ expect(result.style.marginBottom).toBe(18); // 14 + 4
51
+ expect(result.style.borderWidth).toBe(5);
52
+ expect(result.style.borderColor).toBe("black");
53
+ expect(result.style.borderRadius).toBe(10);
54
+ expect(result.additionalProps.borderPosition).toBe("outside");
55
+ expect(result.additionalProps.borderPaddingTop).toBe(6);
56
+ expect(result.additionalProps.borderPaddingRight).toBe(7);
57
+ expect(result.additionalProps.borderPaddingBottom).toBe(8);
58
+ expect(result.additionalProps.borderPaddingLeft).toBe(9);
59
+ });
60
+
61
+ it("handles focused state correctly", () => {
62
+ const props = {
63
+ value: mockValue,
64
+ state: "focused",
65
+ imageStyles: {},
66
+ };
67
+
68
+ const result = ImageBorderContainer(props);
69
+ expect(result.style.borderColor).toBe("red");
70
+ });
71
+
72
+ it("handles selected state correctly", () => {
73
+ const props = {
74
+ value: mockValue,
75
+ state: "selected",
76
+ imageStyles: {},
77
+ };
78
+
79
+ const result = ImageBorderContainer(props);
80
+ expect(result.style.borderColor).toBe("blue");
81
+ });
82
+
83
+ it("handles focused and selected state correctly", () => {
84
+ const props = {
85
+ value: mockValue,
86
+ state: "focused_selected",
87
+ imageStyles: {},
88
+ };
89
+
90
+ const result = ImageBorderContainer(props);
91
+ expect(result.style.borderColor).toBe("green");
92
+ });
93
+ });
@@ -244,7 +244,7 @@ const LiveImageComponent = (props: LiveImageProps) => {
244
244
  return (
245
245
  <>
246
246
  <Image {...props} applyMaxSize />
247
- {isShouldRender && (
247
+ {isShouldRender ? (
248
248
  <View style={[style, positionAbsolute]}>
249
249
  <PlayerLiveImage
250
250
  uri={props.uri}
@@ -257,7 +257,7 @@ const LiveImageComponent = (props: LiveImageProps) => {
257
257
  audioMutedByDefault={audio_muted_by_default}
258
258
  />
259
259
  </View>
260
- )}
260
+ ) : null}
261
261
  </>
262
262
  );
263
263
  };
@@ -6,7 +6,7 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
6
6
  <View
7
7
  onLayout={[Function]}
8
8
  style={
9
- Object {
9
+ {
10
10
  "borderRadius": 10,
11
11
  "height": 100,
12
12
  "width": 100,
@@ -16,12 +16,12 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
16
16
  <Image
17
17
  onAsyncRender={[Function]}
18
18
  source={
19
- Object {
19
+ {
20
20
  "uri": "",
21
21
  }
22
22
  }
23
23
  style={
24
- Object {
24
+ {
25
25
  "aspectRatio": 1,
26
26
  "borderRadius": 10,
27
27
  "height": 100,
@@ -17,7 +17,7 @@ const SECONDARY_IMAGE_PREFIX = "secondary_image";
17
17
 
18
18
  type ImageSizing = typeof IMAGE_SIZING_FIT | typeof IMAGE_SIZING_FILL;
19
19
 
20
- type ImagePosition = typeof IMAGE_POSITION[keyof typeof IMAGE_POSITION];
20
+ type ImagePosition = (typeof IMAGE_POSITION)[keyof typeof IMAGE_POSITION];
21
21
 
22
22
  export type DisplayMode =
23
23
  | typeof DISPLAY_MODE_FIXED
@@ -39,6 +39,6 @@ describe("image with no source", () => {
39
39
  );
40
40
 
41
41
  const testInstance = testRenderer.root;
42
- expect(testInstance.findByType(Image).props.source).toEqual({ uri: "" });
42
+ expect(testInstance.findByType(Image).props.source).toBeUndefined();
43
43
  });
44
44
  });