@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1535029240 → 16.0.0-alpha.1701179921

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 (92) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/Cell/Cell.tsx +3 -6
  5. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  6. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
  7. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  8. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +64 -0
  9. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  10. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +18 -0
  11. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  12. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  13. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  14. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  15. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  16. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  17. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  18. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  19. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  20. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  21. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  22. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  23. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  24. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  25. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  26. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  27. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  28. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  29. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  30. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +87 -97
  31. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +87 -126
  32. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +140 -83
  33. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +252 -123
  34. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  35. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  36. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  37. package/Components/ModalComponent/AudioPlayer/Components/index.ts +3 -3
  38. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  39. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  40. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  41. package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -66
  42. package/Components/ModalComponent/Button/index.tsx +5 -3
  43. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  44. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  45. package/Components/ModalComponent/Header/index.tsx +20 -7
  46. package/Components/ModalComponent/Header/utils.ts +1 -1
  47. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  48. package/Components/ModalComponent/SortableList.tsx +156 -0
  49. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  50. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  51. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  52. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  53. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  54. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  55. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  56. package/Components/ModalComponent/index.tsx +18 -9
  57. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  58. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  59. package/Components/ModalComponent/presets/index.ts +27 -0
  60. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  61. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  62. package/Components/ModalComponent/presets/types.ts +35 -0
  63. package/Components/ModalComponent/showTextInput.ts +52 -0
  64. package/Components/ModalComponent/utils.ts +82 -21
  65. package/Components/OfflineHandler/hooks.ts +0 -12
  66. package/Components/PlayerContainer/PlayerContainer.tsx +19 -8
  67. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  68. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +66 -13
  69. package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
  70. package/Components/River/River.tsx +4 -0
  71. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +138 -75
  72. package/Components/River/__tests__/componentsMap.test.js +8 -3
  73. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  74. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  75. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  76. package/Components/Screen/index.tsx +5 -0
  77. package/Components/Screen/navigationHandler.ts +8 -8
  78. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  79. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  80. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  81. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  82. package/Components/Transitioner/Transitioner.tsx +2 -3
  83. package/Components/Transitioner/__tests__/transitioner.test.js +83 -2
  84. package/Components/VideoLive/LiveImageManager.ts +9 -3
  85. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  86. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  87. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  88. package/Hooks/useEntryActionsExpander.ts +63 -0
  89. package/assets/empty-Info.png +0 -0
  90. package/package.json +6 -5
  91. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -332
  92. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -17,16 +17,37 @@ jest.mock(
17
17
  );
18
18
 
19
19
  jest.mock("@applicaster/zapp-react-native-utils/reactUtils", () => ({
20
+ // Defaults to `false` for the hardware-back-handling tests below, which
21
+ // key off `isTV`. The ProgramInfo/content-context describe block below
22
+ // overrides this per test (`isTV()` is also what PlayerContainer's
23
+ // `playerContentValue` uses to pick TV vs mobile data keys, decoupled
24
+ // from `isTvOSPlatform` below).
20
25
  isTV: jest.fn(() => false),
21
26
  isAndroidTVPlatform: jest.fn(() => false),
22
- isTvOSPlatform: jest.fn(() => false),
27
+ // `isTvOSPlatform` backs a module-level constant in PlayerContainer.tsx that
28
+ // is read once at import time, so it must already be `true` here in order
29
+ // to exercise `renderApplePlayer`'s tvOS-only ProgramInfo branch in this
30
+ // test file. It does not affect the hardware-back-handling tests below,
31
+ // which key off `isTV` instead.
32
+ isTvOSPlatform: jest.fn(() => true),
33
+ isApplePlatform: jest.fn(() => true),
23
34
  platformSelect: jest.fn(({ native }) => native),
24
35
  }));
25
36
 
