@applicaster/zapp-react-native-ui-components 15.0.0-rc.11 → 15.0.0-rc.110

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 (121) hide show
  1. package/Components/AnimatedInOut/index.tsx +69 -26
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/Cell.tsx +14 -3
  4. package/Components/Cell/CellWithFocusable.tsx +9 -0
  5. package/Components/Cell/FocusableWrapper.tsx +47 -0
  6. package/Components/Cell/TvOSCellComponent.tsx +106 -19
  7. package/Components/Focusable/Focusable.tsx +4 -2
  8. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  9. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  10. package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
  11. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  12. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
  13. package/Components/HandlePlayable/HandlePlayable.tsx +33 -94
  14. package/Components/HandlePlayable/const.ts +3 -0
  15. package/Components/HandlePlayable/utils.ts +105 -0
  16. package/Components/Layout/TV/LayoutBackground.tsx +5 -2
  17. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  18. package/Components/Layout/TV/index.tsx +3 -4
  19. package/Components/Layout/TV/index.web.tsx +3 -4
  20. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  21. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
  22. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
  23. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
  24. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  25. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  26. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  27. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +10 -6
  28. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
  29. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
  30. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
  31. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
  32. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  33. package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
  34. package/Components/MasterCell/DefaultComponents/Text/index.tsx +8 -8
  35. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -2
  36. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +233 -11
  37. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +19 -15
  38. package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
  39. package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
  40. package/Components/MasterCell/index.tsx +2 -0
  41. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
  42. package/Components/MasterCell/utils/index.ts +61 -31
  43. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
  44. package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
  45. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
  46. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
  47. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  48. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  49. package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
  50. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  51. package/Components/PlayerContainer/PlayerContainer.tsx +43 -55
  52. package/Components/PlayerImageBackground/index.tsx +3 -22
  53. package/Components/River/ComponentsMap/ComponentsMap.tsx +16 -0
  54. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  55. package/Components/River/TV/River.tsx +31 -14
  56. package/Components/River/TV/index.tsx +8 -4
  57. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  58. package/Components/River/TV/utils/index.ts +4 -0
  59. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  60. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  61. package/Components/River/__tests__/componentsMap.test.js +38 -0
  62. package/Components/Screen/TV/index.web.tsx +4 -2
  63. package/Components/Screen/__tests__/Screen.test.tsx +66 -42
  64. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  65. package/Components/Screen/hooks.ts +75 -6
  66. package/Components/Screen/index.tsx +9 -4
  67. package/Components/Screen/navigationHandler.ts +49 -24
  68. package/Components/Screen/orientationHandler.ts +10 -13
  69. package/Components/ScreenResolver/index.tsx +26 -16
  70. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  71. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  72. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +44 -26
  73. package/Components/Tabs/TV/Tabs.tsx +20 -3
  74. package/Components/Tabs/TabContent.tsx +7 -4
  75. package/Components/Transitioner/Scene.tsx +10 -3
  76. package/Components/Transitioner/index.js +3 -3
  77. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  78. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
  79. package/Components/VideoModal/ModalAnimation/index.ts +2 -13
  80. package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
  81. package/Components/VideoModal/PlayerWrapper.tsx +14 -88
  82. package/Components/VideoModal/VideoModal.tsx +1 -5
  83. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  84. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  85. package/Components/VideoModal/hooks/useModalSize.ts +10 -5
  86. package/Components/VideoModal/playerWrapperStyle.ts +70 -0
  87. package/Components/VideoModal/playerWrapperUtils.ts +91 -0
  88. package/Components/VideoModal/utils.ts +19 -9
  89. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  90. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  91. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  92. package/Contexts/ScreenContext/index.tsx +54 -19
  93. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  94. package/Contexts/ZappHookModalContext/index.tsx +37 -61
  95. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  96. package/Contexts/index.ts +0 -2
  97. package/Decorators/Analytics/index.tsx +6 -5
  98. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  99. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  100. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  101. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  102. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  103. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  104. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  105. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  106. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  107. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  108. package/Helpers/DataSourceHelper/index.ts +19 -0
  109. package/events/index.ts +3 -0
  110. package/events/scrollEndReached.ts +15 -0
  111. package/index.d.ts +7 -0
  112. package/package.json +6 -5
  113. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  114. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
  115. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
  116. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
  117. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
  118. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
  119. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
  120. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
  121. package/Helpers/DataSourceHelper/index.js +0 -19
