@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4342066143 → 12.0.0-alpha.4354608902
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 +3 -3
- package/Components/Cell/Cell.tsx +14 -13
- package/Components/Cell/CellWithFocusable.tsx +24 -10
- package/Components/Cell/TvOSCellComponent.tsx +6 -11
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableGroup/index.tsx +11 -2
- package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
- package/Components/Layout/TV/ScreenContainer.tsx +79 -5
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
- package/Components/Layout/TV/__tests__/index.test.tsx +20 -75
- package/Components/Layout/TV/index.tsx +11 -67
- package/Components/Layout/TV/index.web.tsx +33 -0
- package/Components/Layout/index.tsx +15 -11
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +7 -2
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +20 -0
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +17 -30
- package/Components/NativeFocusables/{index.js → index.ts} +4 -2
- package/Components/Navigator/StackNavigator.tsx +57 -0
- package/Components/Navigator/index.ts +1 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +5 -1
- package/Components/River/TV/River.tsx +0 -23
- package/Components/Screen/TV/hooks/index.ts +3 -0
- package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
- package/Components/Screen/TV/hooks/useRestoreFocus.ts +22 -0
- package/Components/Screen/TV/index.web.tsx +165 -0
- package/Components/Screen/__tests__/Screen.test.tsx +3 -0
- package/Components/Screen/index.tsx +55 -58
- package/Components/Screen/navigationHandler.ts +5 -0
- package/Components/ScreenResolver/index.tsx +3 -1
- package/Components/Transitioner/Scene.tsx +12 -11
- package/Components/Transitioner/Transitioner.tsx +15 -29
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
- package/Components/VideoLive/LiveImageManager.ts +25 -1
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +2 -14
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +56 -43
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +42 -32
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +104 -86
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +32 -4
- package/Components/VideoModal/ModalAnimation/utils.ts +7 -4
- package/Components/VideoModal/PlayerWrapper.tsx +10 -10
- package/Components/VideoModal/VideoModal.tsx +4 -4
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +32 -12
- package/Components/VideoModal/hooks/useModalSize.ts +10 -12
- package/Components/VideoModal/utils.ts +5 -2
- package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
- package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
- package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
- package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
- package/Contexts/FocusableGroupContext/index.ts +7 -0
- package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
- package/Contexts/ScreenDataContext/index.tsx +20 -0
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/Decorators/Analytics/{index.js → index.tsx} +11 -11
- package/README.md +1 -13
- package/package.json +5 -5
- package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
- package/Contexts/ScreenDataContext/index.ts +0 -3
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
|
@@ -2,11 +2,16 @@ import React, { useContext } from "react";
|
|
|
2
2
|
import { Animated } from "react-native";
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
useSafeAreaFrame,
|
|
6
5
|
useSafeAreaInsets,
|
|
6
|
+
useSafeAreaFrame,
|
|
7
7
|
} from "react-native-safe-area-context";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
useGetBottomTabBarHeight,
|
|
10
|
+
useNavigation,
|
|
11
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
|
+
import { isLive } from "@applicaster/zapp-react-native-utils/playerUtils";
|
|
9
13
|
|
|
14
|
+
import { PROGRESS_BAR_HEIGHT } from "./utils";
|
|
10
15
|
import { useConfiguration } from "../utils";
|
|
11
16
|
|
|
12
17
|
export enum PlayerAnimationStateEnum {
|
|
@@ -39,6 +44,8 @@ export type ModalAnimationContextT = {
|
|
|
39
44
|
setLastSnap: (value: number) => void;
|
|
40
45
|
tabletLandscapePlayerTopPosition: number;
|
|
41
46
|
setTabletLandscapePlayerTopPosition: (value: number) => void;
|
|
47
|
+
startComponentsAnimationDistance: number;
|
|
48
|
+
progressBarHeight: number;
|
|
42
49
|
};
|
|
43
50
|
|
|
44
51
|
const ReactContext = React.createContext<ModalAnimationContextT>({
|
|
@@ -62,6 +69,8 @@ const ReactContext = React.createContext<ModalAnimationContextT>({
|
|
|
62
69
|
setLastSnap: () => null,
|
|
63
70
|
tabletLandscapePlayerTopPosition: 0,
|
|
64
71
|
setTabletLandscapePlayerTopPosition: () => null,
|
|
72
|
+
startComponentsAnimationDistance: 0,
|
|
73
|
+
progressBarHeight: 0,
|
|
65
74
|
});
|
|
66
75
|
|
|
67
76
|
export function useModalAnimationContext() {
|
|
@@ -80,6 +89,11 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
80
89
|
const [startComponentsAnimation, setStartComponentsAnimation] =
|
|
81
90
|
React.useState<boolean>(false);
|
|
82
91
|
|
|
92
|
+
const {
|
|
93
|
+
videoModalState: { mode, visible, item },
|
|
94
|
+
} = useNavigation();
|
|
95
|
+
|
|
96
|
+
const isLiveItem = isLive(item);
|
|
83
97
|
const { minimised_height: minimisedHeight } = useConfiguration();
|
|
84
98
|
|
|
85
99
|
const resetPlayerAnimationState = React.useCallback(() => {
|
|
@@ -93,16 +107,28 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
93
107
|
const translateYOffset = React.useRef(new Animated.Value(0)).current;
|
|
94
108
|
const dragVideoPlayerY = React.useRef(new Animated.Value(0)).current;
|
|
95
109
|
|
|
96
|
-
const { height } = useSafeAreaFrame();
|
|
110
|
+
const { height: safeAreaFrameHeight } = useSafeAreaFrame();
|
|
111
|
+
const [height, setHeight] = React.useState<number>(safeAreaFrameHeight);
|
|
112
|
+
const progressBarHeight = isLiveItem ? 0 : PROGRESS_BAR_HEIGHT;
|
|
97
113
|
const { bottom: bottomSafeArea } = useSafeAreaInsets();
|
|
98
114
|
const bottomTabBarHeight = useGetBottomTabBarHeight();
|
|
115
|
+
const startComponentsAnimationDistance = Math.round((height * 60) / 100);
|
|
116
|
+
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
if (visible && mode === "MAXIMIZED" && height !== safeAreaFrameHeight) {
|
|
119
|
+
setHeight(safeAreaFrameHeight);
|
|
120
|
+
}
|
|
121
|
+
}, [height, safeAreaFrameHeight, mode, visible]);
|
|
99
122
|
|
|
100
123
|
/*
|
|
101
124
|
If bottomTabBarHeight is equal 0 it means an app does not use bottomTabBar.
|
|
102
125
|
Because of this we need to minus bottom SafeArea offset.
|
|
103
126
|
*/
|
|
104
127
|
const minValue =
|
|
105
|
-
height -
|
|
128
|
+
height -
|
|
129
|
+
minimisedHeight -
|
|
130
|
+
(bottomTabBarHeight || bottomSafeArea) -
|
|
131
|
+
progressBarHeight;
|
|
106
132
|
|
|
107
133
|
const modalSnapPoints = React.useMemo(() => [0, minValue], [minValue]);
|
|
108
134
|
// Last snap state which will helps us to make smooth responder to scrollview animation
|
|
@@ -134,6 +160,8 @@ const Provider = ({ children }: { children: React.ReactNode }) => {
|
|
|
134
160
|
setLastSnap,
|
|
135
161
|
tabletLandscapePlayerTopPosition,
|
|
136
162
|
setTabletLandscapePlayerTopPosition,
|
|
163
|
+
startComponentsAnimationDistance,
|
|
164
|
+
progressBarHeight,
|
|
137
165
|
}}
|
|
138
166
|
>
|
|
139
167
|
{children}
|
|
@@ -13,6 +13,7 @@ export enum ComponentAnimationType {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export const AUDIO_PLAYER_HORIZONTAL_PADDING = 15;
|
|
16
|
+
export const PROGRESS_BAR_HEIGHT = 3;
|
|
16
17
|
|
|
17
18
|
export const defaultAspectRatioWidth = (height: number): number =>
|
|
18
19
|
(height / 9) * 16;
|
|
@@ -233,7 +234,7 @@ export const gestureListenerHelper = (data) => {
|
|
|
233
234
|
animatedValue,
|
|
234
235
|
calculationData,
|
|
235
236
|
modalSnapPoint,
|
|
236
|
-
|
|
237
|
+
startComponentsAnimationDistance,
|
|
237
238
|
startComponentsAnimation,
|
|
238
239
|
setStartComponentsAnimation,
|
|
239
240
|
getAnimationValue,
|
|
@@ -243,7 +244,7 @@ export const gestureListenerHelper = (data) => {
|
|
|
243
244
|
if (listenerValue <= 0) {
|
|
244
245
|
if (
|
|
245
246
|
preparedValue >= 0 &&
|
|
246
|
-
preparedValue <= modalSnapPoint -
|
|
247
|
+
preparedValue <= modalSnapPoint - startComponentsAnimationDistance
|
|
247
248
|
) {
|
|
248
249
|
!startComponentsAnimation && setStartComponentsAnimation(true);
|
|
249
250
|
|
|
@@ -263,9 +264,11 @@ export const gestureListenerHelper = (data) => {
|
|
|
263
264
|
animatedValue.setValue(value);
|
|
264
265
|
}
|
|
265
266
|
} else {
|
|
266
|
-
if (preparedValue >=
|
|
267
|
+
if (preparedValue >= startComponentsAnimationDistance) {
|
|
267
268
|
!startComponentsAnimation && setStartComponentsAnimation(true);
|
|
268
|
-
const value = Math.round(
|
|
269
|
+
const value = Math.round(
|
|
270
|
+
preparedValue - startComponentsAnimationDistance
|
|
271
|
+
);
|
|
269
272
|
|
|
270
273
|
animatedValue.setValue(
|
|
271
274
|
calculateAnimationValue(animationType, value, calculationData)
|
|
@@ -184,8 +184,8 @@ export const PlayerWrapper = (props: Props) => {
|
|
|
184
184
|
const pathname = `${ROUTE_TYPES.VIDEO_MODAL}/${entry?.id}`;
|
|
185
185
|
|
|
186
186
|
return (
|
|
187
|
-
<
|
|
188
|
-
<
|
|
187
|
+
<PathnameContext.Provider value={pathname}>
|
|
188
|
+
<ScreenContextProvider pathname={pathname}>
|
|
189
189
|
<WrapperView
|
|
190
190
|
edges={getEdges(isTablet, isInlineModal) as readonly Edge[]}
|
|
191
191
|
style={[
|
|
@@ -197,10 +197,10 @@ export const PlayerWrapper = (props: Props) => {
|
|
|
197
197
|
<AnimationComponent
|
|
198
198
|
animationType={ComponentAnimationType.moveUpComponent}
|
|
199
199
|
additionalData={{ saveArea: true }}
|
|
200
|
-
style={directionStyles(isTabletLandscape)}
|
|
200
|
+
style={[directionStyles(isTabletLandscape), defaultStyles.flex]}
|
|
201
201
|
>
|
|
202
|
-
<
|
|
203
|
-
<
|
|
202
|
+
<PathnameContext.Provider value={pathname}>
|
|
203
|
+
<ScreenContextProvider pathname={pathname}>
|
|
204
204
|
<View
|
|
205
205
|
testID={`${entry?.id}-player-container`}
|
|
206
206
|
style={[
|
|
@@ -225,7 +225,7 @@ export const PlayerWrapper = (props: Props) => {
|
|
|
225
225
|
</AnimationComponent>
|
|
226
226
|
</View>
|
|
227
227
|
<AnimatedScrollModal>
|
|
228
|
-
{isInlineModal ? (
|
|
228
|
+
{isInlineModal && !docked ? (
|
|
229
229
|
<AnimationComponent
|
|
230
230
|
animationType={ComponentAnimationType.componentFade}
|
|
231
231
|
style={defaultStyles.flex}
|
|
@@ -241,11 +241,11 @@ export const PlayerWrapper = (props: Props) => {
|
|
|
241
241
|
<View style={defaultStyles.playerDetails} />
|
|
242
242
|
)}
|
|
243
243
|
</AnimatedScrollModal>
|
|
244
|
-
</
|
|
245
|
-
</
|
|
244
|
+
</ScreenContextProvider>
|
|
245
|
+
</PathnameContext.Provider>
|
|
246
246
|
</AnimationComponent>
|
|
247
247
|
</WrapperView>
|
|
248
|
-
</
|
|
249
|
-
</
|
|
248
|
+
</ScreenContextProvider>
|
|
249
|
+
</PathnameContext.Provider>
|
|
250
250
|
);
|
|
251
251
|
};
|
|
@@ -129,8 +129,8 @@ const VideoModalComponent = () => {
|
|
|
129
129
|
const pathname = `${ROUTE_TYPES.VIDEO_MODAL}/${item?.id}`;
|
|
130
130
|
|
|
131
131
|
return (
|
|
132
|
-
<
|
|
133
|
-
<
|
|
132
|
+
<PathnameContext.Provider value={pathname}>
|
|
133
|
+
<ScreenContextProvider pathname={pathname}>
|
|
134
134
|
{itemIdHooksFinished === item?.id ? (
|
|
135
135
|
<AnimatedPlayerModalWrapper
|
|
136
136
|
style={[
|
|
@@ -153,8 +153,8 @@ const VideoModalComponent = () => {
|
|
|
153
153
|
<Spinner />
|
|
154
154
|
</View>
|
|
155
155
|
)}
|
|
156
|
-
</
|
|
157
|
-
</
|
|
156
|
+
</ScreenContextProvider>
|
|
157
|
+
</PathnameContext.Provider>
|
|
158
158
|
);
|
|
159
159
|
};
|
|
160
160
|
|
|
@@ -42,9 +42,14 @@ exports[`PlayerWrapper renders inline 1`] = `
|
|
|
42
42
|
}
|
|
43
43
|
animationType="moveUpComponent"
|
|
44
44
|
style={
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
Array [
|
|
46
|
+
Object {
|
|
47
|
+
"flexDirection": "column",
|
|
48
|
+
},
|
|
49
|
+
Object {
|
|
50
|
+
"flex": 1,
|
|
51
|
+
},
|
|
52
|
+
]
|
|
48
53
|
}
|
|
49
54
|
>
|
|
50
55
|
<View
|
|
@@ -120,9 +125,14 @@ exports[`PlayerWrapper renders inline and docked 1`] = `
|
|
|
120
125
|
}
|
|
121
126
|
animationType="moveUpComponent"
|
|
122
127
|
style={
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
128
|
+
Array [
|
|
129
|
+
Object {
|
|
130
|
+
"flexDirection": "column",
|
|
131
|
+
},
|
|
132
|
+
Object {
|
|
133
|
+
"flex": 1,
|
|
134
|
+
},
|
|
135
|
+
]
|
|
126
136
|
}
|
|
127
137
|
>
|
|
128
138
|
<View
|
|
@@ -198,9 +208,14 @@ exports[`PlayerWrapper renders inline on tablet in landscape orientation 1`] = `
|
|
|
198
208
|
}
|
|
199
209
|
animationType="moveUpComponent"
|
|
200
210
|
style={
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
211
|
+
Array [
|
|
212
|
+
Object {
|
|
213
|
+
"flexDirection": "row",
|
|
214
|
+
},
|
|
215
|
+
Object {
|
|
216
|
+
"flex": 1,
|
|
217
|
+
},
|
|
218
|
+
]
|
|
204
219
|
}
|
|
205
220
|
>
|
|
206
221
|
<View
|
|
@@ -292,9 +307,14 @@ exports[`PlayerWrapper renders properly 1`] = `
|
|
|
292
307
|
}
|
|
293
308
|
animationType="moveUpComponent"
|
|
294
309
|
style={
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
310
|
+
Array [
|
|
311
|
+
Object {
|
|
312
|
+
"flexDirection": "column",
|
|
313
|
+
},
|
|
314
|
+
Object {
|
|
315
|
+
"flex": 1,
|
|
316
|
+
},
|
|
317
|
+
]
|
|
298
318
|
}
|
|
299
319
|
>
|
|
300
320
|
<View
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { Dimensions } from "react-native";
|
|
3
2
|
import { useIsTabletLandscape } from "@applicaster/zapp-react-native-utils/reactHooks/device/useMemoizedIsTablet";
|
|
4
3
|
import {
|
|
5
4
|
addOrientationChangeListener,
|
|
@@ -9,11 +8,10 @@ import {
|
|
|
9
8
|
} from "@applicaster/zapp-react-native-utils/appUtils/orientationHelper";
|
|
10
9
|
|
|
11
10
|
import { getXray } from "@applicaster/zapp-react-native-utils/logger";
|
|
11
|
+
import { useSafeAreaFrame } from "react-native-safe-area-context";
|
|
12
12
|
|
|
13
13
|
const { Logger } = getXray();
|
|
14
14
|
|
|
15
|
-
const { width: WIDTH, height: HEIGHT } = Dimensions.get("window");
|
|
16
|
-
|
|
17
15
|
const logger = new Logger(
|
|
18
16
|
"hooks",
|
|
19
17
|
"zapp-react-native-ui-components/Components/VideoModal"
|
|
@@ -24,20 +22,18 @@ type Size = {
|
|
|
24
22
|
height: number | string;
|
|
25
23
|
};
|
|
26
24
|
|
|
27
|
-
const MODAL_SIZE_FOR_PORTRAIT: Size = {
|
|
28
|
-
width: WIDTH,
|
|
29
|
-
height: HEIGHT,
|
|
30
|
-
};
|
|
31
|
-
|
|
32
25
|
const MODAL_SIZE_FOR_LANDSCAPE: Size = {
|
|
33
26
|
width: "100%",
|
|
34
27
|
height: "100%",
|
|
35
28
|
};
|
|
36
29
|
|
|
37
30
|
export const useModalSize = (): Size => {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
const frame = useSafeAreaFrame();
|
|
32
|
+
|
|
33
|
+
const [modalSize, setModalSize] = React.useState<Size>({
|
|
34
|
+
width: frame.width,
|
|
35
|
+
height: frame.height,
|
|
36
|
+
});
|
|
41
37
|
|
|
42
38
|
const setNewModalSize = React.useCallback((newSize, log) => {
|
|
43
39
|
setModalSize((oldSize) => {
|
|
@@ -90,7 +86,9 @@ export const useModalSize = (): Size => {
|
|
|
90
86
|
toOrientation === ORIENTATIONS.landscapeSensor;
|
|
91
87
|
|
|
92
88
|
setNewModalSize(
|
|
93
|
-
isLandscape
|
|
89
|
+
isLandscape
|
|
90
|
+
? MODAL_SIZE_FOR_LANDSCAPE
|
|
91
|
+
: { width: frame.width, height: frame.height },
|
|
94
92
|
`useModalSize: set modal size ${
|
|
95
93
|
isLandscape ? "landscape" : "portrait"
|
|
96
94
|
} for orientation: ${to}`
|
|
@@ -33,8 +33,11 @@ export const useConfiguration = () => {
|
|
|
33
33
|
...pluginConfiguration,
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
const {
|
|
37
|
-
|
|
36
|
+
const {
|
|
37
|
+
minimised_height = 0,
|
|
38
|
+
minimised_height_tablet = 0,
|
|
39
|
+
modal_background_color,
|
|
40
|
+
} = config;
|
|
38
41
|
|
|
39
42
|
const minimisedHeight = useIsTablet()
|
|
40
43
|
? minimised_height_tablet
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { FocusableGroupContext } from "./FocusableGroupContext";
|
|
3
|
+
|
|
4
|
+
type ProviderArgs = {
|
|
5
|
+
id: string;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const FocusableGroupContextProvider = ({
|
|
10
|
+
id,
|
|
11
|
+
children,
|
|
12
|
+
}: ProviderArgs) => (
|
|
13
|
+
<FocusableGroupContext.Provider value={id}>
|
|
14
|
+
{children}
|
|
15
|
+
</FocusableGroupContext.Provider>
|
|
16
|
+
);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React, { useContext } from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import { render } from "@testing-library/react-native";
|
|
4
|
+
import { FocusableGroupContextProvider, FocusableGroupContext } from "../";
|
|
5
|
+
|
|
6
|
+
const MockComponent = () => {
|
|
7
|
+
const focusableContext = useContext(FocusableGroupContext) || "default";
|
|
8
|
+
|
|
9
|
+
return <Text testID={focusableContext} />;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
describe("FocusableGroupContextProvider", () => {
|
|
13
|
+
it("should provide focusable context to the wrapped component", () => {
|
|
14
|
+
const { getByTestId } = render(
|
|
15
|
+
<FocusableGroupContextProvider id="testId">
|
|
16
|
+
<MockComponent />
|
|
17
|
+
</FocusableGroupContextProvider>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
expect(getByTestId("testId")).toBeTruthy();
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Text } from "react-native";
|
|
3
|
+
import { render } from "@testing-library/react-native";
|
|
4
|
+
import { withFocusableContext } from "../withFocusableContext";
|
|
5
|
+
import { FocusableGroupContext } from "../FocusableGroupContext";
|
|
6
|
+
|
|
7
|
+
const MockComponent = ({ groupId }: { groupId: string }) => (
|
|
8
|
+
<Text testID={groupId} />
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
const WrappedComponent = withFocusableContext(MockComponent);
|
|
12
|
+
|
|
13
|
+
describe("withFocusableContext HOC", () => {
|
|
14
|
+
it("should provide focusable context to the wrapped component", () => {
|
|
15
|
+
const { getByTestId } = render(<WrappedComponent />, {
|
|
16
|
+
// eslint-disable-next-line react/display-name
|
|
17
|
+
wrapper: ({ children }: { children: React.ReactElement }) => (
|
|
18
|
+
<FocusableGroupContext.Provider value="test">
|
|
19
|
+
{children}
|
|
20
|
+
</FocusableGroupContext.Provider>
|
|
21
|
+
),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
expect(getByTestId("test")).toBeTruthy();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FocusableGroupContext } from "./FocusableGroupContext";
|
|
3
|
+
import { FocusableGroupContextProvider } from "./FocusableGroupContextProvider";
|
|
4
|
+
|
|
5
|
+
export { FocusableGroupContext, FocusableGroupContextProvider };
|
|
6
|
+
|
|
7
|
+
export const useGroupId = () => React.useContext(FocusableGroupContext);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { FocusableGroupContext } from "./FocusableGroupContext";
|
|
3
|
+
|
|
4
|
+
export const withFocusableContext = (Component) => {
|
|
5
|
+
// eslint-disable-next-line react/display-name
|
|
6
|
+
return ({ groupId, ...props }: Record<string, any>) => (
|
|
7
|
+
<FocusableGroupContext.Consumer>
|
|
8
|
+
{(groupIdContext: string) => {
|
|
9
|
+
// eslint-disable-next-line react/display-name
|
|
10
|
+
const propsGroupId = groupId || null;
|
|
11
|
+
const providedGroupId = propsGroupId || groupIdContext;
|
|
12
|
+
|
|
13
|
+
return <Component {...props} groupId={providedGroupId} />;
|
|
14
|
+
}}
|
|
15
|
+
</FocusableGroupContext.Consumer>
|
|
16
|
+
);
|
|
17
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
3
|
+
|
|
4
|
+
export const ScreenDataContext = React.createContext<any>(null);
|
|
5
|
+
|
|
6
|
+
// Use this only to provide initial screenData context, before the Transitioner and Screen is rendered.
|
|
7
|
+
export const withScreenDataContextProvider = (Component: any) => {
|
|
8
|
+
return function ScreenDataContextProvider(props: any) {
|
|
9
|
+
const { activeRiver } = useNavigation();
|
|
10
|
+
|
|
11
|
+
// use only initial activeRiver as this context should only be used for "Home", initial route
|
|
12
|
+
const screenData = React.useMemo(() => ({ screen: activeRiver }), []);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ScreenDataContext.Provider value={screenData}>
|
|
16
|
+
<Component {...props} />
|
|
17
|
+
</ScreenDataContext.Provider>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
};
|
|
@@ -5,18 +5,18 @@ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
|
5
5
|
|
|
6
6
|
type ComponentProps = {
|
|
7
7
|
component: {
|
|
8
|
-
id: string
|
|
9
|
-
rules: { item_limit: number
|
|
10
|
-
component_type: string
|
|
8
|
+
id: string;
|
|
9
|
+
rules: { item_limit: number; component_cells_selectable: boolean };
|
|
10
|
+
component_type: string;
|
|
11
11
|
styles: {
|
|
12
|
-
cell_style: string
|
|
13
|
-
header: { visible: boolean }
|
|
14
|
-
component_cells_selectable: boolean
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
zappPipesData: { url: string
|
|
18
|
-
selected: boolean
|
|
19
|
-
assets: { menu_button: string }
|
|
12
|
+
cell_style: string;
|
|
13
|
+
header: { visible: boolean };
|
|
14
|
+
component_cells_selectable: boolean;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
zappPipesData: { url: string; data: { title: string } };
|
|
18
|
+
selected: boolean;
|
|
19
|
+
assets: { menu_button: string };
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
function withAnalytics(Component) {
|
package/README.md
CHANGED
|
@@ -78,17 +78,6 @@ export default MyAwesomeComponent;
|
|
|
78
78
|
export { MyAwesomeComponent } from "./MyAwesomeComponent";
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
We provide flow types for component props - please use it to make sure your component will be compliant
|
|
82
|
-
|
|
83
|
-
```javascript
|
|
84
|
-
import * as React from "react";
|
|
85
|
-
import type { ZappComponentProps } from "../../flow-types"; // in the root of the project
|
|
86
|
-
|
|
87
|
-
export class MyComponent extends React.Component<ZappComponentProps> {
|
|
88
|
-
/* ... */
|
|
89
|
-
}
|
|
90
|
-
```
|
|
91
|
-
|
|
92
81
|
## Connecting my component to Zapp Pipes
|
|
93
82
|
|
|
94
83
|
Zapp App components rely on [Zapp-Pipes](https://github.com/applicaster/zapp-pipes) to load data.
|
|
@@ -100,10 +89,9 @@ In order to connect your component to ZappPipes, you simply need to use the `Zap
|
|
|
100
89
|
import * as React from "react";
|
|
101
90
|
import { View, Text } from "react-native";
|
|
102
91
|
import { zappPipesDataConnector } from "../ZappPipesDataConnector";
|
|
103
|
-
import type { ZappComponentProps } from "../../flow-types";
|
|
104
92
|
|
|
105
93
|
@zappPipesDataConnector
|
|
106
|
-
export class MyComponent extends React.Component
|
|
94
|
+
export class MyComponent extends React.Component {
|
|
107
95
|
renderComponent = (data) => {
|
|
108
96
|
return (
|
|
109
97
|
<View>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "12.0.0-alpha.
|
|
3
|
+
"version": "12.0.0-alpha.4354608902",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"redux-mock-store": "^1.5.3"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@applicaster/applicaster-types": "12.0.0-alpha.
|
|
38
|
-
"@applicaster/zapp-react-native-bridge": "12.0.0-alpha.
|
|
39
|
-
"@applicaster/zapp-react-native-redux": "12.0.0-alpha.
|
|
40
|
-
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.
|
|
37
|
+
"@applicaster/applicaster-types": "12.0.0-alpha.4354608902",
|
|
38
|
+
"@applicaster/zapp-react-native-bridge": "12.0.0-alpha.4354608902",
|
|
39
|
+
"@applicaster/zapp-react-native-redux": "12.0.0-alpha.4354608902",
|
|
40
|
+
"@applicaster/zapp-react-native-utils": "12.0.0-alpha.4354608902",
|
|
41
41
|
"promise": "^8.3.0",
|
|
42
42
|
"react-router-native": "^5.1.2",
|
|
43
43
|
"url": "^0.11.0",
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import * as React from "react";
|
|
2
|
-
import { View, StyleSheet } from "react-native";
|
|
3
|
-
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
4
|
-
import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
5
|
-
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
6
|
-
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
7
|
-
import { Spinner } from "@applicaster/zapp-react-native-ui-components/Components/Spinner";
|
|
8
|
-
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
9
|
-
|
|
10
|
-
import { useScreenConfiguration } from "../../River/useScreenConfiguration";
|
|
11
|
-
|
|
12
|
-
const styles = StyleSheet.create({
|
|
13
|
-
container: {
|
|
14
|
-
position: "absolute",
|
|
15
|
-
top: 0,
|
|
16
|
-
left: 0,
|
|
17
|
-
right: 0,
|
|
18
|
-
bottom: 0,
|
|
19
|
-
|
|
20
|
-
alignItems: "center",
|
|
21
|
-
justifyContent: "center",
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
export const TransitionOverlay = () => {
|
|
26
|
-
const screen = useCurrentScreenData();
|
|
27
|
-
|
|
28
|
-
const { backgroundColor: screenBackgroundColor } = useScreenConfiguration(
|
|
29
|
-
screen?.id
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
const theme = useTheme();
|
|
33
|
-
|
|
34
|
-
const themeBackgroundColor = React.useMemo(
|
|
35
|
-
() => theme.app_background_color,
|
|
36
|
-
[theme.app_background_color]
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const backgroundColor = ifEmptyUseFallback(
|
|
40
|
-
screenBackgroundColor,
|
|
41
|
-
themeBackgroundColor
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
const {
|
|
45
|
-
appState: { isTransitioning },
|
|
46
|
-
} = usePickFromState(["appState"]);
|
|
47
|
-
|
|
48
|
-
if (!isWeb() || !isTransitioning) {
|
|
49
|
-
return null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return (
|
|
53
|
-
<View
|
|
54
|
-
style={[
|
|
55
|
-
styles.container,
|
|
56
|
-
{
|
|
57
|
-
backgroundColor,
|
|
58
|
-
},
|
|
59
|
-
]}
|
|
60
|
-
>
|
|
61
|
-
<Spinner size={"large"} />
|
|
62
|
-
</View>
|
|
63
|
-
);
|
|
64
|
-
};
|
|
File without changes
|
|
File without changes
|