26
- jest.mock("@applicaster/zapp-react-native-utils/playerUtils", () => ({
27
- isAudioItem: jest.fn(() => false),
28
- isInlineTV: jest.fn(() => false),
29
- }));
37
+ jest.mock("@applicaster/zapp-react-native-utils/playerUtils", () => {
38
+ // Keep the real data-key resolver (createConfigValueGetter,
39
+ // resolvePlayerContentText, VIDEO_PLAYER_CONTENT_DATA_KEYS) so the tvOS
40
+ // ProgramInfo data-key tests exercise actual resolution logic, not a stub.
41
+ const actual = jest.requireActual(
42
+ "@applicaster/zapp-react-native-utils/playerUtils"
43
+ );
44
+
45
+ return {
46
+ ...actual,
47
+ isAudioItem: jest.fn(() => false),
48
+ isInlineTV: jest.fn(() => false),
49
+ };
50
+ });
30
51
 
31
52
  jest.mock(
32
53
  "@applicaster/zapp-react-native-tvos-ui-components/Components/TVEventHandlerComponent",
@@ -75,6 +96,11 @@ jest.mock("@applicaster/zapp-react-native-bridge/QuickBrick", () => ({
75
96
  sendQuickBrickEvent: jest.fn(),
76
97
  }));
77
98
 
99
+ // Stubbed so PlayerContainer can render without ProgramInfo's own
100
+ // redux/style plumbing. It no longer needs to capture Title/Subtitle: those
101
+ // come from the player manager's content channel, and the key-set selection
102
+ // they depend on is covered directly in
103
+ // `playerManager/__tests__/playerContent.test.ts`.
78
104
  jest.mock("../ProgramInfo", () => ({
79
105
  ProgramInfo: () => null,
80
106
  }));
@@ -142,14 +168,33 @@ jest.mock(
142
168
  jest.mock("../PlayerContainerContext", () => {
143
169
  const ReactActual = require("react");
144
170
 
171
+ const PlayerContainerContext = ReactActual.createContext({
172
+ isLanguageOverlayVisible: false,
173
+ bottomFocusableId: "bottom",
174
+ showComponentsContainer: false,
175
+ refs: null,
176
+ });
177
+
145
178
  return {
146
- PlayerContainerContext: ReactActual.createContext({
147
- isLanguageOverlayVisible: false,
148
- bottomFocusableId: "bottom",
149
- showComponentsContainer: false,
150
- refs: null,
151
- }),
152
- PlayerContainerContextProvider: ({ children }) => children,
179
+ PlayerContainerContext,
180
+ // Mirrors just enough of the real provider for these tests: renders an
181
+ // actual `PlayerContainerContext.Provider` so the real
182
+ // `PlayerContainerContext.Consumer` inside PlayerContainer.tsx sees the
183
+ // container fields a real provider would give it, instead of a bare
184
+ // `({ children }) => children` passthrough.
185
+ PlayerContainerContextProvider: ({ children }: any) =>
186
+ ReactActual.createElement(
187
+ PlayerContainerContext.Provider,
188
+ {
189
+ value: {
190
+ isLanguageOverlayVisible: false,
191
+ bottomFocusableId: "bottom",
192
+ showComponentsContainer: false,
193
+ refs: null,
194
+ },
195
+ },
196
+ children
197
+ ),
153
198
  };
154
199
  });
155
200
 
@@ -200,7 +245,14 @@ const { __player: mockPlayer } = jest.requireMock(
200
245
  "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer"
201
246
  );
202
247
 
203
- const Player = React.forwardRef(() => null);
248
+ const { isTV: mockIsTV } = jest.requireMock(
249
+ "@applicaster/zapp-react-native-utils/reactUtils"
250
+ );
251
+
252
+ // Renders children so that `renderApplePlayer`'s returned <ProgramInfo>
253
+ // element (passed as this component's `children` prop) actually mounts —
254
+ // otherwise React never invokes the ProgramInfo function component at all.
255
+ const Player = React.forwardRef((props: any, _ref) => props.children ?? null);
204
256
 
205
257
  const item = {
206
258
  id: "entry-1",
@@ -234,6 +286,7 @@ const pressHardwareBack = () => {
234
286
  describe("PlayerContainer hardware back handling", () => {
235
287
  beforeEach(() => {
236
288
  jest.clearAllMocks();
289
+ mockIsTV.mockReturnValue(false);
237
290
  mockBackHandlerRef.current = null;
238
291
  mockPlayer.getPluginConfiguration.mockReturnValue({});
239
292
  mockPlayer.isPaused.mockReturnValue(false);
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
- import * as R from "ramda";
3
2
  import { FlatList, StyleSheet, View } from "react-native";
3
+ import { SafeAreaView, EdgeMode, Edge } from "react-native-safe-area-context";
4
4
  import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
5
5
  import { RiverItem } from "../RiverItem";
6
6
  import { RiverFooter } from "../RiverFooter";
@@ -40,6 +40,12 @@ type Props = {
40
40
  riverId?: string;
41
41
  getStaticComponentFeed: any;
42
42
  stickyHeaderIndices?: number[];
43
+ safeArea?:
44
+ | {
45
+ edges: Record<Edge, EdgeMode>;
46
+ mode: "padding" | "margin" | undefined;
47
+ }
48
+ | undefined;
43
49
  };
44
50
 
45
51
  const styles = StyleSheet.create({
@@ -52,7 +58,7 @@ const getNearestValue = (value, optionA, optionB) =>
52
58
  Math.abs(value - optionA) < Math.abs(value - optionB) ? optionA : optionB;
53
59
 
54
60
  const scrollIndicatorInsets = { right: 1 };
55
- const keyExtractor = R.compose(String, R.prop("id"));
61
+ const keyExtractor = (item) => item.id.toString();
56
62
 
57
63
  function ComponentsMapComponent(props: Props) {
58
64
  const {
@@ -64,6 +70,7 @@ function ComponentsMapComponent(props: Props) {
64
70
  riverId,
65
71
  getStaticComponentFeed,
66
72
  stickyHeaderIndices,
73
+ safeArea,
67
74
  } = props;
68
75
 
69
76
  const flatListRef = React.useRef<FlatList | null>(null);
@@ -77,7 +84,7 @@ function ComponentsMapComponent(props: Props) {
77
84
 
78
85
  const riverComponents = React.useMemo(() => {
79
86
  if (feed?.entry?.length < components.length) {
80
- return R.slice(0, feed?.entry?.length, components);
87
+ return components.slice(0, feed?.entry?.length);
81
88
  }
82
89
 
83
90
  return components;
@@ -92,7 +99,7 @@ function ComponentsMapComponent(props: Props) {
92
99
  const { loadingState, onLoadFinished, onLoadFailed, shouldShowLoadingError } =
93
100
  useLoadingState(riverComponents.length, onLoadDone);
94
101
 
95
- const theme = useTheme();
102
+ const theme = useTheme<BaseThemePropertiesMobile>();
96
103
 
97
104
  const renderRiverItem = React.useCallback(
98
105
  ({ item, index }) => {
@@ -121,21 +128,13 @@ function ComponentsMapComponent(props: Props) {
121
128
 
122
129
  const screenStyle = React.useMemo(
123
130
  () => ({
124
- paddingTop: ifEmptyUseFallback(
125
- screenConfig.paddingTop,
126
- R.prop("screen_padding_top")(theme)
127
- ),
128
- paddingBottom: ifEmptyUseFallback(
129
- screenConfig.paddingBottom,
130
- R.prop("screen_padding_bottom")(theme)
131
- ),
132
131
  paddingLeft: ifEmptyUseFallback(
133
132
  screenConfig.paddingLeft,
134
- R.prop("screen_padding_left")(theme)
133
+ theme.screen_padding_left
135
134
  ),
136
135
  paddingRight: ifEmptyUseFallback(
137
136
  screenConfig.paddingRight,
138
- R.prop("screen_padding_right")(theme)
137
+ theme.screen_padding_right
139
138
  ),
140
139
  }),
141
140
  [riverId]
@@ -266,51 +265,79 @@ function ComponentsMapComponent(props: Props) {
266
265
  numberOfComponents={riverComponents.length}
267
266
  >
268
267
  <ViewportTracker>
269
- <FlatList
270
- testID="components-map-flat-list"
271
- ref={(ref) => {
272
- flatListRef.current = ref;
273
- }}
274
- // Fix for WebView rerender crashes on Android API 28+
275
- // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
276
- overScrollMode={isAndroid ? "never" : "auto"}
277
- scrollIndicatorInsets={scrollIndicatorInsets}
278
- extraData={feed}
279
- stickyHeaderIndices={stickyHeaderIndices}
280
- removeClippedSubviews={isAndroid}
281
- onLayout={handleOnLayout}
282
- initialNumToRender={3}
283
- maxToRenderPerBatch={10}
284
- windowSize={12}
285
- keyExtractor={keyExtractor}
286
- renderItem={renderRiverItem}
287
- data={riverComponents}
288
- contentContainerStyle={contentContainerStyle}
289
- ListFooterComponent={
290
- <RiverFooter
291
- flatListHeight={flatListHeight}
292
- loadingState={loadingState}
293
- />
294
- }
295
- refreshControl={refreshControl}
296
- onScrollBeginDrag={onScrollBeginDrag}
297
- onScroll={onScroll}
298
- onMomentumScrollEnd={_onMomentumScrollEnd}
299
- onScrollEndDrag={_onScrollEndDrag}
300
- scrollEventThrottle={16}
301
- {...scrollViewExtraProps}
302
- onEndReached={
303
- /* When wrapped in a parent ScrollView (e.g. tabs),
268
+ <>
269
+ <FlatList
270
+ ListHeaderComponent={
271
+ <SafeAreaView
272
+ mode={safeArea?.mode ?? "margin"}
273
+ style={{
274
+ paddingTop: ifEmptyUseFallback(
275
+ screenConfig.paddingTop,
276
+ theme.screen_padding_top
277
+ ),
278
+ height: StyleSheet.hairlineWidth,
279
+ }}
280
+ edges={{ top: safeArea?.edges?.top ?? "off" }}
281
+ />
282
+ }
283
+ testID="components-map-flat-list"
284
+ ref={(ref) => {
285
+ flatListRef.current = ref;
286
+ }}
287
+ // Fix for WebView rerender crashes on Android API 28+
288
+ // https://github.com/react-native-webview/react-native-webview/issues/1915#issuecomment-964035468
289
+ overScrollMode={isAndroid ? "never" : "auto"}
290
+ scrollIndicatorInsets={scrollIndicatorInsets}
291
+ extraData={feed}
292
+ stickyHeaderIndices={stickyHeaderIndices}
293
+ removeClippedSubviews={isAndroid}
294
+ onLayout={handleOnLayout}
295
+ initialNumToRender={3}
296
+ maxToRenderPerBatch={10}
297
+ windowSize={12}
298
+ keyExtractor={keyExtractor}
299
+ renderItem={renderRiverItem}
300
+ data={riverComponents}
301
+ contentContainerStyle={contentContainerStyle}
302
+ ListFooterComponent={
303
+ <>
304
+ <RiverFooter
305
+ flatListHeight={flatListHeight}
306
+ loadingState={loadingState}
307
+ />
308
+ <SafeAreaView
309
+ mode={safeArea?.mode ?? "margin"}
310
+ style={{
311
+ paddingBottom: ifEmptyUseFallback(
312
+ screenConfig.paddingBottom,
313
+ theme.screen_padding_bottom
314
+ ),
315
+ height: StyleSheet.hairlineWidth,
316
+ }}
317
+ edges={{ bottom: safeArea?.edges?.bottom ?? "off" }}
318
+ />
319
+ </>
320
+ }
321
+ refreshControl={refreshControl}
322
+ onScrollBeginDrag={onScrollBeginDrag}
323
+ onScroll={onScroll}
324
+ onMomentumScrollEnd={_onMomentumScrollEnd}
325
+ onScrollEndDrag={_onScrollEndDrag}
326
+ scrollEventThrottle={16}
327
+ {...scrollViewExtraProps}
328
+ onEndReached={
329
+ /* When wrapped in a parent ScrollView (e.g. tabs),
304
330
  this FlatList doesn't scroll so onEndReached can fire repeatedly;
305
331
  skip it here and let the parent ScrollView emit scroll-end instead. */
306
- isScreenWrappedInContainer
307
- ? undefined
308
- : () => {
309
- if (!hasUserScrolledRef.current) return;
310
- emitScrollEndReached();
311
- }
312
- }
313
- />
332
+ isScreenWrappedInContainer
333
+ ? undefined
334
+ : () => {
335
+ if (!hasUserScrolledRef.current) return;
336
+ emitScrollEndReached();
337
+ }
338
+ }
339
+ />
340
+ </>
314
341
  </ViewportTracker>
315
342
  </ScreenLoadingMeasurements>
316
343
  </ComponentsMapRefContext.Provider>
@@ -320,6 +347,5 @@ function ComponentsMapComponent(props: Props) {
320
347
  }
321
348
 
322
349
  export const ComponentsMap = React.memo(
323
- withComponentsMapProvider(ComponentsMapComponent),
324
- R.equals
350
+ withComponentsMapProvider(ComponentsMapComponent)
325
351
  );
@@ -20,6 +20,8 @@ type Props = ZappScreenProps & {
20
20
  setIsScreenWrappedInContainer: (isInsideContainer: boolean) => void;
21
21
  screenContext: ZappRiver;
22
22
  river: ZappRiver;
23
+ /** Result channel for screens presented as a modal; forwarded to the resolved screen. */
24
+ resultCallback?: hookCallback;
23
25
  };
24
26
 
25
27
  export class RiverComponent extends React.Component<Props> {
@@ -64,6 +66,7 @@ export class RiverComponent extends React.Component<Props> {
64
66
  isInsideContainer,
65
67
  groupId,
66
68
  scrollViewExtraProps,
69
+ resultCallback,
67
70
  } = this.props;
68
71
 
69
72
  const { id, type } = river;
@@ -77,6 +80,7 @@ export class RiverComponent extends React.Component<Props> {
77
80
  })}
78
81
  screenId={id}
79
82
  screenType={type}
83
+ resultCallback={resultCallback}
80
84
  />
81
85
  );
82
86
  }
@@ -14,95 +14,125 @@ exports[`componentsMap renders renders components map correctly 1`] = `
14
14
  >
15
15
  <RCTScrollView
16
16
  ListFooterComponent={
17
- <Memo(RiverFooterComponent)
18
- flatListHeight={null}
19
- loadingState={
20
- BehaviorSubject {
21
- "_value": {
22
- "done": false,
23
- "index": -1,
24
- "waitForAllComponents": false,
25
- },
26
- "closed": false,
27
- "currentObservers": null,
28
- "hasError": false,
29
- "isStopped": false,
30
- "observers": [
31
- SafeSubscriber {
32
- "_finalizers": [
33
- Subscription {
34
- "_finalizers": null,
35
- "_parentage": [Circular],
36
- "closed": false,
37
- "initialTeardown": [Function],
38
- },
39
- ],
40
- "_parentage": null,
41
- "closed": false,
42
- "destination": ConsumerObserver {
43
- "partialObserver": {
44
- "complete": undefined,
45
- "error": undefined,
46
- "next": [Function],
47
- },
48
- },
49
- "initialTeardown": undefined,
50
- "isStopped": false,
17
+ <React.Fragment>
18
+ <Memo(RiverFooterComponent)
19
+ flatListHeight={null}
20
+ loadingState={
21
+ BehaviorSubject {
22
+ "_value": {
23
+ "done": false,
24
+ "index": -1,
25
+ "waitForAllComponents": false,
51
26
  },
52
- SafeSubscriber {
53
- "_finalizers": [
54
- Subscription {
55
- "_finalizers": null,
56
- "_parentage": [Circular],
57
- "closed": false,
58
- "initialTeardown": [Function],
59
- },
60
- ],
61
- "_parentage": null,
62
- "closed": false,
63
- "destination": ConsumerObserver {
64
- "partialObserver": {
65
- "complete": undefined,
66
- "error": undefined,
67
- "next": [Function],
27
+ "closed": false,
28
+ "currentObservers": null,
29
+ "hasError": false,
30
+ "isStopped": false,
31
+ "observers": [
32
+ SafeSubscriber {
33
+ "_finalizers": [
34
+ Subscription {
35
+ "_finalizers": null,
36
+ "_parentage": [Circular],
37
+ "closed": false,
38
+ "initialTeardown": [Function],
39
+ },
40
+ ],
41
+ "_parentage": null,
42
+ "closed": false,
43
+ "destination": ConsumerObserver {
44
+ "partialObserver": {
45
+ "complete": undefined,
46
+ "error": undefined,
47
+ "next": [Function],
48
+ },
68
49
  },
50
+ "initialTeardown": undefined,
51
+ "isStopped": false,
69
52
  },
70
- "initialTeardown": undefined,
71
- "isStopped": false,
72
- },
73
- SafeSubscriber {
74
- "_finalizers": [
75
- Subscription {
76
- "_finalizers": null,
77
- "_parentage": [Circular],
78
- "closed": false,
79
- "initialTeardown": [Function],
53
+ SafeSubscriber {
54
+ "_finalizers": [
55
+ Subscription {
56
+ "_finalizers": null,
57
+ "_parentage": [Circular],
58
+ "closed": false,
59
+ "initialTeardown": [Function],
60
+ },
61
+ ],
62
+ "_parentage": null,
63
+ "closed": false,
64
+ "destination": ConsumerObserver {
65
+ "partialObserver": {
66
+ "complete": undefined,
67
+ "error": undefined,
68
+ "next": [Function],
69
+ },
80
70
  },
81
- ],
82
- "_parentage": null,
83
- "closed": false,
84
- "destination": ConsumerObserver {
85
- "partialObserver": {
86
- "complete": undefined,
87
- "error": undefined,
88
- "next": [Function],
71
+ "initialTeardown": undefined,
72
+ "isStopped": false,
73
+ },
74
+ SafeSubscriber {
75
+ "_finalizers": [
76
+ Subscription {
77
+ "_finalizers": null,
78
+ "_parentage": [Circular],
79
+ "closed": false,
80
+ "initialTeardown": [Function],
81
+ },
82
+ ],
83
+ "_parentage": null,
84
+ "closed": false,
85
+ "destination": ConsumerObserver {
86
+ "partialObserver": {
87
+ "complete": undefined,
88
+ "error": undefined,
89
+ "next": [Function],
90
+ },
89
91
  },
92
+ "initialTeardown": undefined,
93
+ "isStopped": false,
90
94
  },
91
- "initialTeardown": undefined,
92
- "isStopped": false,
93
- },
94
- ],
95
- "thrownError": null,
95
+ ],
96
+ "thrownError": null,
97
+ }
98
+ }
99
+ />
100
+ <View
101
+ edges={
102
+ {
103
+ "bottom": "off",
104
+ }
105
+ }
106
+ mode="margin"
107
+ style={
108
+ {
109
+ "height": 0.5,
110
+ "paddingBottom": undefined,
111
+ }
112
+ }
113
+ />
114
+ </React.Fragment>
115
+ }
116
+ ListHeaderComponent={
117
+ <View
118
+ edges={
119
+ {
120
+ "top": "off",
121
+ }
122
+ }
123
+ mode="margin"
124
+ style={
125
+ {
126
+ "height": 0.5,
127
+ "paddingTop": undefined,
96
128
  }
97
129
  }
98
130
  />
99
131
  }
100
132
  contentContainerStyle={
101
133
  {
102
- "paddingBottom": undefined,
103
134
  "paddingLeft": undefined,
104
135
  "paddingRight": undefined,
105
- "paddingTop": undefined,
106
136
  }
107
137
  }
108
138
  data={
@@ -156,6 +186,25 @@ exports[`componentsMap renders renders components map correctly 1`] = `
156
186
  windowSize={12}
157
187
  >
158
188
  <View>
189
+ <View
190
+ collapsable={false}
191
+ onLayout={[Function]}
192
+ >
193
+ <View
194
+ edges={
195
+ {
196
+ "top": "off",
197
+ }
198
+ }
199
+ mode="margin"
200
+ style={
201
+ {
202
+ "height": 0.5,
203
+ "paddingTop": undefined,
204
+ }
205
+ }
206
+ />
207
+ </View>
159
208
  <View
160
209
  onFocusCapture={[Function]}
161
210
  onLayout={[Function]}
@@ -200,6 +249,20 @@ exports[`componentsMap renders renders components map correctly 1`] = `
200
249
  size="large"
201
250
  />
202
251
  </View>
252
+ <View
253
+ edges={
254
+ {
255
+ "bottom": "off",
256
+ }
257
+ }
258
+ mode="margin"
259
+ style={
260
+ {
261
+ "height": 0.5,
262
+ "paddingBottom": undefined,
263
+ }
264
+ }
265
+ />
203
266
  </View>
204
267
  </View>
205
268
  </RCTScrollView>
@@ -11,9 +11,14 @@ jest.mock(
11
11
  () => () => null
12
12
  );
13
13
 
14
- jest.mock("react-native-safe-area-context", () => ({
15
- useSafeAreaInsets: () => ({ top: 50 }),
16
- }));
14
+ jest.mock("react-native-safe-area-context", () => {
15
+ const { View } = jest.requireActual("react-native");
16
+
17
+ return {
18
+ SafeAreaView: View,
19
+ useSafeAreaInsets: () => ({ top: 50 }),
20
+ };
21
+ });
17
22
 
18
23
  jest.mock("react-native", () => {
19
24
  const RN = jest.genMockFromModule("react-native");
@@ -0,0 +1,47 @@
1
+ import React from "react";
2
+ import { render } from "@testing-library/react-native";
3
+
4
+ import { RiverComponent } from "../River";
5
+
6
+ const mockResolverProps: any[] = [];
7
+
8
+ jest.mock("../../ScreenResolver", () => ({
9
+ ScreenResolver: (props: any) => {
10
+ mockResolverProps.push(props);
11
+
12
+ return null;
13
+ },
14
+ }));
15
+
16
+ const river = { id: "river-parent-lock", type: "parent_lock" } as any;
17
+
18
+ const baseProps = {
19
+ river,
20
+ screenData: {},
21
+ groupId: "group-1",
22
+ scrollViewExtraProps: {},
23
+ setIsScreenWrappedInContainer: jest.fn(),
24
+ screenContext: { navBar: { setTitle: jest.fn(), setSummary: jest.fn() } },
25
+ } as any;
26
+
27
+ const resolved = () => mockResolverProps[mockResolverProps.length - 1];
28
+
29
+ describe("RiverComponent", () => {
30
+ beforeEach(() => {
31
+ mockResolverProps.length = 0;
32
+ });
33
+
34
+ it("forwards resultCallback to the screen it resolves", () => {
35
+ const resultCallback = jest.fn();
36
+
37
+ render(<RiverComponent {...baseProps} resultCallback={resultCallback} />);
38
+
39
+ expect(resolved().resultCallback).toBe(resultCallback);
40
+ });
41
+
42
+ it("resolves the screen without a resultCallback when none was given", () => {
43
+ render(<RiverComponent {...baseProps} />);
44
+
45
+ expect(resolved().resultCallback).toBeUndefined();
46
+ });
47
+ });