@applicaster/zapp-react-native-ui-components 12.0.0-rc.14 → 12.0.0-rc.140

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 (171) hide show
  1. package/Components/AudioPlayer/AudioPlayer.tsx +9 -1
  2. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayer.test.js.snap +5 -0
  3. package/Components/AudioPlayer/helpers.tsx +1 -0
  4. package/Components/BaseFocusable/index.tsx +3 -3
  5. package/Components/Cell/Cell.tsx +40 -12
  6. package/Components/Cell/CellWithFocusable.tsx +40 -19
  7. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  8. package/Components/ErrorScreen/index.tsx +1 -1
  9. package/Components/Focusable/Focusable.tsx +6 -1
  10. package/Components/Focusable/__tests__/index.android.test.tsx +72 -0
  11. package/Components/Focusable/index.android.tsx +15 -9
  12. package/Components/FocusableCell/index.tsx +7 -7
  13. package/Components/FocusableGroup/index.tsx +11 -2
  14. package/Components/FocusableList/FocusableChild.ts +26 -16
  15. package/Components/FocusableList/FocusableItem.tsx +1 -1
  16. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  17. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  18. package/Components/FocusableList/index.tsx +37 -37
  19. package/Components/FocusableScrollView/index.tsx +1 -1
  20. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  21. package/Components/FreezeWithCallback/LICENSE +21 -0
  22. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  23. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  24. package/Components/FreezeWithCallback/index.tsx +3 -0
  25. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +11 -8
  26. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  27. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  28. package/Components/HandlePlayable/HandlePlayable.tsx +4 -1
  29. package/Components/HookRenderer/index.tsx +1 -6
  30. package/Components/Image/Image.android.tsx +4 -26
  31. package/Components/Image/Image.tsx +18 -2
  32. package/Components/Image/utils/index.ts +39 -0
  33. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  34. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  35. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  36. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  37. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  38. package/Components/Layout/TV/__tests__/index.test.tsx +20 -108
  39. package/Components/Layout/TV/index.tsx +15 -84
  40. package/Components/Layout/TV/index.web.tsx +33 -0
  41. package/Components/Layout/index.tsx +15 -11
  42. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +14 -7
  43. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +86 -117
  44. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +27 -9
  45. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  46. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +16 -1
  47. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  48. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +3 -0
  49. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +2 -2
  50. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  51. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  52. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  53. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +37 -2
  54. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  55. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  56. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  57. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  58. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  59. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  60. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  61. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +6 -10
  62. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  63. package/Components/MasterCell/dataAdapter.ts +21 -7
  64. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  65. package/Components/MasterCell/index.tsx +5 -3
  66. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  67. package/Components/MasterCell/utils/index.ts +31 -34
  68. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  69. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  70. package/Components/ModalComponent/BottomSheetModalContent.tsx +121 -0
  71. package/Components/ModalComponent/Button/Item.tsx +82 -0
  72. package/Components/ModalComponent/Button/ItemIcon.tsx +60 -0
  73. package/Components/ModalComponent/Button/ItemLabel.tsx +101 -0
  74. package/Components/ModalComponent/Button/assets.ts +4 -0
  75. package/Components/ModalComponent/Button/index.tsx +111 -0
  76. package/Components/ModalComponent/Header/CloseButton.tsx +59 -0
  77. package/Components/ModalComponent/Header/Title.tsx +70 -0
  78. package/Components/ModalComponent/Header/assets.ts +4 -0
  79. package/Components/ModalComponent/Header/index.tsx +71 -0
  80. package/Components/ModalComponent/Header/utils.ts +79 -0
  81. package/Components/ModalComponent/index.tsx +38 -0
  82. package/Components/ModalComponent/utils.ts +126 -0
  83. package/Components/Navigator/StackNavigator.tsx +57 -0
  84. package/Components/Navigator/index.ts +1 -0
  85. package/Components/PlayerContainer/PlayerContainer.tsx +80 -39
  86. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  87. package/Components/PlayerImageBackground/index.tsx +34 -8
  88. package/Components/River/ComponentsMap/ComponentsMap.tsx +97 -2
  89. package/Components/River/RefreshControl.tsx +55 -1
  90. package/Components/River/TV/River.tsx +2 -25
  91. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -1
  92. package/Components/River/__tests__/componentsMap.test.js +7 -0
  93. package/Components/River/useScreenConfiguration.ts +2 -2
  94. package/Components/RouteManager/__tests__/__snapshots__/routeManager.test.js.snap +1 -0
  95. package/Components/RouteManager/__tests__/routeManager.test.js +9 -2
  96. package/Components/RouteManager/index.tsx +4 -2
  97. package/Components/Screen/TV/hooks/index.ts +1 -0
  98. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  99. package/Components/Screen/TV/index.web.tsx +178 -0
  100. package/Components/Screen/__tests__/Screen.test.tsx +43 -75
  101. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +24 -6
  102. package/Components/Screen/__tests__/navigationHandler.test.ts +1 -178
  103. package/Components/Screen/index.tsx +104 -65
  104. package/Components/Screen/navigationHandler.ts +6 -85
  105. package/Components/Screen/orientationHandler.ts +18 -7
  106. package/Components/Screen/utils.ts +16 -0
  107. package/Components/ScreenResolver/index.tsx +10 -13
  108. package/Components/Tabs/TV/styles.ts +1 -1
  109. package/Components/TrackedView/index.tsx +6 -3
  110. package/Components/Transitioner/Scene.tsx +39 -26
  111. package/Components/Transitioner/Transitioner.tsx +56 -48
  112. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  113. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  114. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -0
  115. package/Components/VideoLive/LiveImageManager.ts +65 -11
  116. package/Components/VideoLive/PlayerLiveImageComponent.tsx +132 -95
  117. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  118. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +24 -27
  119. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +60 -0
  120. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +401 -0
  121. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +172 -0
  122. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +475 -0
  123. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
  124. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  125. package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
  126. package/Components/VideoModal/PlayerDetails.tsx +21 -4
  127. package/Components/VideoModal/PlayerWrapper.tsx +115 -55
  128. package/Components/VideoModal/VideoModal.tsx +32 -43
  129. package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +6 -1
  130. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +12 -1
  131. package/Components/VideoModal/__tests__/__snapshots__/PlayerDetails.test.tsx.snap +2 -0
  132. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +358 -166
  133. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  134. package/Components/VideoModal/utils.ts +8 -5
  135. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +26 -21
  136. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  137. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +4 -5
  138. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  139. package/Components/Viewport/utils.js +1 -1
  140. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +7 -3
  141. package/Components/ZappFrameworkComponents/BarView/Buttons/BackButton.tsx +7 -2
  142. package/Components/ZappFrameworkComponents/BarView/Buttons/CloseButton.tsx +7 -2
  143. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +87 -0
  144. package/Components/ZappFrameworkComponents/Buttons/ActionButton.tsx +9 -4
  145. package/Components/ZappUIComponent/index.tsx +22 -12
  146. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  147. package/Components/index.js +2 -0
  148. package/Contexts/CachedDimensionsContext/index.ts +13 -9
  149. package/Contexts/ConfirmDialogState/index.ts +1 -1
  150. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  151. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  152. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  153. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  154. package/Contexts/FocusableGroupContext/index.ts +7 -0
  155. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  156. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  157. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  158. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  159. package/Contexts/ScreenContext/index.tsx +101 -30
  160. package/Contexts/ScreenDataContext/index.tsx +20 -0
  161. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  162. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  163. package/const.ts +6 -0
  164. package/events/index.ts +1 -0
  165. package/index.js +3 -0
  166. package/package.json +5 -5
  167. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  168. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  169. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  170. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  171. /package/Contexts/{index.js → index.ts} +0 -0
