@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5251355865 → 12.0.0-alpha.5347482137

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 (112) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +7 -7
  3. package/Components/Cell/CellWithFocusable.tsx +40 -19
  4. package/Components/Cell/TvOSCellComponent.tsx +5 -9
  5. package/Components/ErrorScreen/index.tsx +1 -1
  6. package/Components/Focusable/Focusable.tsx +6 -1
  7. package/Components/FocusableCell/index.tsx +7 -7
  8. package/Components/FocusableGroup/index.tsx +11 -2
  9. package/Components/FocusableList/FocusableChild.ts +24 -14
  10. package/Components/FocusableList/FocusableItem.tsx +1 -1
  11. package/Components/FocusableList/FocusableListItemWrapper.tsx +76 -0
  12. package/Components/FocusableList/hooks/useCellState.android.ts +3 -1
  13. package/Components/FocusableList/index.tsx +26 -25
  14. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +6 -3
  15. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  17. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  18. package/Components/Image/Image.android.tsx +3 -25
  19. package/Components/Image/Image.tsx +18 -2
  20. package/Components/Image/utils/index.ts +39 -0
  21. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  22. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -10
  23. package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
  24. package/Components/Layout/TV/index.tsx +12 -11
  25. package/Components/Layout/TV/index.web.tsx +33 -0
  26. package/Components/Layout/index.tsx +15 -11
  27. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
  28. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
  29. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  30. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
  31. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +9 -26
  32. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  33. package/Components/MasterCell/DefaultComponents/Image/hooks/useImageSize.ts +65 -0
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  35. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +17 -2
  36. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +3 -2
  37. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  38. package/Components/MasterCell/DefaultComponents/Toggle.tsx +5 -1
  39. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  40. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  41. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  42. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  43. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  44. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  45. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  46. package/Components/MasterCell/utils/index.ts +33 -35
  47. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  48. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  49. package/Components/Navigator/StackNavigator.tsx +54 -0
  50. package/Components/Navigator/index.ts +1 -0
  51. package/Components/PlayerContainer/PlayerContainer.tsx +69 -31
  52. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -1
  53. package/Components/PlayerImageBackground/index.tsx +34 -8
  54. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  55. package/Components/River/RefreshControl.tsx +55 -1
  56. package/Components/River/TV/River.tsx +2 -25
  57. package/Components/River/__tests__/componentsMap.test.js +7 -0
  58. package/Components/River/useScreenConfiguration.ts +2 -2
  59. package/Components/Screen/TV/hooks/index.ts +3 -0
  60. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  61. package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
  62. package/Components/Screen/TV/index.web.tsx +186 -0
  63. package/Components/Screen/__tests__/Screen.test.tsx +8 -1
  64. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +14 -0
  65. package/Components/Screen/index.tsx +78 -30
  66. package/Components/ScreenResolver/index.tsx +3 -1
  67. package/Components/Tabs/TV/styles.ts +1 -1
  68. package/Components/Transitioner/Scene.tsx +24 -19
  69. package/Components/Transitioner/Transitioner.tsx +21 -28
  70. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
  71. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
  72. package/Components/VideoLive/LiveImageManager.ts +28 -1
  73. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -28
  74. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +45 -3
  75. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +18 -28
  76. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
  77. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +352 -0
  78. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
  79. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
  80. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
  81. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  82. package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
  83. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  84. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  85. package/Components/VideoModal/VideoModal.tsx +23 -40
  86. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  87. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +157 -105
  88. package/Components/VideoModal/utils.ts +8 -5
  89. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  90. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  91. package/Components/Viewport/utils.js +1 -1
  92. package/Components/ZappUIComponent/index.tsx +22 -12
  93. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  94. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  95. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  96. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  97. package/Contexts/FocusableGroupContext/index.ts +7 -0
  98. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  99. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  100. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  101. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  102. package/Contexts/ScreenContext/index.tsx +94 -28
  103. package/Contexts/ScreenDataContext/index.tsx +20 -0
  104. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  105. package/Decorators/ComponentsMapWrapper/withComponentsMapProvider.tsx +7 -2
  106. package/events/index.ts +1 -0
  107. package/package.json +5 -5
  108. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  109. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  110. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  111. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  112. /package/Contexts/{index.js → index.ts} +0 -0
@@ -23,9 +23,6 @@ exports[`PlayerWrapper renders inline 1`] = `
23
23
  }
24
24
  style={
25
25
  Array [
26
- Object {
27
- "flexDirection": "column",
28
- },
29
26
  Object {
30
27
  "backgroundColor": "transparent",
31
28
  },
@@ -38,44 +35,50 @@ exports[`PlayerWrapper renders inline 1`] = `
38
35
  }
39
36
  >
40
37
  <View
