@applicaster/zapp-react-native-ui-components 14.0.0-alpha.5114565431 → 14.0.0-alpha.5203410334
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.
- package/Components/BaseFocusable/index.tsx +23 -12
- package/Components/Cell/__tests__/CellWIthFocusable.test.js +3 -2
- package/Components/Cell/index.js +1 -1
- package/Components/ComponentResolver/index.ts +1 -1
- package/Components/FeedLoader/FeedLoader.tsx +6 -15
- package/Components/FeedLoader/FeedLoaderHOC.tsx +21 -0
- package/Components/FeedLoader/index.js +2 -8
- package/Components/FreezeWithCallback/__tests__/index.test.tsx +67 -43
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +42 -59
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +13 -10
- package/Components/Layout/TV/LayoutBackground.tsx +1 -1
- package/Components/Layout/TV/__tests__/index.test.tsx +0 -1
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
- package/Components/MasterCell/DefaultComponents/Button.tsx +1 -1
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +1 -1
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +10 -10
- package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +18 -18
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/index.test.tsx +10 -10
- package/Components/MasterCell/utils/behaviorProvider.ts +82 -14
- package/Components/MasterCell/utils/index.ts +11 -5
- package/Components/OfflineHandler/__tests__/index.test.tsx +26 -35
- package/Components/PlayerContainer/ErrorDisplay/index.ts +1 -1
- package/Components/PlayerContainer/ProgramInfo/index.tsx +1 -1
- package/Components/PlayerContainer/index.ts +1 -1
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +378 -0
- package/Components/River/ComponentsMap/hooks/useLoadingState.ts +2 -2
- package/Components/River/RefreshControl.tsx +11 -17
- package/Components/River/__tests__/river.test.js +12 -26
- package/Components/River/index.tsx +1 -1
- package/Components/Screen/__tests__/Screen.test.tsx +28 -29
- package/Components/Tabs/Tabs.tsx +2 -3
- package/Components/Touchable/__tests__/touchable.test.tsx +12 -17
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +3 -9
- package/Components/VideoModal/__tests__/PlayerDetails.test.tsx +5 -5
- package/Contexts/ConfigutaionContext/__tests__/ConfigurationProvider.test.tsx +3 -3
- package/Contexts/ScreenContext/index.tsx +46 -6
- package/Decorators/ConfigurationWrapper/__tests__/withConfigurationProvider.test.tsx +3 -3
- package/Decorators/RiverFeedLoader/__tests__/__snapshots__/riverFeedLoader.test.tsx.snap +221 -209
- package/Decorators/RiverFeedLoader/__tests__/riverFeedLoader.test.tsx +14 -16
- package/Decorators/RiverFeedLoader/__tests__/utils.test.ts +0 -20
- package/Decorators/RiverFeedLoader/index.tsx +22 -4
- package/Decorators/RiverFeedLoader/utils/index.ts +0 -18
- package/Decorators/RiverResolver/__tests__/riverResolver.test.tsx +3 -6
- package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +27 -27
- package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +35 -10
- package/Decorators/ZappPipesDataConnector/utils/mongoFilter.ts +738 -0
- package/Decorators/ZappPipesDataConnector/utils/useFilter.tsx +157 -0
- package/events/index.ts +1 -0
- package/package.json +5 -6
- package/Components/River/__tests__/__snapshots__/river.test.js.snap +0 -27
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { renderHook, act } from "@testing-library/react-hooks";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
3
|
+
import { useLoadingState } from "../useLoadingState";
|
|
4
|
+
|
|
5
|
+
jest.mock(
|
|
6
|
+
"@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue",
|
|
7
|
+
() => ({
|
|
8
|
+
useRefWithInitialValue: jest.fn((initializer) => ({
|
|
9
|
+
current: initializer(),
|
|
10
|
+
})),
|
|
11
|
+
})
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
describe("useLoadingState", () => {
|
|
15
|
+
let onLoadDone: jest.Mock;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
onLoadDone = jest.fn();
|
|
19
|
+
jest.clearAllMocks();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe("initialization", () => {
|
|
23
|
+
it("should initialize with correct default state for zero components", () => {
|
|
24
|
+
const { result } = renderHook(() => useLoadingState(0, onLoadDone));
|
|
25
|
+
|
|
26
|
+
const initialState = result.current.loadingState.getValue();
|
|
27
|
+
|
|
28
|
+
expect(initialState).toEqual({
|
|
29
|
+
index: -1,
|
|
30
|
+
done: true,
|
|
31
|
+
waitForAllComponents: false,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
expect(result.current.shouldShowLoadingError).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("should initialize with correct default state for multiple components", () => {
|
|
38
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
39
|
+
|
|
40
|
+
const initialState = result.current.loadingState.getValue();
|
|
41
|
+
|
|
42
|
+
expect(initialState).toEqual({
|
|
43
|
+
index: -1,
|
|
44
|
+
done: false,
|
|
45
|
+
waitForAllComponents: false,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(result.current.shouldShowLoadingError).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should return a BehaviorSubject for loadingState", () => {
|
|
52
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
53
|
+
|
|
54
|
+
expect(result.current.loadingState).toBeInstanceOf(BehaviorSubject);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("arePreviousComponentsLoaded", () => {
|
|
59
|
+
it("should return true for index 0 (first component)", () => {
|
|
60
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
61
|
+
|
|
62
|
+
expect(result.current.arePreviousComponentsLoaded(0)).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should return false when previous components are not loaded", () => {
|
|
66
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
67
|
+
|
|
68
|
+
expect(result.current.arePreviousComponentsLoaded(1)).toBe(false);
|
|
69
|
+
expect(result.current.arePreviousComponentsLoaded(2)).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("should return true when all previous components are loaded", () => {
|
|
73
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
74
|
+
|
|
75
|
+
act(() => {
|
|
76
|
+
result.current.onLoadFinished(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(result.current.arePreviousComponentsLoaded(1)).toBe(true);
|
|
80
|
+
expect(result.current.arePreviousComponentsLoaded(2)).toBe(false);
|
|
81
|
+
|
|
82
|
+
act(() => {
|
|
83
|
+
result.current.onLoadFinished(1);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(result.current.arePreviousComponentsLoaded(2)).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe("onLoadFinished", () => {
|
|
91
|
+
it("should update component state and loading state when component finishes loading", () => {
|
|
92
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
93
|
+
|
|
94
|
+
act(() => {
|
|
95
|
+
result.current.onLoadFinished(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const state = result.current.loadingState.getValue();
|
|
99
|
+
expect(state.index).toBe(0);
|
|
100
|
+
expect(state.done).toBe(false);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("should update index to highest loaded component", () => {
|
|
104
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
105
|
+
|
|
106
|
+
act(() => {
|
|
107
|
+
result.current.onLoadFinished(2);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
let state = result.current.loadingState.getValue();
|
|
111
|
+
expect(state.index).toBe(2);
|
|
112
|
+
|
|
113
|
+
act(() => {
|
|
114
|
+
result.current.onLoadFinished(1);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
state = result.current.loadingState.getValue();
|
|
118
|
+
expect(state.index).toBe(2); // Should remain 2, not decrease to 1
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("should mark as done when all components are loaded", () => {
|
|
122
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
123
|
+
|
|
124
|
+
act(() => {
|
|
125
|
+
result.current.onLoadFinished(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
let state = result.current.loadingState.getValue();
|
|
129
|
+
expect(state.done).toBe(true); // True because arePreviousComponentsLoaded(1) returns true when component 0 is loaded
|
|
130
|
+
expect(onLoadDone).toHaveBeenCalledTimes(1);
|
|
131
|
+
|
|
132
|
+
act(() => {
|
|
133
|
+
result.current.onLoadFinished(1);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
state = result.current.loadingState.getValue();
|
|
137
|
+
expect(state.done).toBe(true);
|
|
138
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called again
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("should call onLoadDone when count is 0", () => {
|
|
142
|
+
const { result } = renderHook(() => useLoadingState(0, onLoadDone));
|
|
143
|
+
|
|
144
|
+
const state = result.current.loadingState.getValue();
|
|
145
|
+
expect(state.done).toBe(true);
|
|
146
|
+
// onLoadDone is not called on initialization for count 0, only when all components are loaded via dispatch
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should handle loading components out of order", () => {
|
|
150
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
151
|
+
|
|
152
|
+
// Load component 2 first
|
|
153
|
+
act(() => {
|
|
154
|
+
result.current.onLoadFinished(2);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
let state = result.current.loadingState.getValue();
|
|
158
|
+
expect(state.done).toBe(false);
|
|
159
|
+
|
|
160
|
+
// Load component 0
|
|
161
|
+
act(() => {
|
|
162
|
+
result.current.onLoadFinished(0);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
state = result.current.loadingState.getValue();
|
|
166
|
+
expect(state.done).toBe(false);
|
|
167
|
+
|
|
168
|
+
// Load component 1 - should complete loading
|
|
169
|
+
act(() => {
|
|
170
|
+
result.current.onLoadFinished(1);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
state = result.current.loadingState.getValue();
|
|
174
|
+
expect(state.done).toBe(true);
|
|
175
|
+
expect(onLoadDone).toHaveBeenCalledTimes(1);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should call onLoadDone again on subsequent dispatches", () => {
|
|
179
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
180
|
+
|
|
181
|
+
act(() => {
|
|
182
|
+
result.current.onLoadFinished(0);
|
|
183
|
+
result.current.onLoadFinished(1);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called for each dispatch when done is true
|
|
187
|
+
|
|
188
|
+
// Try loading again - onLoadDone will be called again because dispatch runs again
|
|
189
|
+
act(() => {
|
|
190
|
+
result.current.onLoadFinished(0);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(onLoadDone).toHaveBeenCalledTimes(3); // Will be called again
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe("onLoadFailed", () => {
|
|
198
|
+
it("should treat failed components as loaded when SHOULD_FAIL_ON_COMPONENT_LOADING is false", () => {
|
|
199
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
200
|
+
|
|
201
|
+
const error = new Error("Load failed");
|
|
202
|
+
|
|
203
|
+
act(() => {
|
|
204
|
+
result.current.onLoadFailed({ error, index: 0 });
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const state = result.current.loadingState.getValue();
|
|
208
|
+
expect(state.index).toBe(0);
|
|
209
|
+
expect(result.current.shouldShowLoadingError).toBe(false);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it("should complete loading when all components fail", () => {
|
|
213
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
214
|
+
|
|
215
|
+
const error = new Error("Load failed");
|
|
216
|
+
|
|
217
|
+
act(() => {
|
|
218
|
+
result.current.onLoadFailed({ error, index: 0 });
|
|
219
|
+
result.current.onLoadFailed({ error, index: 1 });
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const state = result.current.loadingState.getValue();
|
|
223
|
+
expect(state.done).toBe(true);
|
|
224
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called for each failed component
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it("should handle mixed success and failure", () => {
|
|
228
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone));
|
|
229
|
+
|
|
230
|
+
const error = new Error("Load failed");
|
|
231
|
+
|
|
232
|
+
act(() => {
|
|
233
|
+
result.current.onLoadFinished(0);
|
|
234
|
+
result.current.onLoadFailed({ error, index: 1 });
|
|
235
|
+
result.current.onLoadFinished(2);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const state = result.current.loadingState.getValue();
|
|
239
|
+
expect(state.done).toBe(true);
|
|
240
|
+
expect(onLoadDone).toHaveBeenCalledTimes(2); // Called when all components 0,1,2 are handled
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
describe("loading state observable", () => {
|
|
245
|
+
it("should emit state changes through BehaviorSubject", () => {
|
|
246
|
+
const { result } = renderHook(() => useLoadingState(3, onLoadDone)); // Use 3 components so loading component 0 doesn't complete everything
|
|
247
|
+
const mockSubscriber = jest.fn();
|
|
248
|
+
|
|
249
|
+
result.current.loadingState.subscribe(mockSubscriber);
|
|
250
|
+
|
|
251
|
+
act(() => {
|
|
252
|
+
result.current.onLoadFinished(0);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Should have been called twice: initial state + update
|
|
256
|
+
expect(mockSubscriber).toHaveBeenCalledTimes(2);
|
|
257
|
+
|
|
258
|
+
expect(mockSubscriber).toHaveBeenLastCalledWith({
|
|
259
|
+
index: 0,
|
|
260
|
+
done: false, // Will be false because we need components 1 and 2 as well
|
|
261
|
+
waitForAllComponents: false,
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("should preserve waitForAllComponents flag in state updates", () => {
|
|
266
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
267
|
+
|
|
268
|
+
act(() => {
|
|
269
|
+
result.current.onLoadFinished(0);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
const state = result.current.loadingState.getValue();
|
|
273
|
+
expect(state.waitForAllComponents).toBe(false);
|
|
274
|
+
});
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
describe("memoization", () => {
|
|
278
|
+
it("should return stable references for functions", () => {
|
|
279
|
+
const { result, rerender } = renderHook(() =>
|
|
280
|
+
useLoadingState(2, onLoadDone)
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
const firstRender = {
|
|
284
|
+
onLoadFinished: result.current.onLoadFinished,
|
|
285
|
+
onLoadFailed: result.current.onLoadFailed,
|
|
286
|
+
arePreviousComponentsLoaded: result.current.arePreviousComponentsLoaded,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
rerender();
|
|
290
|
+
|
|
291
|
+
expect(result.current.onLoadFinished).toBe(firstRender.onLoadFinished);
|
|
292
|
+
expect(result.current.onLoadFailed).toBe(firstRender.onLoadFailed);
|
|
293
|
+
|
|
294
|
+
expect(result.current.arePreviousComponentsLoaded).toBe(
|
|
295
|
+
firstRender.arePreviousComponentsLoaded
|
|
296
|
+
);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it("should return stable function references (current behavior)", () => {
|
|
300
|
+
const { result, rerender } = renderHook(
|
|
301
|
+
({ onLoadDone }) => useLoadingState(2, onLoadDone),
|
|
302
|
+
{ initialProps: { onLoadDone } }
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
const firstResult = result.current;
|
|
306
|
+
|
|
307
|
+
const newOnLoadDone = jest.fn();
|
|
308
|
+
rerender({ onLoadDone: newOnLoadDone });
|
|
309
|
+
|
|
310
|
+
// Functions should remain the same due to empty dependency arrays (this is the current behavior)
|
|
311
|
+
expect(result.current.onLoadFinished).toBe(firstResult.onLoadFinished);
|
|
312
|
+
expect(result.current.onLoadFailed).toBe(firstResult.onLoadFailed);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
describe("edge cases", () => {
|
|
317
|
+
it("should handle duplicate load finished calls gracefully", () => {
|
|
318
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
319
|
+
|
|
320
|
+
act(() => {
|
|
321
|
+
result.current.onLoadFinished(0);
|
|
322
|
+
result.current.onLoadFinished(0); // Duplicate call
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
const state = result.current.loadingState.getValue();
|
|
326
|
+
expect(state.index).toBe(0);
|
|
327
|
+
expect(state.done).toBe(true); // True because loading component 0 makes it done in a 2-component setup
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("should handle loading index greater than component count", () => {
|
|
331
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
332
|
+
|
|
333
|
+
act(() => {
|
|
334
|
+
result.current.onLoadFinished(5); // Index out of bounds
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const state = result.current.loadingState.getValue();
|
|
338
|
+
expect(state.index).toBe(5);
|
|
339
|
+
expect(state.done).toBe(false); // Should not be done as not all components loaded
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
it("should handle negative indices", () => {
|
|
343
|
+
const { result } = renderHook(() => useLoadingState(2, onLoadDone));
|
|
344
|
+
|
|
345
|
+
act(() => {
|
|
346
|
+
result.current.onLoadFinished(-1);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
const state = result.current.loadingState.getValue();
|
|
350
|
+
expect(state.index).toBe(-1); // Should remain -1
|
|
351
|
+
});
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
describe("component count changes", () => {
|
|
355
|
+
it("should handle changing component count", () => {
|
|
356
|
+
const { result, rerender } = renderHook(
|
|
357
|
+
({ count }) => useLoadingState(count, onLoadDone),
|
|
358
|
+
{ initialProps: { count: 2 } }
|
|
359
|
+
);
|
|
360
|
+
|
|
361
|
+
act(() => {
|
|
362
|
+
result.current.onLoadFinished(0);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Change count
|
|
366
|
+
rerender({ count: 3 });
|
|
367
|
+
|
|
368
|
+
// The hook should work with the new count
|
|
369
|
+
act(() => {
|
|
370
|
+
result.current.onLoadFinished(1);
|
|
371
|
+
result.current.onLoadFinished(2);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const state = result.current.loadingState.getValue();
|
|
375
|
+
expect(state.done).toBe(true);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { isNil, set, lensIndex,
|
|
2
|
+
import { isNil, set, lensIndex, slice } from "ramda";
|
|
3
3
|
import { BehaviorSubject } from "rxjs";
|
|
4
4
|
import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue";
|
|
5
5
|
|
|
@@ -53,7 +53,7 @@ export const useLoadingState = (
|
|
|
53
53
|
|
|
54
54
|
const componentsBefore = slice(0, index, componentStateRef.current);
|
|
55
55
|
|
|
56
|
-
return componentsBefore.every(
|
|
56
|
+
return componentsBefore.every(Boolean);
|
|
57
57
|
}, []);
|
|
58
58
|
|
|
59
59
|
const dispatch = React.useCallback(({ payload }) => {
|
|
@@ -10,11 +10,10 @@ import { useLocalizedStrings } from "@applicaster/zapp-react-native-utils/locali
|
|
|
10
10
|
import { useAnalytics } from "@applicaster/zapp-react-native-utils/analyticsUtils";
|
|
11
11
|
import { useSendAnalyticsEventWithFunction } from "@applicaster/zapp-react-native-utils/analyticsUtils/helpers/hooks";
|
|
12
12
|
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useCurrentScreenData";
|
|
13
|
-
import { loadPipesData } from "@applicaster/zapp-react-native-redux/ZappPipes";
|
|
14
|
-
import { useDispatch } from "react-redux";
|
|
15
13
|
import { useShallow } from "zustand/react/shallow";
|
|
16
14
|
import { useScreenContextV2 } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
17
15
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
16
|
+
import { useLoadPipesDataDispatch } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
18
17
|
|
|
19
18
|
const BRIGHTNESS_THRESHOLD = 160;
|
|
20
19
|
const ABOVE_DEFAULT_COLOR = "gray";
|
|
@@ -61,38 +60,33 @@ export const usePullToRefresh = (
|
|
|
61
60
|
) => {
|
|
62
61
|
const isPipesV1 = !!pullToRefreshPipesV1RefreshingStateUpdater;
|
|
63
62
|
|
|
64
|
-
const dispatch = useDispatch();
|
|
65
|
-
|
|
66
63
|
const [refreshing, setRefreshing] = React.useState(false);
|
|
67
64
|
|
|
68
65
|
const feeds: string[] =
|
|
69
66
|
riverComponents?.map(R.path(["data", "source"])).filter((feed) => !!feed) ??
|
|
70
67
|
[];
|
|
71
68
|
|
|
72
|
-
const screenData = useCurrentScreenData();
|
|
73
|
-
|
|
74
69
|
const feedsLength = feeds.length;
|
|
75
70
|
|
|
76
71
|
const [requestsCompletedCounter, setRequestsCompletedCounter] =
|
|
77
72
|
React.useState(0);
|
|
78
73
|
|
|
74
|
+
const loadPipesDataDispatcher = useLoadPipesDataDispatch();
|
|
75
|
+
|
|
79
76
|
React.useEffect(() => {
|
|
80
77
|
// will not work for pipes v1 on 1st level screens
|
|
81
78
|
if (refreshing && !isPipesV1) {
|
|
82
79
|
feeds.forEach((feed) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
riverId: screenData.id,
|
|
91
|
-
})
|
|
92
|
-
);
|
|
80
|
+
loadPipesDataDispatcher(feed, {
|
|
81
|
+
silentRefresh: true,
|
|
82
|
+
clearCache: true,
|
|
83
|
+
callback: () => {
|
|
84
|
+
setRequestsCompletedCounter(R.inc);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
93
87
|
});
|
|
94
88
|
}
|
|
95
|
-
}, [refreshing, isPipesV1]);
|
|
89
|
+
}, [refreshing, isPipesV1, feeds, loadPipesDataDispatcher]);
|
|
96
90
|
|
|
97
91
|
React.useEffect(() => {
|
|
98
92
|
if (requestsCompletedCounter === feedsLength) {
|
|
@@ -1,20 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import {
|
|
3
|
-
import configureStore from "redux-mock-store";
|
|
2
|
+
import { screen } from "@testing-library/react-native";
|
|
4
3
|
import { renderWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
|
|
5
|
-
import { Provider } from "react-redux";
|
|
6
4
|
|
|
7
|
-
import { River } from "../";
|
|
8
5
|
import { ScreenResolver } from "../../ScreenResolver";
|
|
9
6
|
import { RiverComponent } from "../River";
|
|
10
7
|
|
|
11
|
-
jest.mock(
|
|
12
|
-
"@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive",
|
|
13
|
-
() => ({
|
|
14
|
-
useIsScreenActive: jest.fn(() => true),
|
|
15
|
-
})
|
|
16
|
-
);
|
|
17
|
-
|
|
18
8
|
jest.mock(
|
|
19
9
|
"@applicaster/zapp-react-native-ui-components/Components/GeneralContentScreen",
|
|
20
10
|
() => {
|
|
@@ -46,6 +36,13 @@ jest.mock(
|
|
|
46
36
|
})
|
|
47
37
|
);
|
|
48
38
|
|
|
39
|
+
jest.mock(
|
|
40
|
+
"@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive",
|
|
41
|
+
() => ({
|
|
42
|
+
useIsScreenActive: jest.fn(() => true),
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
|
|
49
46
|
const river = {
|
|
50
47
|
home: true,
|
|
51
48
|
id: "A1234",
|
|
@@ -89,28 +86,17 @@ const riverProps = {
|
|
|
89
86
|
|
|
90
87
|
const appData = { layoutVersion: "v1" };
|
|
91
88
|
|
|
92
|
-
const store =
|
|
89
|
+
const store = { rivers, appData };
|
|
93
90
|
|
|
94
91
|
describe("When River has a general_content type", () => {
|
|
95
92
|
it("renders GeneralContentScreen correctly", () => {
|
|
96
|
-
const { getByTestId } =
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
</Provider>
|
|
93
|
+
const { getByTestId } = renderWithProviders(
|
|
94
|
+
<RiverComponent {...riverProps} />,
|
|
95
|
+
store
|
|
100
96
|
);
|
|
101
97
|
|
|
102
98
|
expect(getByTestId("general-content-screen")).toBeTruthy();
|
|
103
99
|
});
|
|
104
|
-
|
|
105
|
-
it("renders River component correctly", () => {
|
|
106
|
-
const wrapper = render(
|
|
107
|
-
<Provider store={store}>
|
|
108
|
-
<River screenId="A1234" />
|
|
109
|
-
</Provider>
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
113
|
-
});
|
|
114
100
|
});
|
|
115
101
|
|
|
116
102
|
describe("When River has any other type other than general_content", () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
-
import { connectToStore } from "@applicaster/zapp-react-native-redux";
|
|
2
|
+
import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
|
|
3
3
|
|
|
4
4
|
import { withRiver } from "../../Decorators/RiverResolver";
|
|
5
5
|
import { withNavigator } from "../../Decorators/Navigator";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View } from "react-native";
|
|
3
|
-
import {
|
|
3
|
+
import { render } from "@testing-library/react-native";
|
|
4
4
|
|
|
5
5
|
const Mocked_RouteManager = jest.fn((props) => (
|
|
6
6
|
<View testID="routeManager" {...props} />
|
|
@@ -68,12 +68,33 @@ jest.mock(
|
|
|
68
68
|
})
|
|
69
69
|
);
|
|
70
70
|
|
|
71
|
+
jest.mock(
|
|
72
|
+
"@applicaster/zapp-react-native-utils/reactHooks/state/useRivers",
|
|
73
|
+
() => ({
|
|
74
|
+
useRivers: jest.fn(() => ({
|
|
75
|
+
rivers: [{ id: "testId", name: "Test River" }],
|
|
76
|
+
})),
|
|
77
|
+
})
|
|
78
|
+
);
|
|
79
|
+
|
|
71
80
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
72
81
|
isNavBarVisible: mockIsNavBarVisible,
|
|
73
82
|
useIsScreenActive: jest.fn().mockReturnValue(true),
|
|
83
|
+
useNavigation: jest.fn(() => ({
|
|
84
|
+
canGoBack: () => false,
|
|
85
|
+
currentRoute: "/river/testId",
|
|
86
|
+
activeRiver: { id: "testId" },
|
|
87
|
+
screenData: { id: "testId" },
|
|
88
|
+
data: { screen: { id: "testId" } },
|
|
89
|
+
})),
|
|
90
|
+
useRoute: jest.fn(() => ({
|
|
91
|
+
pathname: "/river/testId",
|
|
92
|
+
screenData: { id: "testId" },
|
|
93
|
+
})),
|
|
74
94
|
}));
|
|
75
95
|
|
|
76
96
|
jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
97
|
+
...jest.requireActual("@applicaster/zapp-react-native-utils/reactHooks"),
|
|
77
98
|
useCurrentScreenData: jest.fn(() => ({
|
|
78
99
|
id: "testId",
|
|
79
100
|
})),
|
|
@@ -84,17 +105,6 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
|
84
105
|
id: "testId",
|
|
85
106
|
navigations: [{ id: "testId", category: "nav_bar" }],
|
|
86
107
|
})),
|
|
87
|
-
useNavigation: jest.fn(() => ({
|
|
88
|
-
canGoBack: () => false,
|
|
89
|
-
currentRoute: "/river/testId",
|
|
90
|
-
activeRiver: { id: "testId" },
|
|
91
|
-
screenData: { id: "testId" },
|
|
92
|
-
data: { screen: { id: "testId" } },
|
|
93
|
-
})),
|
|
94
|
-
useRoute: jest.fn(() => ({
|
|
95
|
-
pathname: "/river/testId",
|
|
96
|
-
screenData: { id: "testId" },
|
|
97
|
-
})),
|
|
98
108
|
useDimensions: jest.fn(() => ({
|
|
99
109
|
width: 1920,
|
|
100
110
|
height: 1080,
|
|
@@ -102,10 +112,11 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
|
|
|
102
112
|
useIsTablet: jest.fn(() => false),
|
|
103
113
|
}));
|
|
104
114
|
|
|
105
|
-
jest.mock("@applicaster/zapp-react-native-redux/hooks
|
|
115
|
+
jest.mock("@applicaster/zapp-react-native-redux/hooks", () => {
|
|
106
116
|
const View = jest.requireActual("react-native").View;
|
|
107
117
|
|
|
108
118
|
return {
|
|
119
|
+
...jest.requireActual("@applicaster/zapp-react-native-redux/hooks"),
|
|
109
120
|
usePickFromState: () => ({
|
|
110
121
|
plugins: [
|
|
111
122
|
{
|
|
@@ -141,8 +152,6 @@ const screenProps = {
|
|
|
141
152
|
const { Screen } = require("..");
|
|
142
153
|
|
|
143
154
|
describe("<Screen Component />", () => {
|
|
144
|
-
let wrapper;
|
|
145
|
-
|
|
146
155
|
beforeEach(() => {
|
|
147
156
|
allowedOrientationsForScreen.mockClear();
|
|
148
157
|
getOrientation.mockClear();
|
|
@@ -150,26 +159,16 @@ describe("<Screen Component />", () => {
|
|
|
150
159
|
});
|
|
151
160
|
|
|
152
161
|
describe("when the navbar should show", () => {
|
|
153
|
-
act(() => {
|
|
154
|
-
wrapper = create(<Screen {...screenProps} />);
|
|
155
|
-
});
|
|
156
|
-
|
|
157
162
|
it("renders correctly", () => {
|
|
158
|
-
|
|
163
|
+
const { toJSON } = render(<Screen {...screenProps} />);
|
|
164
|
+
expect(toJSON()).toMatchSnapshot();
|
|
159
165
|
});
|
|
160
166
|
});
|
|
161
167
|
|
|
162
168
|
describe("when the navbar should be hidden", () => {
|
|
163
|
-
beforeEach(() => {
|
|
164
|
-
wrapper = create(<Screen {...screenProps} />);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
169
|
it("renders correctly", () => {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
170
|
+
const { toJSON } = render(<Screen {...screenProps} />);
|
|
171
|
+
expect(toJSON()).toMatchSnapshot();
|
|
173
172
|
});
|
|
174
173
|
});
|
|
175
174
|
});
|
package/Components/Tabs/Tabs.tsx
CHANGED
|
@@ -158,12 +158,11 @@ const TabsComponent = ({
|
|
|
158
158
|
|
|
159
159
|
const renderItem = React.useCallback(
|
|
160
160
|
({ item, index, item: { id } }: RenderItemProps) => (
|
|
161
|
-
|
|
161
|
+
<React.Fragment key={id}>
|
|
162
162
|
<Tab
|
|
163
163
|
ref={(ref) => {
|
|
164
164
|
tabRefs.current[index] = ref;
|
|
165
165
|
}}
|
|
166
|
-
key={id}
|
|
167
166
|
{...{
|
|
168
167
|
title: getTitle(item),
|
|
169
168
|
id,
|
|
@@ -177,7 +176,7 @@ const TabsComponent = ({
|
|
|
177
176
|
{display_mode === "fractional" &&
|
|
178
177
|
index !== tabs?.length - 1 &&
|
|
179
178
|
renderGutter()}
|
|
180
|
-
|
|
179
|
+
</React.Fragment>
|
|
181
180
|
),
|
|
182
181
|
[tabs, display_mode, configuration, onTabPress, getTitle, getSelectedItem]
|
|
183
182
|
);
|