@@ -1,35 +1,38 @@
1
1
  /* eslint-disable react/prop-types */
2
2
 
3
- jest.mock("react-native", () => {
4
- const { ScrollView } = jest.requireActual("react-native");
3
+ import * as React from "react";
4
+
5
+ import { shallow } from "enzyme";
6
+ import { create, act } from "react-test-renderer";
7
+ import { shallowToJson } from "enzyme-to-json";
8
+
9
+ import { ViewportEvents } from "../../ViewportEvents";
10
+
11
+ jest.useFakeTimers();
5
12
 
13
+ jest.mock("react-native/Libraries/ReactNative/UIManager", () => {
6
14
  return {
7
- ScrollView,
8
- UIManager: {
9
- measureLayout: jest.fn((handle, parent, error, success) => {
10
- success(100, 100, 400, 400);
11
- }),
12
- },
13
- findNodeHandle: jest.fn(() => 1234),
14
- Animated: {
15
- timing: jest.fn(() => ({ start: jest.fn() })),
15
+ ...jest.requireActual("react-native/Libraries/ReactNative/NativeUIManager"),
16
+ measureLayout: (handle, parent, error, success) => {
17
+ success(100, 100, 400, 400);
16
18
  },
17
19
  };
18
20
  });
19
21
 
20
- import * as React from "react";
22
+ jest.mock("react-native/Libraries/Renderer/shims/ReactNative", () => ({
23
+ ...jest.requireActual("react-native/Libraries/Renderer/shims/ReactNative"),
24
+ findNodeHandle: () => 1234,
25
+ }));
21
26
 
22
- import { shallow } from "enzyme";
23
- import TestRenderer from "react-test-renderer";
24
- import { act } from "@testing-library/react-hooks";
25
- import { shallowToJson } from "enzyme-to-json";
27
+ const ReactNative = require("react-native");
26
28
 
27
- import { ViewportEvents } from "../../ViewportEvents";
29
+ const { Animated, ScrollView } = ReactNative;
30
+
31
+ Animated.timing = jest.fn(() => ({ start: jest.fn() }));
28
32
 
29
33
  const viewportEventsManager = new ViewportEvents(true);
30
34
 
31
- const { ScrollView } = require("react-native");
32
- const { ViewportAware } = require("../index");
35
+ const { ViewportAware } = require("../");
33
36
  const { ViewportTracker } = require("../../ViewportTracker");
34
37
 
35
38
  const TestComponent = (props) => {
@@ -116,7 +119,7 @@ describe("<ViewportAware />", () => {
116
119
  });
117
120
 
118
121
  it("invokes the onViewportEnter function when entering the viewport", () => {
119
- const wrapper = TestRenderer.create(
122
+ const wrapper = create(
120
123
  getWrapper(() => (
121
124
  <TestComponent
122
125
  preTriggerRatio={0.1}
@@ -134,6 +137,8 @@ describe("<ViewportAware />", () => {
134
137
  width: 1920,
135
138
  height: 1080,
136
139
  });
140
+
141
+ jest.runAllTimers();
137
142
  });
138
143
 
139
144
  expect(wrapper.toJSON()).toMatchSnapshot();
@@ -151,7 +156,7 @@ describe("<ViewportAware />", () => {
151
156
  });
152
157
 
153
158
  it("invokes the onViewportLeave function when leaving the viewport", () => {
154
- const wrapper = TestRenderer.create(
159
+ const wrapper = create(
155
160
  getWrapper(() => (
156
161
  <TestComponent
157
162
  preTriggerRatio={0.1}
@@ -6,23 +6,23 @@ import { callOnChildren } from "../utils";
6
6
 
7
7
  export function ViewportTracker(props) {
8
8
  const viewportEvents = useViewportEventsContext();
9
- const { children } = props;
10
- const [width, setWidth] = React.useState(0);
11
- const [height, setHeight] = React.useState(0);
12
- const [offsetX, setOffsetX] = React.useState(0);
13
- const [offsetY, setOffsetY] = React.useState(0);
14
9
  const [nodeHandle, setNodeHandle] = React.useState(null);
15
10
 
16
- const assignRef = React.useCallback(
17
- (ref) => {
18
- callOnChildren(children, "ref", ref);
19
- setNodeHandle(findNodeHandle(ref));
20
- },
21
- [children]
22
- );
11
+ const childrenRef = React.useRef(props.children);
12
+
13
+ const dimensionsRef = React.useRef({ width: 0, height: 0 });
14
+ const offsetRef = React.useRef({ offsetX: 0, offsetY: 0 });
15
+
16
+ const assignRef = React.useCallback((ref) => {
17
+ childrenRef.current?.ref?.(ref);
18
+ setNodeHandle(findNodeHandle(ref));
19
+ }, []);
23
20
 
24
21
  const onViewportChange = React.useCallback(
25
- (event, shouldMeasureLayout = true) => {
22
+ ({
23
+ event = { ...dimensionsRef.current, ...offsetRef.current },
24
+ shouldMeasureLayout = true,
25
+ } = {}) => {
26
26
  if (nodeHandle && event?.width > 0 && event?.height > 0) {
27
27
  viewportEvents?.notify?.({
28
28
  parentHandle: nodeHandle,
@@ -36,54 +36,45 @@ export function ViewportTracker(props) {
36
36
 
37
37
  const onLayout = React.useCallback(
38
38
  (event) => {
39
- callOnChildren(children, "onLayout", event);
40
- setWidth(event?.nativeEvent?.layout?.width);
41
- setHeight(event?.nativeEvent?.layout?.height);
39
+ callOnChildren(childrenRef.current, "onLayout", event);
40
+ dimensionsRef.current = event?.nativeEvent?.layout;
42
41
 
43
- onViewportChange({
44
- width: event?.nativeEvent?.layout?.width,
45
- height: event?.nativeEvent?.layout?.height,
46
- offsetX,
47
- offsetY,
48
- });
42
+ onViewportChange();
49
43
  },
50
- [offsetX, offsetY, children, onViewportChange]
44
+ [onViewportChange]
51
45
  );
52
46
 
53
47
  const onContentSizeChange = React.useCallback(
54
48
  (contentWidth, contentHeight) => {
55
49
  callOnChildren(
56
- children,
50
+ childrenRef.current,
57
51
  "onContentSizeChange",
58
52
  contentWidth,
59
53
  contentHeight
60
54
  );
61
55
 
62
- onViewportChange({ width, height, offsetX, offsetY });
56
+ onViewportChange();
63
57
  },
64
- [onViewportChange, children, width, height, offsetX, offsetY]
58
+ [onViewportChange]
65
59
  );
66
60
 
67
61
  const onScroll = React.useCallback(
68
62
  (event) => {
69
- callOnChildren(children, "onScroll", event);
70
- setOffsetX(event?.nativeEvent?.contentOffset?.x);
71
- setOffsetY(event?.nativeEvent?.contentOffset?.y);
63
+ callOnChildren(childrenRef.current, "onScroll", event);
64
+ offsetRef.current = event?.nativeEvent?.contentOffset;
72
65
 
73
- onViewportChange(
74
- {
75
- width,
76
- height,
77
- offsetX: event?.nativeEvent?.contentOffset?.x,
78
- offsetY: event?.nativeEvent?.contentOffset?.y,
79
- },
80
- false
81
- );
66
+ onViewportChange({
67
+ shouldMeasureLayout: false,
68
+ });
82
69
  },
83
- [onViewportChange, width, height, children]
70
+ [onViewportChange]
84
71
  );
85
72
 
86
- return React.cloneElement(React.Children.only(children), {
73
+ React.useEffect(() => {
74
+ childrenRef.current = props.children;
75
+ }, [props.children]);
76
+
77
+ return React.cloneElement(React.Children.only(props.children), {
87
78
  ref: assignRef,
88
79
  onLayout,
89
80
  onContentSizeChange,
@@ -5,11 +5,10 @@ import { ViewportEvents } from "../../ViewportEvents";
5
5
 
6
6
  const TestComponent = () => <ScrollView />;
7
7
 
8
- jest.mock("react-native", () => {
9
- return {
10
- findNodeHandle: jest.fn(() => 1234),
11
- };
12
- });
8
+ jest.mock("react-native/Libraries/Renderer/shims/ReactNative", () => ({
9
+ ...jest.requireActual("react-native/Libraries/Renderer/shims/ReactNative"),
10
+ findNodeHandle: () => 1234,
11
+ }));
13
12
 
14
13
  const viewportEventsManager = new ViewportEvents(true);
15
14
 
@@ -6,6 +6,7 @@ import { ViewportEventContext, ViewportEvents } from "../ViewportEvents";
6
6
  type Props = {
7
7
  viewportEventsManager?: any;
8
8
  children: React.ReactNode;
9
+ flatListRef?: any; // this ref is used to bind to children component
9
10
  };
10
11
 
11
12
  export function ViewportTracker(props: Props) {
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
 
3
3
  export function callOnChildren(children, method, ...args) {
4
4
  const childMethod = React.Children.only(children).props[method];
5
- childMethod && childMethod(...args);
5
+ typeof childMethod === "function" && childMethod?.(...args);
6
6
  }
7
7
 
8
8
  export function isInViewport(
@@ -1,5 +1,6 @@
1
1
  import React from "react";
2
- import { View, StyleSheet } from "react-native";
2
+ import { StyleSheet } from "react-native";
3
+ import { SafeAreaView } from "react-native-safe-area-context";
3
4
 
4
5
  import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils";
5
6
  import CloseButton from "./Buttons/CloseButton";
@@ -44,11 +45,14 @@ const BarView = ({ screenStyles, barState = defaultState }: Props) => {
44
45
  );
45
46
 
46
47
  return (
47
- <View style={[style.view, isRTL ? style.rtlStyle : {}]}>
48
+ <SafeAreaView
49
+ style={[style.view, isRTL ? style.rtlStyle : {}]}
50
+ testID="BarView-safeAreaView"
51
+ >
48
52
  {backButton}
49
53
  <BarTitle title={barState.title} screenStyles={screenStyles} />
50
54
  {closeButton}
51
- </View>
55
+ </SafeAreaView>
52
56
  );
53
57
  };
54
58
 
@@ -11,12 +11,16 @@ const renderEmptyView = () => {
11
11
  return <View style={emptyViewStyle} />;
12
12
  };
13
13
 
14
- type FloatingButtonProps = {
14
+ type FloatingButtonProps = Testable & {
15
15
  onPress: () => void;
16
16
  screenStyles: any;
17
17
  };
18
18
 
19
- const BackButton = ({ onPress, screenStyles }: FloatingButtonProps) => {
19
+ const BackButton = ({
20
+ onPress,
21
+ screenStyles,
22
+ testID = "backButton",
23
+ }: FloatingButtonProps) => {
20
24
  const containerStyle: any = {};
21
25
 
22
26
  if (!onPress) {
@@ -25,6 +29,7 @@ const BackButton = ({ onPress, screenStyles }: FloatingButtonProps) => {
25
29
 
26
30
  return (
27
31
  <ActionButton
32
+ testID={`${testID}-actionButton`}
28
33
  onPress={onPress}
29
34
  buttonStyles={containerStyle}
30
35
  imageUrl={screenStyles.back_button_image}
@@ -11,12 +11,16 @@ const renderEmptyView = () => {
11
11
  return <View style={emptyViewStyle} />;
12
12
  };
13
13
 
14
- type CloseButtonProps = {
14
+ type CloseButtonProps = Testable & {
15
15
  onPress: () => void;
16
16
  screenStyles?: any;
17
17
  };
18
18
 
19
- const CloseButton = ({ onPress, screenStyles }: CloseButtonProps) => {
19
+ const CloseButton = ({
20
+ onPress,
21
+ screenStyles,
22
+ testID = "closeButton",
23
+ }: CloseButtonProps) => {
20
24
  const containerStyle: any = {};
21
25
 
22
26
  if (!onPress) {
@@ -25,6 +29,7 @@ const CloseButton = ({ onPress, screenStyles }: CloseButtonProps) => {
25
29
 
26
30
  return (
27
31
  <ActionButton
32
+ testID={`${testID}-actionButton`}
28
33
  onPress={onPress}
29
34
  buttonStyles={containerStyle}
30
35
  imageUrl={screenStyles.close_button_image}
@@ -0,0 +1,87 @@
1
+ import React from "react";
2
+ import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
3
+ import { fireEvent } from "@testing-library/react-native";
4
+
5
+ const mockUseIsRTL = jest.fn(() => true);
6
+
7
+ jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
8
+ useIsRTL: mockUseIsRTL,
9
+ }));
10
+
11
+ const BarView = require("../BarView").default;
12
+
13
+ describe("BarView", () => {
14
+ const screenStyles = {
15
+ title_font_color: "#000",
16
+ title_font_android: "Roboto",
17
+ title_font_ios: "San Francisco",
18
+ title_font_size: "16",
19
+ background_color: "#fff",
20
+ };
21
+
22
+ const barState = {
23
+ backHandler: jest.fn(),
24
+ closeHandler: jest.fn(),
25
+ title: "Test Title",
26
+ };
27
+
28
+ beforeEach(() => {
29
+ jest.clearAllMocks();
30
+ });
31
+
32
+ it("renders without crashing", () => {
33
+ mockUseIsRTL.mockReturnValue(false);
34
+
35
+ const { getByText } = renderWithProviders(
36
+ <BarView screenStyles={screenStyles} barState={barState} />
37
+ );
38
+
39
+ expect(getByText("Test Title")).toBeTruthy();
40
+ });
41
+
42
+ it("applies RTL styles when isRTL is true", () => {
43
+ mockUseIsRTL.mockReturnValue(true);
44
+
45
+ const { getByTestId } = renderWithProviders(
46
+ <BarView screenStyles={screenStyles} barState={barState} />
47
+ );
48
+
49
+ expect(getByTestId("BarView-safeAreaView").props.style).toContainEqual({
50
+ transform: [{ scaleX: -1 }],
51
+ });
52
+ });
53
+
54
+ it("does not apply RTL styles when isRTL is false", () => {
55
+ mockUseIsRTL.mockReturnValue(false);
56
+
57
+ const { getByTestId } = renderWithProviders(
58
+ <BarView screenStyles={screenStyles} barState={barState} />
59
+ );
60
+
61
+ expect(getByTestId("BarView-safeAreaView").props.style).not.toContainEqual({
62
+ transform: [{ scaleX: -1 }],
63
+ });
64
+ });
65
+
66
+ it("calls closeHandler when CloseButton is pressed", () => {
67
+ mockUseIsRTL.mockReturnValue(false);
68
+
69
+ const { getByTestId } = renderWithProviders(
70
+ <BarView screenStyles={screenStyles} barState={barState} />
71
+ );
72
+
73
+ fireEvent.press(getByTestId("closeButton-actionButton-touchable"));
74
+ expect(barState.closeHandler).toHaveBeenCalled();
75
+ });
76
+
77
+ it("calls backHandler when BackButton is pressed", () => {
78
+ mockUseIsRTL.mockReturnValue(false);
79
+
80
+ const { getByTestId } = renderWithProviders(
81
+ <BarView screenStyles={screenStyles} barState={barState} />
82
+ );
83
+
84
+ fireEvent.press(getByTestId("backButton-actionButton-touchable"));
85
+ expect(barState.backHandler).toHaveBeenCalled();
86
+ });
87
+ });
@@ -28,7 +28,7 @@ styles: {
28
28
  }
29
29
  */
30
30
 
31
- type ActionButtonProps = {
31
+ type ActionButtonProps = Testable & {
32
32
  onPress: () => void;
33
33
  buttonStyles: any;
34
34
  imageUrl: string;
@@ -46,11 +46,16 @@ const ActionButton = ({
46
46
  buttonStyles,
47
47
  imageUrl,
48
48
  imageStyle = closeButtonStyle,
49
+ testID,
49
50
  }: ActionButtonProps) => {
50
51
  return (
51
- <View style={buttonStyles}>
52
- <TouchableOpacity onPress={onPress}>
53
- <Image source={{ uri: imageUrl }} style={imageStyle} />
52
+ <View style={buttonStyles} testID={testID}>
53
+ <TouchableOpacity onPress={onPress} testID={`${testID}-touchable`}>
54
+ <Image
55
+ source={{ uri: imageUrl }}
56
+ style={imageStyle}
57
+ testID={`${testID}-image`}
58
+ />
54
59
  </TouchableOpacity>
55
60
  </View>
56
61
  );
@@ -10,7 +10,7 @@ import {
10
10
 
11
11
  import { Placeholder } from "./Placeholder";
12
12
  import { useInitialLoading } from "./hooks";
13
- import { FocusableContext } from "../Focusable/index.android";
13
+ import { useFocusable } from "../Focusable/index.android";
14
14
 
15
15
  type ReactComponent = React.ComponentType<any>;
16
16
 
@@ -26,13 +26,23 @@ type ZappUIComponentType = {
26
26
  options?: Options;
27
27
  };
28
28
 
29
- const renderComponent = (Component: ReactComponent | undefined, props) => {
30
- if (Component) {
31
- const { component, parent } = props;
29
+ type RenderComponentProps = any;
30
+
31
+ const RenderComponent = (
32
+ Component: ReactComponent | undefined,
33
+ args: RenderComponentProps
34
+ ) => {
35
+ const { component, parent } = args;
32
36
 
37
+ const contextValue = React.useMemo(
38
+ () => ({ ...component, parent }),
39
+ [component, parent]
40
+ );
41
+
42
+ if (Component) {
33
43
  return (
34
- <UIComponentContext.Provider value={{ ...component, parent }}>
35
- <Component {...props} />
44
+ <UIComponentContext.Provider value={contextValue}>
45
+ <Component {...args} />
36
46
  </UIComponentContext.Provider>
37
47
  );
38
48
  }
@@ -117,7 +127,7 @@ New signature: {Component, ErrorComponent, LoadingComponent, options}`
117
127
  props;
118
128
 
119
129
  const parent = useUIComponentContext();
120
- const { setIsFocusable } = React.useContext(FocusableContext) || {};
130
+ const { setIsFocusable } = useFocusable();
121
131
 
122
132
  const isLoading = toBooleanWithDefaultFalse(zappPipesData?.loading);
123
133
  const isInitialLoading = useInitialLoading(isLoading);
@@ -154,11 +164,11 @@ New signature: {Component, ErrorComponent, LoadingComponent, options}`
154
164
  }, [props.component?.rules?.component_cells_focusable]);
155
165
 
156
166
  if (isDataSourceEmpty(props) && allowsEmptyDataSource) {
157
- return renderComponent(Component, componentProps);
167
+ return RenderComponent(Component, componentProps);
158
168
  }
159
169
 
160
- if (isLoading && !zappPipesData.data && isInitialLoading) {
161
- return renderComponent(LoadingComponent || Placeholder, componentProps);
170
+ if (isLoading && !zappPipesData?.data && isInitialLoading) {
171
+ return RenderComponent(LoadingComponent || Placeholder, componentProps);
162
172
  }
163
173
 
164
174
  if (zappPipesData?.error) {
@@ -167,9 +177,9 @@ New signature: {Component, ErrorComponent, LoadingComponent, options}`
167
177
  index: componentIndex,
168
178
  });
169
179
 
170
- return renderComponent(ErrorComponent, componentProps);
180
+ return RenderComponent(ErrorComponent, componentProps);
171
181
  }
172
182
 
173
- return renderComponent(Component, componentProps);
183
+ return RenderComponent(Component, componentProps);
174
184
  };
175
185
  }
@@ -16,9 +16,9 @@ exports[`DefaultCellRenderer returns a react component and it renders correctly
16
16
  store={null}
17
17
  style={
18
18
  Object {
19
- "backgroundColor": "#CCC",
19
+ "backgroundColor": "transparent",
20
20
  "margin": 12,
21
- "opacity": 0.6,
21
+ "opacity": 1,
22
22
  "width": 169,
23
23
  }
24
24
  }
@@ -105,7 +105,7 @@ exports[`DefaultCellRenderer returns a react component for LG TVs 1`] = `
105
105
  store={null}
106
106
  style={
107
107
  Object {
108
- "backgroundColor": "rgba(252,70,27,1.0)",
108
+ "backgroundColor": "rgba(47,47,47,1.0)",
109
109
  "borderRadius": 4,
110
110
  "flex": 1,
111
111
  "overflow": "hidden",
@@ -238,7 +238,7 @@ exports[`DefaultCellRenderer returns a react component for Samsung TVs 1`] = `
238
238
  store={null}
239
239
  style={
240
240
  Object {
241
- "backgroundColor": "rgba(252,70,27,1.0)",
241
+ "backgroundColor": "rgba(47,47,47,1.0)",
242
242
  "borderRadius": 4,
243
243
  "flex": 1,
244
244
  "overflow": "hidden",
@@ -4,6 +4,8 @@ export { River } from "./River";
4
4
 
5
5
  export { PlayerContainer } from "./PlayerContainer";
6
6
 
7
+ export { ModalComponent } from "./ModalComponent";
8
+
7
9
  export { ContentScreen } from "./ContentScreen";
8
10
 
9
11
  export { TextInputTv } from "./TextInputTv";
@@ -1,5 +1,6 @@
1
1
  import { create, useStore } from "zustand";
2
2
  import { produce } from "immer";
3
+ import { useCallback, useMemo } from "react";
3
4
 
4
5
  type Dimensions = {
5
6
  width: Option<number>;
@@ -31,16 +32,19 @@ export const useCachedDimensions = (id: string) => {
31
32
  dimensionsStore
32
33
  ) as DimensionsState;
33
34
 
34
- const getCachedDimensions = () => {
35
+ const getCachedDimensions = useCallback(() => {
35
36
  return dimensions[id] || initialDimensions();
36
- };
37
+ }, [dimensions?.[id], id]);
37
38
 
38
- const handleSetCachedDimensions = (newDimensions) => {
39
+ const handleSetCachedDimensions = useCallback((newDimensions) => {
39
40
  setCachedDimensions(id, newDimensions);
40
- };
41
-
42
- return {
43
- getCachedDimensions,
44
- setCachedDimensions: handleSetCachedDimensions,
45
- };
41
+ }, []);
42
+
43
+ return useMemo(
44
+ () => ({
45
+ getCachedDimensions,
46
+ setCachedDimensions: handleSetCachedDimensions,
47
+ }),
48
+ [getCachedDimensions, handleSetCachedDimensions]
49
+ );
46
50
  };
@@ -2,7 +2,7 @@ import { createStore } from "zustand/vanilla";
2
2
  import { useStore } from "zustand";
3
3
  import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
4
4
 
5
- interface ConfirmationDialogState {
5
+ export interface ConfirmationDialogState {
6
6
  isDialogVisible: boolean;
7
7
  messages: string;
8
8
  confirmAction: () => void;
@@ -0,0 +1,3 @@
1
+ import * as React from "react";
2
+
3
+ export const FocusableGroupContext = React.createContext<string | null>(null);
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import { FocusableGroupContext } from "./FocusableGroupContext";
3
+
4
+ type ProviderArgs = {
5
+ id: string;
6
+ children: React.ReactNode;
7
+ };
8
+
9
+ export const FocusableGroupContextProvider = ({
10
+ id,
11
+ children,
12
+ }: ProviderArgs) => (
13
+ <FocusableGroupContext.Provider value={id}>
14
+ {children}
15
+ </FocusableGroupContext.Provider>
16
+ );
@@ -0,0 +1,22 @@
1
+ import React, { useContext } from "react";
2
+ import { Text } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+ import { FocusableGroupContextProvider, FocusableGroupContext } from "../";
5
+
6
+ const MockComponent = () => {
7
+ const focusableContext = useContext(FocusableGroupContext) || "default";
8
+
9
+ return <Text testID={focusableContext} />;
10
+ };
11
+
12
+ describe("FocusableGroupContextProvider", () => {
13
+ it("should provide focusable context to the wrapped component", () => {
14
+ const { getByTestId } = render(
15
+ <FocusableGroupContextProvider id="testId">
16
+ <MockComponent />
17
+ </FocusableGroupContextProvider>
18
+ );
19
+
20
+ expect(getByTestId("testId")).toBeTruthy();
21
+ });
22
+ });
@@ -0,0 +1,26 @@
1
+ import React from "react";
2
+ import { Text } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+ import { withFocusableContext } from "../withFocusableContext";
5
+ import { FocusableGroupContext } from "../FocusableGroupContext";
6
+
7
+ const MockComponent = ({ groupId }: { groupId: string }) => (
8
+ <Text testID={groupId} />
9
+ );
10
+
11
+ const WrappedComponent = withFocusableContext(MockComponent);
12
+
13
+ describe("withFocusableContext HOC", () => {
14
+ it("should provide focusable context to the wrapped component", () => {
15
+ const { getByTestId } = render(<WrappedComponent />, {
16
+ // eslint-disable-next-line react/display-name
17
+ wrapper: ({ children }: { children: React.ReactElement }) => (
18
+ <FocusableGroupContext.Provider value="test">
19
+ {children}
20
+ </FocusableGroupContext.Provider>
21
+ ),
22
+ });
23
+
24
+ expect(getByTestId("test")).toBeTruthy();
25
+ });
26
+ });
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { FocusableGroupContext } from "./FocusableGroupContext";
3
+ import { FocusableGroupContextProvider } from "./FocusableGroupContextProvider";
4
+
5
+ export { FocusableGroupContext, FocusableGroupContextProvider };
6
+
7
+ export const useGroupId = () => React.useContext(FocusableGroupContext);