38
+ additionalData={
39
+ Object {
40
+ "saveArea": true,
41
+ }
42
+ }
43
+ animationType="moveUpComponent"
41
44
  style={
42
45
  Array [
43
46
  Object {
44
- "alignSelf": "center",
45
- "position": "relative",
46
- "zIndex": 200,
47
+ "flexDirection": "column",
47
48
  },
48
49
  Object {
49
- "aspectRatio": 1.7777777777777777,
50
- "height": undefined,
51
- "width": "100%",
50
+ "flex": 1,
52
51
  },
53
- Object {},
54
- Object {},
55
52
  ]
56
53
  }
57
- testID="test-player-container"
58
- />
59
- <View
60
- configuration={
61
- Object {
62
- "tablet_landscape_player_container_background_color": "red",
63
- "tablet_landscape_sidebar_width": "35%",
64
- }
65
- }
66
- entry={
67
- Object {
68
- "id": "test",
54
+ >
55
+ <View
56
+ style={
57
+ Array [
58
+ Object {
59
+ "alignSelf": "center",
60
+ "position": "relative",
61
+ "zIndex": 200,
62
+ },
63
+ Object {
64
+ "aspectRatio": 1.7777777777777777,
65
+ "height": undefined,
66
+ "width": "100%",
67
+ },
68
+ Object {},
69
+ Object {},
70
+ ]
69
71
  }
70
- }
71
- isTabletLandscape={false}
72
- style={
73
- Object {
74
- "flex": 1,
75
- "paddingTop": 20,
72
+ testID="test-player-container"
73
+ />
74
+ <View
75
+ style={
76
+ Object {
77
+ "flex": 1,
78
+ }
76
79
  }
77
- }
78
- />
80
+ />
81
+ </View>
79
82
  </RNCSafeAreaView>
80
83
  </RNCSafeAreaProvider>
81
84
  `;
@@ -95,22 +98,19 @@ exports[`PlayerWrapper renders inline and docked 1`] = `
95
98
  <RNCSafeAreaView
96
99
  edges={
97
100
  Object {
98
- "bottom": "additive",
101
+ "bottom": "off",
99
102
  "left": "off",
100
103
  "right": "off",
101
- "top": "off",
104
+ "top": "additive",
102
105
  }
103
106
  }
104
107
  style={
105
108
  Array [
106
- Object {
107
- "flexDirection": "column",
108
- },
109
109
  Object {
110
110
  "backgroundColor": "transparent",
111
111
  },
112
112
  Object {
113
- "height": 50,
113
+ "height": undefined,
114
114
  "width": 300,
115
115
  },
116
116
  Object {},
@@ -118,32 +118,50 @@ exports[`PlayerWrapper renders inline and docked 1`] = `
118
118
  }
119
119
  >
120
120
  <View
121
+ additionalData={
122
+ Object {
123
+ "saveArea": true,
124
+ }
125
+ }
126
+ animationType="moveUpComponent"
121
127
  style={
122
128
  Array [
123
129
  Object {
124
- "alignSelf": "center",
125
- "position": "relative",
126
- "zIndex": 200,
130
+ "flexDirection": "column",
127
131
  },
128
132
  Object {
129
- "aspectRatio": undefined,
130
- "height": 50,
131
- "width": "100%",
133
+ "flex": 1,
132
134
  },
133
- Object {},
134
- Object {},
135
135
  ]
136
136
  }
137
- testID="test-player-container"
138
- />
139
- <View
140
- style={
141
- Object {
142
- "flex": 1,
143
- "paddingTop": 20,
137
+ >
138
+ <View
139
+ style={
140
+ Array [
141
+ Object {
142
+ "alignSelf": "center",
143
+ "position": "relative",
144
+ "zIndex": 200,
145
+ },
146
+ Object {
147
+ "aspectRatio": undefined,
148
+ "height": undefined,
149
+ "width": "100%",
150
+ },
151
+ Object {},
152
+ Object {},
153
+ ]
144
154
  }
145
- }
146
- />
155
+ testID="test-player-container"
156
+ />
157
+ <View
158
+ style={
159
+ Object {
160
+ "flex": 1,
161
+ }
162
+ }
163
+ />
164
+ </View>
147
165
  </RNCSafeAreaView>
148
166
  </RNCSafeAreaProvider>
149
167
  `;
@@ -171,9 +189,6 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
171
189
  }
172
190
  style={
173
191
  Array [
174
- Object {
175
- "flexDirection": "row",
176
- },
177
192
  Object {
178
193
  "backgroundColor": "red",
179
194
  },
@@ -186,44 +201,66 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
186
201
  }
187
202
  >
188
203
  <View
204
+ additionalData={
205
+ Object {
206
+ "saveArea": true,
207
+ }
208
+ }
209
+ animationType="moveUpComponent"
189
210
  style={
190
211
  Array [
191
212
  Object {
192
- "alignSelf": "center",
193
- "position": "relative",
194
- "zIndex": 200,
213
+ "flexDirection": "row",
195
214
  },
196
215
  Object {
197
- "aspectRatio": 1.7777777777777777,
198
- "height": undefined,
199
- "width": 195,
216
+ "flex": 1,
200
217
  },
201
- Object {},
202
- Object {},
203
218
  ]
204
219
  }
205
- testID="test-player-container"
206
- />
207
- <View
208
- configuration={
209
- Object {
210
- "tablet_landscape_player_container_background_color": "red",
211
- "tablet_landscape_sidebar_width": "35%",
212
- }
213
- }
214
- entry={
215
- Object {
216
- "id": "test",
220
+ >
221
+ <View
222
+ style={
223
+ Array [
224
+ Object {
225
+ "alignSelf": "center",
226
+ "position": "relative",
227
+ "zIndex": 200,
228
+ },
229
+ Object {
230
+ "aspectRatio": 1.7777777777777777,
231
+ "height": undefined,
232
+ "width": 195,
233
+ },
234
+ Object {},
235
+ Object {},
236
+ ]
217
237
  }
218
- }
219
- isTabletLandscape={true}
220
- style={
221
- Object {
222
- "flex": 1,
223
- "paddingTop": 20,
238
+ testID="test-player-container"
239
+ >
240
+ <View
241
+ additionalData={
242
+ Object {
243
+ "disableAnimatedComponent": false,
244
+ "resetAnimationValue": undefined,
245
+ "useLayoutMeasure": true,
246
+ }
247
+ }
248
+ animationType="moveUpComponent"
249
+ style={
250
+ Object {
251
+ "flex": 1,
252
+ }
253
+ }
254
+ />
255
+ </View>
256
+ <View
257
+ style={
258
+ Object {
259
+ "flex": 1,
260
+ }
224
261
  }
225
- }
226
- />
262
+ />
263
+ </View>
227
264
  </RNCSafeAreaView>
228
265
  </RNCSafeAreaProvider>
229
266
  `;
@@ -251,9 +288,6 @@ exports[`PlayerWrapper renders properly 1`] = `
251
288
  }
252
289
  style={
253
290
  Array [
254
- Object {
255
- "flexDirection": "column",
256
- },
257
291
  Object {
258
292
  "backgroundColor": "transparent",
259
293
  },
@@ -266,32 +300,50 @@ exports[`PlayerWrapper renders properly 1`] = `
266
300
  }
267
301
  >
268
302
  <View
303
+ additionalData={
304
+ Object {
305
+ "saveArea": true,
306
+ }
307
+ }
308
+ animationType="moveUpComponent"
269
309
  style={
270
310
  Array [
271
311
  Object {
272
- "alignSelf": "center",
273
- "position": "relative",
274
- "zIndex": 200,
312
+ "flexDirection": "column",
275
313
  },
276
314
  Object {
277
- "aspectRatio": 1.7786666666666666,
278
- "height": undefined,
279
- "width": "100%",
315
+ "flex": 1,
280
316
  },
281
- Object {},
282
- Object {},
283
317
  ]
284
318
  }
285
- testID="test-player-container"
286
- />
287
- <View
288
- style={
289
- Object {
290
- "flex": 1,
291
- "paddingTop": 20,
319
+ >
320
+ <View
321
+ style={
322
+ Array [
323
+ Object {
324
+ "alignSelf": "center",
325
+ "position": "relative",
326
+ "zIndex": 200,
327
+ },
328
+ Object {
329
+ "aspectRatio": 1.7786666666666666,
330
+ "height": undefined,
331
+ "width": "100%",
332
+ },
333
+ Object {},
334
+ Object {},
335
+ ]
292
336
  }
293
- }
294
- />
337
+ testID="test-player-container"
338
+ />
339
+ <View
340
+ style={
341
+ Object {
342
+ "flex": 1,
343
+ }
344
+ }
345
+ />
346
+ </View>
295
347
  </RNCSafeAreaView>
296
348
  </RNCSafeAreaProvider>
297
349
  `;
@@ -23,18 +23,21 @@ export const useConfiguration = () => {
23
23
  const targetScreenConfiguration = rivers?.[targetScreenId];
24
24
 
25
25
  const pluginConfiguration =
26
- pluginConfigurations[targetScreenConfiguration.type].configuration_json;
26
+ pluginConfigurations[targetScreenConfiguration?.type]?.configuration_json;
27
27
 
28
28
  const playerPluginConfig = playerManager.getPluginConfiguration();
29
29
 
30
30
  const config = R.mergeRight(playerPluginConfig, {
31
- ...targetScreenConfiguration.general,
32
- ...targetScreenConfiguration.styles,
31
+ ...targetScreenConfiguration?.general,
32
+ ...targetScreenConfiguration?.styles,
33
33
  ...pluginConfiguration,
34
34
  });
35
35
 
36
- const { minimised_height, minimised_height_tablet, modal_background_color } =
37
- config;
36
+ const {
37
+ minimised_height = 0,
38
+ minimised_height_tablet = 0,
39
+ modal_background_color,
40
+ } = config;
38
41
 
39
42
  const minimisedHeight = useIsTablet()
40
43
  ? minimised_height_tablet
@@ -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,
@@ -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(
@@ -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
  }
@@ -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);