@applicaster/zapp-react-native-ui-components 14.0.0-alpha.5071825192 → 14.0.0-alpha.5114565431

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 (93) hide show
  1. package/Components/AnimatedInOut/index.tsx +5 -3
  2. package/Components/AudioPlayer/index.tsx +15 -0
  3. package/Components/AudioPlayer/mobile/Layout.tsx +66 -0
  4. package/Components/AudioPlayer/{__tests__/__snapshots__/audioPlayer.test.js.snap → mobile/__tests__/__snapshots__/audioPlayerMobileLayout.test.js.snap} +2 -2
  5. package/Components/AudioPlayer/mobile/__tests__/audioPlayerMobileLayout.test.js +18 -0
  6. package/Components/AudioPlayer/mobile/index.tsx +18 -0
  7. package/Components/AudioPlayer/{Artwork.tsx → tv/Artwork.tsx} +3 -2
  8. package/Components/AudioPlayer/{Channel.tsx → tv/Channel.tsx} +7 -7
  9. package/Components/AudioPlayer/tv/Layout.tsx +168 -0
  10. package/Components/AudioPlayer/{Runtime.tsx → tv/Runtime.tsx} +7 -1
  11. package/Components/AudioPlayer/{Summary.tsx → tv/Summary.tsx} +6 -2
  12. package/Components/AudioPlayer/{Title.tsx → tv/Title.tsx} +6 -2
  13. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/Runtime.test.js.snap +2 -2
  14. package/Components/AudioPlayer/tv/__tests__/__snapshots__/audioPlayer.test.js.snap +164 -0
  15. package/Components/AudioPlayer/tv/__tests__/__snapshots__/channel.test.js.snap +19 -0
  16. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/summary.test.js.snap +1 -2
  17. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/title.test.js.snap +1 -2
  18. package/Components/AudioPlayer/{__tests__ → tv/__tests__}/audioPlayer.test.js +7 -3
  19. package/Components/AudioPlayer/{helpers.tsx → tv/helpers.tsx} +11 -5
  20. package/Components/AudioPlayer/{AudioPlayer.tsx → tv/index.tsx} +17 -58
  21. package/Components/AudioPlayer/types.ts +40 -0
  22. package/Components/Cell/index.js +6 -2
  23. package/Components/Focusable/Focusable.tsx +5 -3
  24. package/Components/Focusable/FocusableTvOS.tsx +3 -3
  25. package/Components/Focusable/FocusableiOS.tsx +2 -2
  26. package/Components/Focusable/__tests__/index.android.test.tsx +3 -0
  27. package/Components/Focusable/index.android.tsx +12 -8
  28. package/Components/Focusable/index.tsx +1 -1
  29. package/Components/FocusableList/index.tsx +4 -0
  30. package/Components/HandlePlayable/HandlePlayable.tsx +25 -9
  31. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -27
  32. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +1 -1
  33. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +2 -2
  34. package/Components/MasterCell/elementMapper.tsx +1 -2
  35. package/Components/MasterCell/index.tsx +1 -1
  36. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +13 -18
  37. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +9 -0
  38. package/Components/PlayerContainer/PlayerContainer.tsx +42 -32
  39. package/Components/PlayerImageBackground/index.tsx +1 -1
  40. package/Components/River/ComponentsMap/ComponentsMap.tsx +0 -1
  41. package/Components/River/TV/River.tsx +2 -17
  42. package/Components/River/TV/index.tsx +3 -1
  43. package/Components/River/TV/withPipesV1DataLoader.tsx +43 -0
  44. package/Components/River/TV/withRiverDataLoader.tsx +17 -0
  45. package/Components/River/TV/withTVEventHandler.tsx +1 -1
  46. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  47. package/Components/ScreenRevealManager/ScreenRevealManager.ts +76 -0
  48. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +107 -0
  49. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +96 -0
  50. package/Components/ScreenRevealManager/index.ts +1 -0
  51. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +79 -0
  52. package/Components/Tabs/TV/Tabs.android.tsx +0 -2
  53. package/Components/Touchable/__tests__/__snapshots__/touchable.test.tsx.snap +34 -0
  54. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +15 -9
  55. package/Components/VideoLive/animationUtils.ts +3 -3
  56. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -8
  57. package/Components/VideoModal/PlayerDetails.tsx +24 -2
  58. package/Components/VideoModal/PlayerWrapper.tsx +26 -142
  59. package/Components/VideoModal/VideoModal.tsx +3 -17
  60. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -7
  61. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +44 -240
  62. package/Components/VideoModal/hooks/index.ts +0 -2
  63. package/Components/VideoModal/hooks/useModalSize.ts +18 -2
  64. package/Components/VideoModal/utils.ts +6 -0
  65. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +12 -16
  66. package/Components/Viewport/ViewportTracker/__tests__/viewportTracker.test.js +84 -24
  67. package/Components/Viewport/VisibilitySensor/VisibilitySensor.tsx +3 -3
  68. package/Components/default-cell-renderer/viewTrees/tv/DefaultCell/index.ts +3 -3
  69. package/Decorators/ConfigurationWrapper/withConfigurationProvider.tsx +2 -2
  70. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -0
  71. package/Decorators/ZappPipesDataConnector/__tests__/NullFeedResolver.test.tsx +78 -0
  72. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +205 -0
  73. package/Decorators/ZappPipesDataConnector/__tests__/StaticFeedResolver.test.tsx +251 -0
  74. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +368 -0
  75. package/Decorators/ZappPipesDataConnector/__tests__/utils.test.ts +39 -0
  76. package/Decorators/ZappPipesDataConnector/index.tsx +26 -293
  77. package/Decorators/ZappPipesDataConnector/resolvers/NullFeedResolver.tsx +25 -0
  78. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +87 -0
  79. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +241 -0
  80. package/Decorators/ZappPipesDataConnector/types.ts +29 -0
  81. package/package.json +5 -9
  82. package/Components/AudioPlayer/AudioPlayerLayout.tsx +0 -202
  83. package/Components/AudioPlayer/__tests__/__snapshots__/audioPlayerLayout.test.js.snap +0 -66
  84. package/Components/AudioPlayer/__tests__/__snapshots__/channel.test.js.snap +0 -28
  85. package/Components/AudioPlayer/__tests__/audioPlayerLayout.test.js +0 -26
  86. package/Components/AudioPlayer/index.ts +0 -1
  87. package/Components/VideoModal/hooks/useBackgroundColor.ts +0 -10
  88. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/Runtime.test.js +0 -0
  89. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/__snapshots__/artWork.test.js.snap +0 -0
  90. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/artWork.test.js +0 -0
  91. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/channel.test.js +0 -0
  92. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/summary.test.js +0 -0
  93. /package/Components/AudioPlayer/{__tests__ → tv/__tests__}/title.test.js +0 -0