@@ -1,4 +1,4 @@
1
- import React, { useCallback } from "react";
1
+ import { create } from "zustand";
2
2
 
3
3
  type HookModalState = {
4
4
  path: string;
@@ -10,11 +10,12 @@ export type HookModalContextT = {
10
10
  setIsHooksExecutionInProgress: (hookExecutionState?: boolean) => void;
11
11
  isRunningInBackground: boolean;
12
12
  isPresentationFullScreen: boolean;
13
- setIsRunningInBackground: (hookBackgroundProcessState?: boolean) => void;
14
- setIsPresentingFullScreen: (hookBackgroundProcessState?: boolean) => void;
13
+ setIsRunningInBackground: () => void;
14
+ setIsPresentingFullScreen: () => void;
15
15
  state: HookModalState;
16
16
  setState: (state: HookModalState) => void;
17
17
  resetState: () => void;
18
+ hookPresentationMode: HookPresentationMode;
18
19
  };
19
20
 
20
21
  const initialState = {
@@ -24,68 +25,43 @@ const initialState = {
24
25
 
25
26
  type HookPresentationMode = "background" | "fullScreen";
26
27
 
27
- const ReactContext = React.createContext<HookModalContextT>({
28
- isHooksExecutionInProgress: false,
29
- setIsHooksExecutionInProgress: () => {},
30
- isRunningInBackground: null,
31
- setIsRunningInBackground: () => {},
32
- setIsPresentingFullScreen: () => {},
33
- isPresentationFullScreen: null,
28
+ // Use useZappHookModalStore() in React components (hooks)
29
+ // Use zappHookModalStore.getState() in non-React functions (utility functions, etc.)
30
+ export const useZappHookModalStore = create<HookModalContextT>()((set) => ({
34
31
  state: initialState,
35
- setState: () => null,
36
- resetState: () => null,
37
- });
38
-
39
- const Provider = ({ children }: { children: React.ReactNode }) => {
40
- const [state, setState] = React.useState<HookModalState>(initialState);
41
-
42
- const [hookPresentationMode, setHookPresentationMode] =
43
- React.useState<HookPresentationMode>(null);
44
-
45
- const resetState = useCallback(() => {
46
- setState(initialState);
47
- }, [setState]);
48
-
49
- const [isHooksExecutionInProgress, setIsHooksExecutionInProgress] =
50
- React.useState(false);
32
+ isHooksExecutionInProgress: false,
33
+ hookPresentationMode: null as HookPresentationMode,
34
+ isRunningInBackground: false,
35
+ isPresentationFullScreen: false,
51
36
 
52
- const setIsRunningInBackground = () => {
53
- setHookPresentationMode("background");
54
- };
37
+ setState: (newState: HookModalState) => {
38
+ set({ state: newState });
39
+ },
55
40
 
56
- const setIsPresentingFullScreen = () => {
57
- setHookPresentationMode("fullScreen");
58
- };
41
+ setIsHooksExecutionInProgress: (hookExecutionState?: boolean) => {
42
+ set({ isHooksExecutionInProgress: hookExecutionState ?? false });
43
+ },
59
44
 
60
- return (
61
- <ReactContext.Provider
62
- value={{
63
- isRunningInBackground: hookPresentationMode === "background",
64
- setIsRunningInBackground,
65
- setIsPresentingFullScreen,
66
- isPresentationFullScreen: hookPresentationMode === "fullScreen",
67
- isHooksExecutionInProgress,
68
- setIsHooksExecutionInProgress,
69
- state,
70
- setState,
71
- resetState,
72
- }}
73
- >
74
- {children}
75
- </ReactContext.Provider>
76
- );
77
- };
45
+ setIsRunningInBackground: () => {
46
+ set({
47
+ hookPresentationMode: "background",
48
+ isRunningInBackground: true,
49
+ isPresentationFullScreen: false,
50
+ });
51
+ },
78
52
 
79
- const withProvider = (Component) => {
80
- const WithProvider = (props) => {
81
- return (
82
- <Provider>
83
- <Component {...props} />
84
- </Provider>
85
- );
86
- };
53
+ setIsPresentingFullScreen: () => {
54
+ set({
55
+ hookPresentationMode: "fullScreen",
56
+ isRunningInBackground: false,
57
+ isPresentationFullScreen: true,
58
+ });
59
+ },
87
60
 
88
- return WithProvider;
89
- };
61
+ resetState: () => {
62
+ set({ state: initialState });
63
+ },
64
+ }));
90
65
 
91
- export const ZappHookModalContext = { withProvider, ReactContext };
66
+ // Export an alias for clearer non-React usage (utility functions, etc.)
67
+ export const zappHookModalStore = useZappHookModalStore;
@@ -5,10 +5,10 @@ import React, {
5
5
  useMemo,
6
6
  useState,
7
7
  } from "react";
8
- import * as R from "ramda";
9
8
 
10
- type ProviderProps = {
9
+ type ProviderProps<S> = {
11
10
  children: React.ReactChild;
11
+ initialContextValue?: S;
12
12
  };
13
13
 
14
14
  type ContextType<T> = {
@@ -27,21 +27,29 @@ export function createZappPipesContext<T, S = T>(
27
27
  ) {
28
28
  const Context = createContext<ContextType<S>>(initialContext);
29
29
 
30
- const { selector = R.identity, prepareContext = R.identity } = options || {};
30
+ const defaultSelector = (c: any) => c;
31
+ const defaultPrepareContext = (n: any) => n;
32
+ const joinArgs = (args: any[]) => args.join("-");
33
+
34
+ const { selector = defaultSelector, prepareContext = defaultPrepareContext } =
35
+ options || {};
31
36
 
32
37
  function useZappPipesContext(...hookArgs: any[]): [T, (T) => void] {
33
38
  const { context, setContext } = useContext(Context);
39
+ const joinedArgs = joinArgs(hookArgs);
34
40
 
35
41
  const contextValue = useMemo(
36
42
  () => selector(context, ...hookArgs),
37
- [context, R.join("-", hookArgs)]
43
+ // eslint-disable-next-line @wogns3623/better-exhaustive-deps/exhaustive-deps
44
+ [context, joinedArgs]
38
45
  );
39
46
 
40
47
  const contextSetter = useCallback(
41
48
  (newContext: T) => {
42
49
  setContext(prepareContext(newContext, context, ...hookArgs));
43
50
  },
44
- [context, setContext, R.join("-", hookArgs)]
51
+ // eslint-disable-next-line @wogns3623/better-exhaustive-deps/exhaustive-deps
52
+ [context, joinedArgs]
45
53
  );
46
54
 
47
55
  return useMemo(
@@ -50,8 +58,11 @@ export function createZappPipesContext<T, S = T>(
50
58
  );
51
59
  }
52
60
 
53
- function Provider({ children }: ProviderProps) {
54
- const [context, setContext] = useState<S>(initialContext.context);
61
+ /** Provider accepts `initialContextValue` prop to set the initial context value */
62
+ function Provider({ children, initialContextValue }: ProviderProps<S>) {
63
+ const [context, setContext] = useState<S>(
64
+ initialContextValue ?? initialContext.context
65
+ );
55
66
 
56
67
  return (
57
68
  <Context.Provider value={{ context, setContext }}>
package/Contexts/index.ts CHANGED
@@ -12,8 +12,6 @@ export { HorizontalScrollContext } from "./HorizontalScrollContext";
12
12
 
13
13
  export { RiverOffsetContext } from "./RiverOffsetContext";
14
14
 
15
- export { ZappHookModalContext } from "./ZappHookModalContext";
16
-
17
15
  export { MeasurementContext } from "./MeasurementContext";
18
16
 
19
17
  export * from "./ZappPipesContext";
@@ -1,7 +1,6 @@
1
1
  import React from "react";
2
2
 
3
3
  import { getAnalyticsFunctions } from "@applicaster/zapp-react-native-utils/analyticsUtils";
4
- import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
5
4
 
6
5
  type ComponentProps = {
7
6
  component: {
@@ -21,11 +20,13 @@ type ComponentProps = {
21
20
 
22
21
  function withAnalytics(Component) {
23
22
  return function WithAnalytics(props: ComponentProps) {
24
- const { appData } = usePickFromState(["appData"]);
25
-
26
23
  const analyticsFunctions = React.useMemo(
27
- () => getAnalyticsFunctions({ props, appData }),
28
- [props, appData]
24
+ () =>
25
+ getAnalyticsFunctions({
26
+ component: props.component,
27
+ zappPipesData: props.zappPipesData,
28
+ } as any),
29
+ [props.component, props.zappPipesData]
29
30
  );
30
31
 
31
32
  return (
@@ -88,6 +88,7 @@ exports[`withConfigurationProvider correctly passes all the configuration keys c
88
88
  tab_cell_padding_right={10}
89
89
  tab_cell_padding_top={14}
90
90
  tablet_theme={false}
91
+ tabs_screen_background_color="transparent"
91
92
  target={false}
92
93
  target_screen_switch={false}
93
94
  text_label_active_font_color="rgba(239, 239, 239, 0.5)"
@@ -208,4 +208,5 @@ export const keysMap: Record<string, Function> = {
208
208
  tab_bar_item_margin_right: castOrDefaultValue(Number, 0),
209
209
  tab_bar_item_margin_bottom: castOrDefaultValue(Number, 0),
210
210
  tab_bar_item_margin_left: castOrDefaultValue(Number, 0),
211
+ tabs_screen_background_color: castOrDefaultValue(R.identity, "transparent"),
211
212
  };
@@ -1,4 +1,3 @@
1
- // ResolverSelector.tsx
2
1
  import React from "react";
3
2
  import { ComponentDataSourceContext, ZappPipesDataProps } from "./types";
4
3
  import { StaticFeedResolver } from "./resolvers/StaticFeedResolver";
@@ -12,14 +11,33 @@ type ResolverSelectorProps = ComponentDataSourceContext & {
12
11
  export function ResolverSelector(props: ResolverSelectorProps) {
13
12
  const { getStaticComponentFeed, component, feedUrl, children } = props;
14
13
 
15
- // Determine which resolver to use
14
+ const renderDefaultResolver = (
15
+ fallbackChildren: (dataProps: ZappPipesDataProps) => React.ReactNode
16
+ ) => {
17
+ if (feedUrl || component?.data?.source) {
18
+ return <UrlFeedResolver {...props}>{fallbackChildren}</UrlFeedResolver>;
19
+ }
20
+
21
+ return <NullFeedResolver>{fallbackChildren}</NullFeedResolver>;
22
+ };
23
+
16
24
  if (getStaticComponentFeed) {
17
- return <StaticFeedResolver {...props}>{children}</StaticFeedResolver>;
18
- }
25
+ return (
26
+ <StaticFeedResolver {...props}>
27
+ {(zappPipesDataProps) => {
28
+ const { data, loading, error } = zappPipesDataProps.zappPipesData;
29
+
30
+ const shouldFallback = !loading && data === null && !error;
31
+
32
+ if (shouldFallback) {
33
+ return renderDefaultResolver(children);
34
+ }
19
35
 
20
- if (feedUrl || component?.data?.source) {
21
- return <UrlFeedResolver {...props}>{children}</UrlFeedResolver>;
36
+ return children(zappPipesDataProps);
37
+ }}
38
+ </StaticFeedResolver>
39
+ );
22
40
  }
23
41
 
24
- return <NullFeedResolver>{children}</NullFeedResolver>;
42
+ return renderDefaultResolver(children);
25
43
  }
@@ -7,15 +7,30 @@ import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
7
7
  import { NullFeedResolver } from "../resolvers/NullFeedResolver";
8
8
 
9
9
  jest.mock("../resolvers/StaticFeedResolver", () => ({
10
- StaticFeedResolver: jest.fn(() => null),
10
+ StaticFeedResolver: jest.fn(({ children }) =>
11
+ children({
12
+ zappPipesData: { loading: true, data: null, error: null },
13
+ reloadData: jest.fn(),
14
+ })
15
+ ),
11
16
  }));
12
17
 
13
18
  jest.mock("../resolvers/UrlFeedResolver", () => ({
14
- UrlFeedResolver: jest.fn(() => null),
19
+ UrlFeedResolver: jest.fn(({ children }) =>
20
+ children({
21
+ zappPipesData: { loading: true, data: null, error: null },
22
+ reloadData: jest.fn(),
23
+ })
24
+ ),
15
25
  }));
16
26
 
17
27
  jest.mock("../resolvers/NullFeedResolver", () => ({
18
- NullFeedResolver: jest.fn(() => null),
28
+ NullFeedResolver: jest.fn(({ children }) =>
29
+ children({
30
+ zappPipesData: { loading: false, data: null, error: null },
31
+ reloadData: jest.fn(),
32
+ })
33
+ ),
19
34
  }));
20
35
 
21
36
  const testPlugin = {
@@ -52,7 +67,7 @@ describe("ResolverSelector", () => {
52
67
  expect.objectContaining({
53
68
  getStaticComponentFeed: props.getStaticComponentFeed,
54
69
  component: mockComponent,
55
- children: mockChildren,
70
+ children: expect.any(Function),
56
71
  }),
57
72
  expect.anything()
58
73
  );
@@ -151,7 +166,10 @@ describe("ResolverSelector", () => {
151
166
  render(<ResolverSelector {...props} />);
152
167
 
153
168
  expect(StaticFeedResolver).toHaveBeenCalledWith(
154
- expect.objectContaining(props),
169
+ expect.objectContaining({
170
+ ...props,
171
+ children: expect.any(Function),
172
+ }),
155
173
  expect.anything()
156
174
  );
157
175
  });
@@ -202,4 +220,193 @@ describe("ResolverSelector", () => {
202
220
  expect(UrlFeedResolver).not.toHaveBeenCalled();
203
221
  expect(NullFeedResolver).not.toHaveBeenCalled();
204
222
  });
223
+
224
+ describe("Fallback Logic", () => {
225
+ it("should fallback to UrlFeedResolver when StaticFeedResolver returns null and feedUrl is present", () => {
226
+ const props = {
227
+ getStaticComponentFeed: jest.fn(),
228
+ feedUrl: "https://example.com/feed",
229
+ component: mockComponent,
230
+ children: mockChildren,
231
+ riverId: "test-river",
232
+ };
233
+
234
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
235
+ children({
236
+ zappPipesData: { loading: false, data: null, error: null },
237
+ reloadData: jest.fn(),
238
+ })
239
+ );
240
+
241
+ render(<ResolverSelector {...props} />);
242
+
243
+ expect(StaticFeedResolver).toHaveBeenCalled();
244
+
245
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
246
+ expect.objectContaining({
247
+ feedUrl: props.feedUrl,
248
+ component: mockComponent,
249
+ children: mockChildren,
250
+ }),
251
+ expect.anything()
252
+ );
253
+ });
254
+
255
+ it("should fallback to UrlFeedResolver when StaticFeedResolver returns null and component.data.source is present", () => {
256
+ const componentWithSource = {
257
+ ...mockComponent,
258
+ data: {
259
+ source: "data-source",
260
+ },
261
+ };
262
+
263
+ const props = {
264
+ getStaticComponentFeed: jest.fn(),
265
+ component: componentWithSource,
266
+ children: mockChildren,
267
+ riverId: "test-river",
268
+ };
269
+
270
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
271
+ children({
272
+ zappPipesData: { loading: false, data: null, error: null },
273
+ reloadData: jest.fn(),
274
+ })
275
+ );
276
+
277
+ render(<ResolverSelector {...props} />);
278
+
279
+ expect(StaticFeedResolver).toHaveBeenCalled();
280
+
281
+ expect(UrlFeedResolver).toHaveBeenCalledWith(
282
+ expect.objectContaining({
283
+ component: componentWithSource,
284
+ children: mockChildren,
285
+ }),
286
+ expect.anything()
287
+ );
288
+ });
289
+
290
+ it("should fallback to NullFeedResolver when StaticFeedResolver returns null and no feedUrl/source is present", () => {
291
+ const props = {
292
+ getStaticComponentFeed: jest.fn(),
293
+ component: mockComponent,
294
+ children: mockChildren,
295
+ riverId: "test-river",
296
+ };
297
+
298
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
299
+ children({
300
+ zappPipesData: { loading: false, data: null, error: null },
301
+ reloadData: jest.fn(),
302
+ })
303
+ );
304
+
305
+ render(<ResolverSelector {...props} />);
306
+
307
+ expect(StaticFeedResolver).toHaveBeenCalled();
308
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
309
+
310
+ expect(NullFeedResolver).toHaveBeenCalledWith(
311
+ expect.objectContaining({
312
+ children: mockChildren,
313
+ }),
314
+ expect.anything()
315
+ );
316
+ });
317
+
318
+ it("should NOT fallback and call children with static data when StaticFeedResolver returns data", () => {
319
+ const staticData = { entry: [{ id: "1" }] };
320
+
321
+ const props = {
322
+ getStaticComponentFeed: jest.fn(),
323
+ feedUrl: "https://example.com/feed",
324
+ component: mockComponent,
325
+ children: mockChildren,
326
+ riverId: "test-river",
327
+ };
328
+
329
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
330
+ children({
331
+ zappPipesData: { loading: false, data: staticData, error: null },
332
+ reloadData: jest.fn(),
333
+ })
334
+ );
335
+
336
+ render(<ResolverSelector {...props} />);
337
+
338
+ expect(StaticFeedResolver).toHaveBeenCalled();
339
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
340
+
341
+ expect(mockChildren).toHaveBeenCalledWith(
342
+ expect.objectContaining({
343
+ zappPipesData: expect.objectContaining({
344
+ data: staticData,
345
+ }),
346
+ })
347
+ );
348
+ });
349
+
350
+ it("should NOT fallback and call children with loading state when StaticFeedResolver is loading", () => {
351
+ const props = {
352
+ getStaticComponentFeed: jest.fn(),
353
+ feedUrl: "https://example.com/feed",
354
+ component: mockComponent,
355
+ children: mockChildren,
356
+ riverId: "test-river",
357
+ };
358
+
359
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
360
+ children({
361
+ zappPipesData: { loading: true, data: null, error: null },
362
+ reloadData: jest.fn(),
363
+ })
364
+ );
365
+
366
+ render(<ResolverSelector {...props} />);
367
+
368
+ expect(StaticFeedResolver).toHaveBeenCalled();
369
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
370
+
371
+ expect(mockChildren).toHaveBeenCalledWith(
372
+ expect.objectContaining({
373
+ zappPipesData: expect.objectContaining({
374
+ loading: true,
375
+ }),
376
+ })
377
+ );
378
+ });
379
+
380
+ it("should NOT fallback and call children with error when StaticFeedResolver returns error", () => {
381
+ const error = new Error("Static error");
382
+
383
+ const props = {
384
+ getStaticComponentFeed: jest.fn(),
385
+ feedUrl: "https://example.com/feed",
386
+ component: mockComponent,
387
+ children: mockChildren,
388
+ riverId: "test-river",
389
+ };
390
+
391
+ (StaticFeedResolver as jest.Mock).mockImplementation(({ children }) =>
392
+ children({
393
+ zappPipesData: { loading: false, data: null, error },
394
+ reloadData: jest.fn(),
395
+ })
396
+ );
397
+
398
+ render(<ResolverSelector {...props} />);
399
+
400
+ expect(StaticFeedResolver).toHaveBeenCalled();
401
+ expect(UrlFeedResolver).not.toHaveBeenCalled();
402
+
403
+ expect(mockChildren).toHaveBeenCalledWith(
404
+ expect.objectContaining({
405
+ zappPipesData: expect.objectContaining({
406
+ error,
407
+ }),
408
+ })
409
+ );
410
+ });
411
+ });
205
412
  });
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
2
  import * as useFeedLoaderModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
3
- import * as useFeedRefreshModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh";
4
3
  import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
5
4
  import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
6
5
  import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
@@ -14,10 +13,6 @@ jest
14
13
 
15
14
  jest.mock("@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader");
16
15
 
17
- jest.mock(
18
- "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh"
19
- );
20
-
21
16
  jest.mock("@applicaster/zapp-pipes-v2-client");
22
17
 
23
18
  jest.mock("@applicaster/zapp-react-native-bridge/Favorites", () => ({
@@ -62,6 +57,9 @@ describe("UrlFeedResolver", () => {
62
57
  component: { ...componentRequiredKeys } as any,
63
58
  children: mockChildren,
64
59
  riverId: "test-river",
60
+ screenContext: {
61
+ id: "screenId",
62
+ },
65
63
  };
66
64
 
67
65
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -82,6 +80,9 @@ describe("UrlFeedResolver", () => {
82
80
  } as any,
83
81
  children: mockChildren,
84
82
  riverId: "test-river",
83
+ screenContext: {
84
+ id: "screenId",
85
+ },
85
86
  };
86
87
 
87
88
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -103,6 +104,9 @@ describe("UrlFeedResolver", () => {
103
104
  } as any,
104
105
  children: mockChildren,
105
106
  riverId: "test-river",
107
+ screenContext: {
108
+ id: "screenId",
109
+ },
106
110
  };
107
111
 
108
112
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -132,6 +136,9 @@ describe("UrlFeedResolver", () => {
132
136
  } as any,
133
137
  children: mockChildren,
134
138
  riverId: "test-river",
139
+ screenContext: {
140
+ id: "screenId",
141
+ },
135
142
  };
136
143
 
137
144
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -155,6 +162,9 @@ describe("UrlFeedResolver", () => {
155
162
  children: mockChildren,
156
163
  riverId: "test-river",
157
164
  plugins: [],
165
+ screenContext: {
166
+ id: "screenId",
167
+ },
158
168
  };
159
169
 
160
170
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -173,6 +183,9 @@ describe("UrlFeedResolver", () => {
173
183
  component: { ...componentRequiredKeys } as any,
174
184
  children: mockChildren,
175
185
  riverId: "test-river",
186
+ screenContext: {
187
+ id: "screenId",
188
+ },
176
189
  };
177
190
 
178
191
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -197,6 +210,9 @@ describe("UrlFeedResolver", () => {
197
210
  },
198
211
  },
199
212
  ] as any,
213
+ screenContext: {
214
+ id: "screenId",
215
+ },
200
216
  };
201
217
 
202
218
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -215,6 +231,9 @@ describe("UrlFeedResolver", () => {
215
231
  } as any,
216
232
  children: mockChildren,
217
233
  riverId: "test-river",
234
+ screenContext: {
235
+ id: "screenId",
236
+ },
218
237
  };
219
238
 
220
239
  (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
@@ -250,6 +269,9 @@ describe("UrlFeedResolver", () => {
250
269
  children: mockChildren,
251
270
  riverId: "test-river",
252
271
  isLast: true,
272
+ screenContext: {
273
+ id: "screenId",
274
+ },
253
275
  };
254
276
 
255
277
  renderWithProviders(<UrlFeedResolver {...props1} />);
@@ -272,6 +294,9 @@ describe("UrlFeedResolver", () => {
272
294
  children: mockChildren,
273
295
  riverId: "test-river",
274
296
  isLast: false,
297
+ screenContext: {
298
+ id: "screenId",
299
+ },
275
300
  };
276
301
 
277
302
  renderWithProviders(<UrlFeedResolver {...props2} />);
@@ -294,6 +319,9 @@ describe("UrlFeedResolver", () => {
294
319
  children: mockChildren,
295
320
  riverId: "test-river",
296
321
  isLast: false,
322
+ screenContext: {
323
+ id: "screenId",
324
+ },
297
325
  };
298
326
 
299
327
  renderWithProviders(<UrlFeedResolver {...props3} />);
@@ -311,6 +339,9 @@ describe("UrlFeedResolver", () => {
311
339
  component: { ...componentRequiredKeys } as any,
312
340
  children: mockChildren,
313
341
  riverId: "test-river",
342
+ screenContext: {
343
+ id: "screenId",
344
+ },
314
345
  };
315
346
 
316
347
  renderWithProviders(<UrlFeedResolver {...props} />);
@@ -327,22 +358,6 @@ describe("UrlFeedResolver", () => {
327
358
  });
328
359
  });
329
360
 
330
- it("should apply feed refresh hook", () => {
331
- const props = {
332
- feedUrl: "https://example.com/feed",
333
- component: { ...componentRequiredKeys } as any,
334
- children: mockChildren,
335
- riverId: "test-river",
336
- };
337
-
338
- renderWithProviders(<UrlFeedResolver {...props} />);
339
-
340
- expect(useFeedRefreshModule.useFeedRefresh).toHaveBeenCalledWith({
341
- reloadData: mockReloadData,
342
- component: props.component,
343
- });
344
- });
345
-
346
361
  it("should clean up listeners on unmount", () => {
347
362
  const props = {
348
363
  component: {
@@ -354,6 +369,9 @@ describe("UrlFeedResolver", () => {
354
369
  } as any,
355
370
  children: mockChildren,
356
371
  riverId: "test-river",
372
+ screenContext: {
373
+ id: "screenId",
374
+ },
357
375
  };
358
376
 
359
377
  const { unmount } = renderWithProviders(<UrlFeedResolver {...props} />);