@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.
- package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
- package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
- package/Components/AudioPlayer/tv/index.tsx +5 -1
- package/Components/Cell/Cell.tsx +3 -6
- package/Components/Cell/TvOSCellComponent.tsx +0 -1
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +64 -0
- package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +18 -0
- package/Components/LinkHandler/LinkHandler.tsx +2 -7
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
- package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
- package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
- package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
- package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
- package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
- package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +87 -97
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +87 -126
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +140 -83
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +252 -123
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +3 -3
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
- package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -66
- package/Components/ModalComponent/Button/index.tsx +5 -3
- package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
- package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
- package/Components/ModalComponent/Header/index.tsx +20 -7
- package/Components/ModalComponent/Header/utils.ts +1 -1
- package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
- package/Components/ModalComponent/SortableList.tsx +156 -0
- package/Components/ModalComponent/SortableList.web.tsx +34 -0
- package/Components/ModalComponent/TextInputContent.tsx +135 -0
- package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
- package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
- package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
- package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
- package/Components/ModalComponent/index.tsx +18 -9
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
- package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
- package/Components/ModalComponent/presets/index.ts +27 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
- package/Components/ModalComponent/presets/standardCell.tsx +41 -0
- package/Components/ModalComponent/presets/types.ts +35 -0
- package/Components/ModalComponent/showTextInput.ts +52 -0
- package/Components/ModalComponent/utils.ts +82 -21
- package/Components/OfflineHandler/hooks.ts +0 -12
- package/Components/PlayerContainer/PlayerContainer.tsx +19 -8
- package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +66 -13
- package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
- package/Components/River/River.tsx +4 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +138 -75
- package/Components/River/__tests__/componentsMap.test.js +8 -3
- package/Components/River/__tests__/resultCallback.test.tsx +47 -0
- package/Components/Screen/__tests__/Screen.test.tsx +24 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +5 -0
- package/Components/Screen/navigationHandler.ts +8 -8
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
- package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
- package/Components/Transitioner/Transitioner.tsx +2 -3
- package/Components/Transitioner/__tests__/transitioner.test.js +83 -2
- package/Components/VideoLive/LiveImageManager.ts +9 -3
- package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
- package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
- package/Contexts/ModalNavigationContext/index.tsx +2 -2
- package/Hooks/useEntryActionsExpander.ts +63 -0
- package/assets/empty-Info.png +0 -0
- package/package.json +6 -5
- package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -332
- /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
|
|
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
|
-
|
|
28
|
-
|
|
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
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
})
|
|
152
|
-
PlayerContainerContextProvider: ({ 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
|
|
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 =
|
|
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
|
|
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
|
-
|
|
133
|
+
theme.screen_padding_left
|
|
135
134
|
),
|
|
136
135
|
paddingRight: ifEmptyUseFallback(
|
|
137
136
|
screenConfig.paddingRight,
|
|
138
|
-
|
|
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
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
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
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
+
});
|