@@ -0,0 +1,368 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+ import * as useFeedLoaderModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader";
4
+ import * as useFeedRefreshModule from "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh";
5
+ import * as zappPipesClientModule from "@applicaster/zapp-pipes-v2-client";
6
+ import { favoritesListener } from "@applicaster/zapp-react-native-bridge/Favorites";
7
+ import { UrlFeedResolver } from "../resolvers/UrlFeedResolver";
8
+
9
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedLoader");
10
+
11
+ jest.mock(
12
+ "@applicaster/zapp-react-native-utils/reactHooks/feed/useFeedRefresh"
13
+ );
14
+
15
+ jest.mock("@applicaster/zapp-pipes-v2-client");
16
+
17
+ jest.mock("@applicaster/zapp-react-native-bridge/Favorites", () => ({
18
+ favoritesListener: {
19
+ on: jest.fn(),
20
+ removeHandler: jest.fn(),
21
+ },
22
+ }));
23
+
24
+ jest.mock("../utils", () => ({
25
+ isVerticalListOrGrid: jest.fn(
26
+ (component) =>
27
+ component?.type === "vertical_list" || component?.type === "vertical_grid"
28
+ ),
29
+ }));
30
+
31
+ const componentRequiredKeys = { rules: { item_limit: 10 } };
32
+
33
+ describe("UrlFeedResolver", () => {
34
+ const mockChildren = jest.fn(() => <div data-testid="mock-children" />);
35
+ const mockReloadData = jest.fn();
36
+ const mockLoadNext = jest.fn();
37
+
38
+ const mockSubscribeForUrlContextKeyChanges =
39
+ zappPipesClientModule.subscribeForUrlContextKeyChanges as jest.Mock;
40
+
41
+ const mockAddDataSourceListener = jest.fn().mockReturnValue(jest.fn());
42
+
43
+ beforeEach(() => {
44
+ jest.clearAllMocks();
45
+
46
+ (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
47
+ reloadData: mockReloadData,
48
+ loadNext: mockLoadNext,
49
+ error: null,
50
+ loading: false,
51
+ url: "test-url",
52
+ data: { entry: [{ id: "1" }] },
53
+ });
54
+
55
+ mockSubscribeForUrlContextKeyChanges.mockReturnValue(jest.fn());
56
+ });
57
+
58
+ it("should use feedUrl directly when provided", () => {
59
+ const props = {
60
+ feedUrl: "https://example.com/feed",
61
+ component: { ...componentRequiredKeys } as any,
62
+ children: mockChildren,
63
+ riverId: "test-river",
64
+ };
65
+
66
+ render(<UrlFeedResolver {...props} />);
67
+
68
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
69
+ feedUrl: "https://example.com/feed",
70
+ pipesOptions: expect.any(Object),
71
+ });
72
+ });
73
+
74
+ it("should use component.data.source when no feedUrl is provided", () => {
75
+ const props = {
76
+ component: {
77
+ data: {
78
+ source: "https://example.com/source",
79
+ },
80
+ ...componentRequiredKeys,
81
+ } as any,
82
+ children: mockChildren,
83
+ riverId: "test-river",
84
+ };
85
+
86
+ render(<UrlFeedResolver {...props} />);
87
+
88
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
89
+ feedUrl: "https://example.com/source",
90
+ pipesOptions: expect.any(Object),
91
+ });
92
+ });
93
+
94
+ it("should handle FAVOURITES type correctly", () => {
95
+ const props = {
96
+ component: {
97
+ data: {
98
+ source: "favorites-source",
99
+ type: "FAVOURITES",
100
+ },
101
+ ...componentRequiredKeys,
102
+ } as any,
103
+ children: mockChildren,
104
+ riverId: "test-river",
105
+ };
106
+
107
+ render(<UrlFeedResolver {...props} />);
108
+
109
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
110
+ feedUrl: "favorites-source",
111
+ pipesOptions: expect.objectContaining({
112
+ clearCache: true,
113
+ loadLocalFavorites: true,
114
+ }),
115
+ });
116
+
117
+ expect(favoritesListener.on).toHaveBeenCalledWith(
118
+ "FAVORITES_CHANGED",
119
+ mockReloadData
120
+ );
121
+ });
122
+
123
+ it("should handle APPLICASTER_COLLECTION type correctly", () => {
124
+ const props = {
125
+ component: {
126
+ data: {
127
+ source: "collection-id",
128
+ type: "APPLICASTER_COLLECTION",
129
+ },
130
+ ...componentRequiredKeys,
131
+ } as any,
132
+ children: mockChildren,
133
+ riverId: "test-river",
134
+ };
135
+
136
+ render(<UrlFeedResolver {...props} />);
137
+
138
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
139
+ feedUrl:
140
+ "applicaster://fetchData?type=APPLICASTER_COLLECTION&collectionId=collection-id",
141
+ pipesOptions: expect.any(Object),
142
+ });
143
+ });
144
+
145
+ it("should handle APPLICASTER_CATEGORY type correctly", () => {
146
+ const props = {
147
+ component: {
148
+ data: {
149
+ source: "category-id",
150
+ type: "APPLICASTER_CATEGORY",
151
+ },
152
+ ...componentRequiredKeys,
153
+ } as any,
154
+ children: mockChildren,
155
+ riverId: "test-river",
156
+ plugins: [],
157
+ };
158
+
159
+ render(<UrlFeedResolver {...props} />);
160
+
161
+ expect(useFeedLoaderModule.useFeedLoader).toHaveBeenCalledWith({
162
+ feedUrl: expect.stringContaining(
163
+ "applicaster://fetchData?type=APPLICASTER_CATEGORY&categoryId=category-id"
164
+ ),
165
+ pipesOptions: expect.any(Object),
166
+ });
167
+ });
168
+
169
+ it("should set up URL context key changes listener", () => {
170
+ const props = {
171
+ feedUrl: "https://example.com/feed",
172
+ component: { ...componentRequiredKeys } as any,
173
+ children: mockChildren,
174
+ riverId: "test-river",
175
+ };
176
+
177
+ render(<UrlFeedResolver {...props} />);
178
+
179
+ expect(mockSubscribeForUrlContextKeyChanges).toHaveBeenCalledWith(
180
+ "https://example.com/feed",
181
+ {},
182
+ mockReloadData
183
+ );
184
+ });
185
+
186
+ it("should set up pipesv2 URL listener from plugin if available", () => {
187
+ const props = {
188
+ feedUrl: "pipesv2://test-plugin/endpoint",
189
+ component: { ...componentRequiredKeys } as any,
190
+ children: mockChildren,
191
+ riverId: "test-river",
192
+ plugins: [
193
+ {
194
+ identifier: "test-plugin",
195
+ module: {
196
+ addDataSourceListener: mockAddDataSourceListener,
197
+ },
198
+ },
199
+ ] as any,
200
+ };
201
+
202
+ render(<UrlFeedResolver {...props} />);
203
+
204
+ expect(mockAddDataSourceListener).toHaveBeenCalledWith(mockReloadData);
205
+ expect(mockSubscribeForUrlContextKeyChanges).not.toHaveBeenCalled();
206
+ });
207
+
208
+ it("should apply item limit from component rules", () => {
209
+ const props = {
210
+ feedUrl: "https://example.com/feed",
211
+ component: {
212
+ rules: {
213
+ item_limit: "2",
214
+ },
215
+ } as any,
216
+ children: mockChildren,
217
+ riverId: "test-river",
218
+ };
219
+
220
+ (useFeedLoaderModule.useFeedLoader as jest.Mock).mockReturnValue({
221
+ reloadData: mockReloadData,
222
+ loadNext: mockLoadNext,
223
+ error: null,
224
+ loading: false,
225
+ url: "test-url",
226
+ data: { entry: [{ id: "1" }, { id: "2" }, { id: "3" }] },
227
+ });
228
+
229
+ render(<UrlFeedResolver {...props} />);
230
+
231
+ expect(mockChildren).toHaveBeenCalledWith(
232
+ expect.objectContaining({
233
+ zappPipesData: expect.objectContaining({
234
+ data: expect.objectContaining({
235
+ entry: [{ id: "1" }, { id: "2" }],
236
+ }),
237
+ }),
238
+ })
239
+ );
240
+ });
241
+
242
+ it("should conditionally include loadNextData based on isLast and component type", () => {
243
+ // Case 1: isLast=true, should not include loadNextData regardless of component type
244
+ const props1 = {
245
+ feedUrl: "https://example.com/feed",
246
+ component: {
247
+ type: "vertical_list",
248
+ ...componentRequiredKeys,
249
+ } as any,
250
+ children: mockChildren,
251
+ riverId: "test-river",
252
+ isLast: true,
253
+ };
254
+
255
+ render(<UrlFeedResolver {...props1} />);
256
+
257
+ expect(mockChildren).toHaveBeenCalledWith(
258
+ expect.objectContaining({
259
+ loadNextData: mockLoadNext,
260
+ })
261
+ );
262
+
263
+ mockChildren.mockClear();
264
+
265
+ // Case 2: isLast=false, vertical_list component, should not include loadNextData
266
+ const props2 = {
267
+ feedUrl: "https://example.com/feed",
268
+ component: {
269
+ type: "vertical_list",
270
+ ...componentRequiredKeys,
271
+ } as any,
272
+ children: mockChildren,
273
+ riverId: "test-river",
274
+ isLast: false,
275
+ };
276
+
277
+ render(<UrlFeedResolver {...props2} />);
278
+
279
+ expect(mockChildren).toHaveBeenCalledWith(
280
+ expect.objectContaining({
281
+ loadNextData: undefined,
282
+ })
283
+ );
284
+
285
+ mockChildren.mockClear();
286
+
287
+ // Case 3: isLast=false, non-vertical component, should include loadNextData
288
+ const props3 = {
289
+ feedUrl: "https://example.com/feed",
290
+ component: {
291
+ type: "horizontal_list",
292
+ ...componentRequiredKeys,
293
+ } as any,
294
+ children: mockChildren,
295
+ riverId: "test-river",
296
+ isLast: false,
297
+ };
298
+
299
+ render(<UrlFeedResolver {...props3} />);
300
+
301
+ expect(mockChildren).toHaveBeenCalledWith(
302
+ expect.objectContaining({
303
+ loadNextData: mockLoadNext,
304
+ })
305
+ );
306
+ });
307
+
308
+ it("should pass correct zappPipesDataProps to children", () => {
309
+ const props = {
310
+ feedUrl: "https://example.com/feed",
311
+ component: { ...componentRequiredKeys } as any,
312
+ children: mockChildren,
313
+ riverId: "test-river",
314
+ };
315
+
316
+ render(<UrlFeedResolver {...props} />);
317
+
318
+ expect(mockChildren).toHaveBeenCalledWith({
319
+ zappPipesData: {
320
+ url: "test-url",
321
+ loading: false,
322
+ data: { entry: [{ id: "1" }] },
323
+ error: null,
324
+ },
325
+ reloadData: mockReloadData,
326
+ loadNextData: mockLoadNext,
327
+ });
328
+ });
329
+
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
+ render(<UrlFeedResolver {...props} />);
339
+
340
+ expect(useFeedRefreshModule.useFeedRefresh).toHaveBeenCalledWith({
341
+ reloadData: mockReloadData,
342
+ component: props.component,
343
+ });
344
+ });
345
+
346
+ it("should clean up listeners on unmount", () => {
347
+ const props = {
348
+ component: {
349
+ data: {
350
+ source: "favorites-source",
351
+ type: "FAVOURITES",
352
+ },
353
+ ...componentRequiredKeys,
354
+ } as any,
355
+ children: mockChildren,
356
+ riverId: "test-river",
357
+ };
358
+
359
+ const { unmount } = render(<UrlFeedResolver {...props} />);
360
+
361
+ unmount();
362
+
363
+ expect(favoritesListener.removeHandler).toHaveBeenCalledWith(
364
+ "FAVORITES_CHANGED",
365
+ mockReloadData
366
+ );
367
+ });
368
+ });
@@ -0,0 +1,39 @@
1
+ import { isVerticalListOrGrid } from "../utils";
2
+
3
+ describe("ZappPipesDataConnector utils", () => {
4
+ describe("isVerticalListOrGrid", () => {
5
+ it("should return true for list-qb components", () => {
6
+ const component = {
7
+ component_type: "list-qb",
8
+ } as any;
9
+
10
+ expect(isVerticalListOrGrid(component)).toBe(true);
11
+ });
12
+
13
+ it("should return true for grid-qb components", () => {
14
+ const component = {
15
+ component_type: "grid-qb",
16
+ } as any;
17
+
18
+ expect(isVerticalListOrGrid(component)).toBe(true);
19
+ });
20
+
21
+ it("should return false for other component types", () => {
22
+ const component = {
23
+ component_type: "horizontal-list",
24
+ } as any;
25
+
26
+ expect(isVerticalListOrGrid(component)).toBe(false);
27
+ });
28
+
29
+ it("should return false for undefined component", () => {
30
+ expect(isVerticalListOrGrid(undefined)).toBe(false);
31
+ });
32
+
33
+ it("should return false for component without type", () => {
34
+ const component = {} as any;
35
+
36
+ expect(isVerticalListOrGrid(component)).toBe(false);
37
+ });
38
+ });
39
+ });