@applicaster/zapp-react-native-ui-components 12.0.0-alpha.5471145113 → 12.0.0-alpha.5521005225
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 +2 -2
- package/Components/Cell/Cell.tsx +26 -5
- package/Components/Cell/CellWithFocusable.tsx +6 -4
- package/Components/Cell/TvOSCellComponent.tsx +3 -7
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableGroup/index.tsx +10 -2
- package/Components/Layout/TV/ScreenContainer.tsx +79 -5
- 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__/index.test.tsx +2 -1
- package/Components/Layout/TV/index.tsx +11 -65
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -1
- 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.tsx +6 -1
- package/Components/MasterCell/dataAdapter.ts +21 -7
- package/Components/MasterCell/index.tsx +3 -1
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +5 -1
- package/Components/Navigator/StackNavigator.tsx +14 -5
- package/Components/PlayerContainer/PlayerContainer.tsx +2 -5
- package/Components/Screen/TV/index.web.tsx +9 -23
- package/Components/Screen/index.tsx +27 -58
- package/Components/Screen/navigationHandler.ts +5 -0
- package/Components/ScreenResolver/index.tsx +6 -11
- package/Components/Transitioner/Scene.tsx +12 -11
- package/Components/Transitioner/Transitioner.tsx +17 -15
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +8 -4
- package/Components/VideoLive/LiveImageManager.ts +10 -0
- package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +26 -24
- 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 +2 -2
- 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/ScreenContext/index.tsx +0 -6
- package/package.json +5 -5
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
|
@@ -71,9 +71,9 @@ export class BaseFocusable<
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
getId(props?: { id: string; groupId: string }) {
|
|
74
|
-
const { id
|
|
74
|
+
const { id } = props || this.props;
|
|
75
75
|
|
|
76
|
-
return
|
|
76
|
+
return id;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
getRect() {
|
package/Components/Cell/Cell.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { join,
|
|
2
|
+
import { join, merge, path } from "ramda";
|
|
3
3
|
import { View } from "react-native";
|
|
4
4
|
|
|
5
5
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
@@ -11,6 +11,7 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
|
11
11
|
|
|
12
12
|
import { CellWithFocusable } from "./CellWithFocusable";
|
|
13
13
|
import { BaseFocusable } from "../BaseFocusable";
|
|
14
|
+
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
14
15
|
|
|
15
16
|
type Props = {
|
|
16
17
|
item: {
|
|
@@ -66,6 +67,10 @@ type Props = {
|
|
|
66
67
|
behavior: Behavior;
|
|
67
68
|
};
|
|
68
69
|
|
|
70
|
+
type State = {
|
|
71
|
+
hasFocusableInside: boolean;
|
|
72
|
+
};
|
|
73
|
+
|
|
69
74
|
const baseCellStyles = {
|
|
70
75
|
height: "100%",
|
|
71
76
|
display: "flex",
|
|
@@ -77,7 +82,7 @@ const touchableStyles = {
|
|
|
77
82
|
height: "100%",
|
|
78
83
|
} as const;
|
|
79
84
|
|
|
80
|
-
export class CellComponent extends React.Component<Props> {
|
|
85
|
+
export class CellComponent extends React.Component<Props, State> {
|
|
81
86
|
constructor(props) {
|
|
82
87
|
super(props);
|
|
83
88
|
this.onPress = this.onPress.bind(this);
|
|
@@ -87,6 +92,10 @@ export class CellComponent extends React.Component<Props> {
|
|
|
87
92
|
this.hasReceivedFocus = this.hasReceivedFocus.bind(this);
|
|
88
93
|
this.scrollVertically = this.scrollVertically.bind(this);
|
|
89
94
|
this.scrollToIndex = this.scrollToIndex.bind(this);
|
|
95
|
+
|
|
96
|
+
this.state = {
|
|
97
|
+
hasFocusableInside: props.CellRenderer.hasFocusableInside?.(props.item),
|
|
98
|
+
};
|
|
90
99
|
}
|
|
91
100
|
|
|
92
101
|
setScreenLayout(componentAnchorPointY, screenLayout) {
|
|
@@ -139,6 +148,10 @@ export class CellComponent extends React.Component<Props> {
|
|
|
139
148
|
shouldScrollVertically?.(mouse, focusable, id, title)
|
|
140
149
|
) {
|
|
141
150
|
this.scrollVertically(focusable);
|
|
151
|
+
|
|
152
|
+
const accessibilityManager = AccessibilityManager.getInstance();
|
|
153
|
+
// Text from DSP should be localized already
|
|
154
|
+
accessibilityManager.readText({ text: title });
|
|
142
155
|
}
|
|
143
156
|
}
|
|
144
157
|
}
|
|
@@ -187,6 +200,16 @@ export class CellComponent extends React.Component<Props> {
|
|
|
187
200
|
return !isFocusable ? false : focused || focusableFocused;
|
|
188
201
|
}
|
|
189
202
|
|
|
203
|
+
componentDidUpdate(prevProps: Readonly<Props>) {
|
|
204
|
+
if (prevProps.item !== this.props.item) {
|
|
205
|
+
this.setState({
|
|
206
|
+
hasFocusableInside: this.props.CellRenderer.hasFocusableInside?.(
|
|
207
|
+
this.props.item
|
|
208
|
+
),
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
190
213
|
render() {
|
|
191
214
|
const {
|
|
192
215
|
item,
|
|
@@ -215,9 +238,7 @@ export class CellComponent extends React.Component<Props> {
|
|
|
215
238
|
this.onFocus(focusable, mouse);
|
|
216
239
|
};
|
|
217
240
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (hasFocusableInside) {
|
|
241
|
+
if (this.state.hasFocusableInside) {
|
|
221
242
|
return (
|
|
222
243
|
<CellWithFocusable
|
|
223
244
|
CellRenderer={CellRenderer}
|
|
@@ -3,6 +3,8 @@ import * as React from "react";
|
|
|
3
3
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
4
4
|
import * as FOCUS_EVENTS from "@applicaster/zapp-react-native-utils/appUtils/focusManager/events";
|
|
5
5
|
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
6
|
+
import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
|
|
7
|
+
|
|
6
8
|
import { isAppleTV } from "../../Helpers/Platform";
|
|
7
9
|
import { useCellState } from "../MasterCell/utils";
|
|
8
10
|
|
|
@@ -54,7 +56,7 @@ type Props = {
|
|
|
54
56
|
skipFocusManagerRegistration?: boolean;
|
|
55
57
|
isFocusable?: boolean;
|
|
56
58
|
behavior: Behavior;
|
|
57
|
-
|
|
59
|
+
focused?: boolean;
|
|
58
60
|
};
|
|
59
61
|
|
|
60
62
|
export function CellWithFocusable(props: Props) {
|
|
@@ -70,7 +72,7 @@ export function CellWithFocusable(props: Props) {
|
|
|
70
72
|
skipFocusManagerRegistration,
|
|
71
73
|
isFocusable,
|
|
72
74
|
behavior,
|
|
73
|
-
|
|
75
|
+
focused,
|
|
74
76
|
} = props;
|
|
75
77
|
|
|
76
78
|
const isFocused = useCellFocusedState(
|
|
@@ -82,8 +84,7 @@ export function CellWithFocusable(props: Props) {
|
|
|
82
84
|
const state = useCellState({
|
|
83
85
|
id: item.id,
|
|
84
86
|
behavior,
|
|
85
|
-
focused: isFocused,
|
|
86
|
-
selected,
|
|
87
|
+
focused: isFocused || toBooleanWithDefaultFalse(focused),
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
|
|
@@ -114,6 +115,7 @@ export function CellWithFocusable(props: Props) {
|
|
|
114
115
|
preferredFocus={preferredFocus}
|
|
115
116
|
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
116
117
|
isFocusable={isFocusable}
|
|
118
|
+
focused={focused}
|
|
117
119
|
/>
|
|
118
120
|
);
|
|
119
121
|
}
|
|
@@ -226,13 +226,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
226
226
|
|
|
227
227
|
hasReceivedFocus() {}
|
|
228
228
|
|
|
229
|
-
scrollTo(
|
|
230
|
-
// Workaround for HL autoscroll/autoloop feature, we scroll manually on tvOS.
|
|
231
|
-
// Should be removed after refactoring autoscroll/autoloop feature
|
|
232
|
-
if (this.props.component?.component_type !== "horizontal_list_qb") {
|
|
233
|
-
this.props?.scrollToIndex?.(index);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
229
|
+
scrollTo() {}
|
|
236
230
|
|
|
237
231
|
render() {
|
|
238
232
|
const {
|
|
@@ -279,7 +273,9 @@ export class TvOSCellComponent extends React.Component<Props, State> {
|
|
|
279
273
|
index={index}
|
|
280
274
|
scrollTo={this.scrollTo}
|
|
281
275
|
preferredFocus={preferredFocus}
|
|
276
|
+
focused={this.props.focused}
|
|
282
277
|
behavior={behavior}
|
|
278
|
+
isFocusable={isFocusable}
|
|
283
279
|
/>
|
|
284
280
|
</View>
|
|
285
281
|
);
|
|
@@ -4,6 +4,7 @@ import * as R from "ramda";
|
|
|
4
4
|
import { BaseFocusable } from "../BaseFocusable";
|
|
5
5
|
import { focusManager } from "@applicaster/zapp-react-native-utils/appUtils/focusManager";
|
|
6
6
|
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
7
|
+
import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
|
|
7
8
|
|
|
8
9
|
type Props = {
|
|
9
10
|
initialFocus?: boolean;
|
|
@@ -23,7 +24,7 @@ type Props = {
|
|
|
23
24
|
style?: React.CSSProperties;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
class Focusable extends BaseFocusable<Props> {
|
|
27
28
|
isGroup: boolean;
|
|
28
29
|
mouse: boolean;
|
|
29
30
|
longPressTimeout = null;
|
|
@@ -198,3 +199,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
198
199
|
);
|
|
199
200
|
}
|
|
200
201
|
}
|
|
202
|
+
|
|
203
|
+
const FocusableWithFocusableContext = withFocusableContext(Focusable);
|
|
204
|
+
|
|
205
|
+
export { FocusableWithFocusableContext as Focusable };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
|
|
3
3
|
import { BaseFocusable } from "../BaseFocusable";
|
|
4
|
+
import { FocusableGroupContextProvider } from "../../Contexts/FocusableGroupContext";
|
|
5
|
+
import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withFocusableContext";
|
|
4
6
|
|
|
5
7
|
type Props = {
|
|
6
8
|
testID?: string;
|
|
@@ -29,7 +31,7 @@ type Props = {
|
|
|
29
31
|
excludeFromFocusSearching?: boolean;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
class FocusableGroup extends BaseFocusable<Props> {
|
|
33
35
|
constructor(props) {
|
|
34
36
|
super(props);
|
|
35
37
|
this.isGroup = true;
|
|
@@ -82,8 +84,14 @@ export class FocusableGroup extends BaseFocusable<Props> {
|
|
|
82
84
|
style={style}
|
|
83
85
|
ref={this.ref}
|
|
84
86
|
>
|
|
85
|
-
{
|
|
87
|
+
<FocusableGroupContextProvider id={id}>
|
|
88
|
+
{children}
|
|
89
|
+
</FocusableGroupContextProvider>
|
|
86
90
|
</div>
|
|
87
91
|
);
|
|
88
92
|
}
|
|
89
93
|
}
|
|
94
|
+
|
|
95
|
+
const FocusableGroupWithFocusableContext = withFocusableContext(FocusableGroup);
|
|
96
|
+
|
|
97
|
+
export { FocusableGroupWithFocusableContext as FocusableGroup };
|
|
@@ -5,13 +5,30 @@ import * as R from "ramda";
|
|
|
5
5
|
import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
|
|
6
6
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
7
7
|
import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
useCurrentScreenData,
|
|
10
|
+
useNavbarState,
|
|
11
|
+
} from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
9
12
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
10
13
|
|
|
11
14
|
import { useScreenConfiguration } from "../../River/useScreenConfiguration";
|
|
12
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
findMenuPlugin,
|
|
17
|
+
getNavigationProps,
|
|
18
|
+
routeIsPlayerScreen,
|
|
19
|
+
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
20
|
+
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
21
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
22
|
+
import { NavBarContainer } from "./NavBarContainer";
|
|
23
|
+
|
|
24
|
+
type ComponentsExtraProps = {
|
|
25
|
+
Background?: Record<string, any>;
|
|
26
|
+
NavBar?: Record<string, any>;
|
|
27
|
+
};
|
|
13
28
|
|
|
14
29
|
type Props = {
|
|
30
|
+
ComponentsExtraProps?: ComponentsExtraProps;
|
|
31
|
+
NavBar: React.ComponentType<any>;
|
|
15
32
|
children: React.ReactNode;
|
|
16
33
|
};
|
|
17
34
|
|
|
@@ -30,9 +47,13 @@ const styles = StyleSheet.create({
|
|
|
30
47
|
container: { flex: 1, height: "100%", width: "100%" },
|
|
31
48
|
});
|
|
32
49
|
|
|
50
|
+
const isTVOS = isApplePlatform();
|
|
51
|
+
|
|
33
52
|
// eslint-disable-next-line react/display-name
|
|
34
53
|
export const ScreenContainer = React.memo(function ScreenContainer({
|
|
35
54
|
children,
|
|
55
|
+
ComponentsExtraProps,
|
|
56
|
+
NavBar,
|
|
36
57
|
}: Props) {
|
|
37
58
|
const { screenMarginBottom } = React.useContext(ScreenLayoutContext);
|
|
38
59
|
const { currentRoute, screenData } = useNavigation();
|
|
@@ -80,9 +101,62 @@ export const ScreenContainer = React.memo(function ScreenContainer({
|
|
|
80
101
|
[themeStyles]
|
|
81
102
|
);
|
|
82
103
|
|
|
104
|
+
const navigator = useNavigation();
|
|
105
|
+
const { activeRiver } = navigator;
|
|
106
|
+
const { title, visible } = useNavbarState();
|
|
107
|
+
|
|
108
|
+
const { plugins = [] } = usePickFromState([
|
|
109
|
+
"appState",
|
|
110
|
+
"remoteConfigurations",
|
|
111
|
+
"plugins",
|
|
112
|
+
]);
|
|
113
|
+
|
|
114
|
+
const navigationProps = getNavigationProps({
|
|
115
|
+
navigator,
|
|
116
|
+
title,
|
|
117
|
+
category: "menu",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const menuPlugin = React.useMemo(
|
|
121
|
+
() => findMenuPlugin(activeRiver?.navigations, plugins),
|
|
122
|
+
[activeRiver?.navigations]
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const NavBarComponent = React.useMemo(
|
|
126
|
+
() => NavBar || (menuPlugin.module as React.ComponentType<any>),
|
|
127
|
+
[]
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const [navBarReady, setNavBarReady] = React.useState(false);
|
|
131
|
+
|
|
132
|
+
const navBarContainer = (
|
|
133
|
+
<NavBarContainer isVisible={visible} onReady={setNavBarReady}>
|
|
134
|
+
<NavBarComponent
|
|
135
|
+
ref={ComponentsExtraProps?.NavBar?.ref}
|
|
136
|
+
isVisible={visible}
|
|
137
|
+
menuPlugin={menuPlugin}
|
|
138
|
+
navigationProps={navigationProps}
|
|
139
|
+
{...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
|
|
140
|
+
/>
|
|
141
|
+
</NavBarContainer>
|
|
142
|
+
);
|
|
143
|
+
|
|
83
144
|
return (
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
|
|
145
|
+
<>
|
|
146
|
+
{!isTVOS ? navBarContainer : null}
|
|
147
|
+
{navBarReady ? (
|
|
148
|
+
/**
|
|
149
|
+
Need to re-mount this component on each navigation event
|
|
150
|
+
It's a hack to work around the fact that we don't support
|
|
151
|
+
correct screen rendering in our navigator (shared currentRoute state)
|
|
152
|
+
**/
|
|
153
|
+
<View testID="screen-container" style={viewStyles}>
|
|
154
|
+
{children}
|
|
155
|
+
</View>
|
|
156
|
+
) : null}
|
|
157
|
+
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
158
|
+
focus manager works correctly to focus nav bar items first **/}
|
|
159
|
+
{isTVOS ? navBarContainer : null}
|
|
160
|
+
</>
|
|
87
161
|
);
|
|
88
162
|
});
|
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { View } from "react-native";
|
|
3
|
-
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { render, waitFor } from "@testing-library/react-native";
|
|
4
|
+
import { WrappedWithProviders } from "@applicaster/zapp-react-native-utils/testUtils";
|
|
4
5
|
|
|
5
6
|
import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
|
|
6
7
|
|
|
7
8
|
const navigator_mock = {
|
|
8
9
|
currentRoute: "/river/A1234",
|
|
9
10
|
screenData: {},
|
|
11
|
+
activeRiver: {
|
|
12
|
+
navigations: {},
|
|
13
|
+
id: "A1234",
|
|
14
|
+
home: false,
|
|
15
|
+
},
|
|
10
16
|
};
|
|
11
17
|
|
|
18
|
+
jest.mock("@applicaster/zapp-react-native-utils/navigationUtils", () => ({
|
|
19
|
+
...jest.requireActual<any>(
|
|
20
|
+
"@applicaster/zapp-react-native-utils/navigationUtils"
|
|
21
|
+
),
|
|
22
|
+
findMenuPlugin: () => ({}),
|
|
23
|
+
}));
|
|
24
|
+
|
|
12
25
|
jest.mock("@applicaster/quick-brick-core/App/ActionSetters", () => ({
|
|
13
26
|
useRiverInitialState: () => ({}),
|
|
14
27
|
}));
|
|
@@ -17,13 +30,13 @@ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
|
17
30
|
useNavigation: () => navigator_mock,
|
|
18
31
|
}));
|
|
19
32
|
|
|
20
|
-
jest.mock("../../../River/useScreenConfiguration
|
|
33
|
+
jest.mock("../../../River/useScreenConfiguration", () => ({
|
|
21
34
|
useScreenConfiguration: () => ({}),
|
|
22
35
|
}));
|
|
23
36
|
|
|
24
|
-
jest.mock(
|
|
25
|
-
useCurrentScreenData
|
|
26
|
-
|
|
37
|
+
jest.mock(
|
|
38
|
+
"@applicaster/zapp-react-native-utils/reactHooks/screen/useCurrentScreenData"
|
|
39
|
+
);
|
|
27
40
|
|
|
28
41
|
jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
|
|
29
42
|
useTheme: () => ({}),
|
|
@@ -44,42 +57,39 @@ describe("ScreenContainer", () => {
|
|
|
44
57
|
const setScreenLayout = () => {};
|
|
45
58
|
|
|
46
59
|
const wrapper = ({ children }) => (
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
60
|
+
<WrappedWithProviders>
|
|
61
|
+
<ScreenLayoutContext.Provider
|
|
62
|
+
value={{ ...screenLayout, setScreenLayout }}
|
|
63
|
+
>
|
|
64
|
+
<ScreenContainer NavBar={View}>{children}</ScreenContainer>
|
|
65
|
+
</ScreenLayoutContext.Provider>
|
|
66
|
+
</WrappedWithProviders>
|
|
50
67
|
);
|
|
51
68
|
|
|
52
69
|
const TestComp = () => <View testID="test-component" />;
|
|
53
70
|
|
|
54
71
|
it("renders", () => {
|
|
55
|
-
const { toJSON } = render(
|
|
56
|
-
<ScreenContainer>
|
|
57
|
-
<TestComp />
|
|
58
|
-
</ScreenContainer>,
|
|
59
|
-
{ wrapper }
|
|
60
|
-
);
|
|
72
|
+
const { toJSON } = render(<TestComp />, { wrapper });
|
|
61
73
|
|
|
62
74
|
expect(toJSON()).toMatchSnapshot();
|
|
63
75
|
});
|
|
64
76
|
|
|
65
|
-
it("renders passed component", () => {
|
|
66
|
-
const { getByTestId } = render(
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
);
|
|
77
|
+
it("renders passed component", async () => {
|
|
78
|
+
const { getByTestId } = render(<TestComp />, { wrapper });
|
|
79
|
+
|
|
80
|
+
await waitFor(() => {
|
|
81
|
+
return expect(getByTestId("screen-container")).toBeDefined();
|
|
82
|
+
});
|
|
72
83
|
|
|
73
84
|
expect(getByTestId("test-component")).toBeDefined();
|
|
74
85
|
});
|
|
75
86
|
|
|
76
|
-
it("sets margins provided by the theme", () => {
|
|
77
|
-
const { getByTestId } = render(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
87
|
+
it("sets margins provided by the theme", async () => {
|
|
88
|
+
const { getByTestId } = render(<TestComp />, { wrapper });
|
|
89
|
+
|
|
90
|
+
await waitFor(() => {
|
|
91
|
+
return expect(getByTestId("screen-container")).toBeDefined();
|
|
92
|
+
});
|
|
83
93
|
|
|
84
94
|
const screen = getByTestId("screen-container");
|
|
85
95
|
expect(screen.props.style).toMatchSnapshot();
|
|
@@ -1,28 +1,40 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
3
|
exports[`ScreenContainer renders 1`] = `
|
|
4
|
-
<
|
|
4
|
+
<RNCSafeAreaProvider
|
|
5
|
+
onInsetsChange={[Function]}
|
|
5
6
|
style={
|
|
6
7
|
Array [
|
|
7
8
|
Object {
|
|
8
9
|
"flex": 1,
|
|
9
|
-
"height": "100%",
|
|
10
|
-
"width": "100%",
|
|
11
|
-
},
|
|
12
|
-
Object {
|
|
13
|
-
"backgroundColor": "transparent",
|
|
14
|
-
"marginBottom": 10,
|
|
15
|
-
"paddingBottom": 0,
|
|
16
|
-
"paddingTop": 0,
|
|
17
10
|
},
|
|
11
|
+
undefined,
|
|
18
12
|
]
|
|
19
13
|
}
|
|
20
|
-
testID="screen-container"
|
|
21
14
|
>
|
|
22
15
|
<View
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
16
|
+
style={
|
|
17
|
+
Array [
|
|
18
|
+
Object {
|
|
19
|
+
"position": "absolute",
|
|
20
|
+
"top": 0,
|
|
21
|
+
"width": 1920,
|
|
22
|
+
"zIndex": 10,
|
|
23
|
+
},
|
|
24
|
+
Object {
|
|
25
|
+
"maxHeight": 10,
|
|
26
|
+
},
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
testID="nav-bar-container"
|
|
30
|
+
>
|
|
31
|
+
<View
|
|
32
|
+
isVisible={true}
|
|
33
|
+
menuPlugin={Object {}}
|
|
34
|
+
navigationProps={null}
|
|
35
|
+
/>
|
|
36
|
+
</View>
|
|
37
|
+
</RNCSafeAreaProvider>
|
|
26
38
|
`;
|
|
27
39
|
|
|
28
40
|
exports[`ScreenContainer sets margins provided by the theme 1`] = `
|
|
@@ -21,10 +21,11 @@ const mockStore = configureStore()({
|
|
|
21
21
|
describe("Layout TV", () => {
|
|
22
22
|
const navigator = {
|
|
23
23
|
currentRoute: "/river/A1234",
|
|
24
|
+
canGoBack: jest.fn(),
|
|
24
25
|
mainStack: [
|
|
25
26
|
{
|
|
26
27
|
route: "/river/A1234",
|
|
27
|
-
state:
|
|
28
|
+
state: { screen: {} },
|
|
28
29
|
},
|
|
29
30
|
],
|
|
30
31
|
};
|
|
@@ -4,22 +4,14 @@ import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
|
4
4
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
5
5
|
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
getNavigationProps,
|
|
9
|
-
resolveNavigationPlugin,
|
|
10
|
-
routeIsPlayerScreen,
|
|
11
|
-
} from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
12
|
-
|
|
13
7
|
import { getBackgroundImageUrl } from "../utils";
|
|
14
8
|
import { LayoutContainer } from "./LayoutContainer";
|
|
15
|
-
import { NavBarContainer } from "./NavBarContainer";
|
|
16
9
|
import { ScreenContainer } from "./ScreenContainer";
|
|
17
10
|
|
|
18
11
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
19
|
-
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
20
12
|
import { PathnameContext } from "../../../Contexts/PathnameContext";
|
|
21
|
-
import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
22
13
|
import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
|
|
14
|
+
import { ScreenContextProvider } from "../../../Contexts/ScreenContext";
|
|
23
15
|
|
|
24
16
|
type Components = {
|
|
25
17
|
NavBar: React.ComponentType<any>;
|
|
@@ -40,20 +32,9 @@ type Props = {
|
|
|
40
32
|
const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
41
33
|
const navigator = useNavigation();
|
|
42
34
|
const theme = useTheme();
|
|
43
|
-
const { title, visible } = useNavbarState();
|
|
44
|
-
const { activeRiver, currentRoute } = navigator;
|
|
45
|
-
|
|
46
|
-
const {
|
|
47
|
-
appState: { appReady = false } = {},
|
|
48
|
-
remoteConfigurations,
|
|
49
|
-
plugins = [],
|
|
50
|
-
} = usePickFromState(["appState", "remoteConfigurations", "plugins"]);
|
|
51
35
|
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
title,
|
|
55
|
-
category: "menu",
|
|
56
|
-
});
|
|
36
|
+
const { appState: { appReady = false } = {}, remoteConfigurations } =
|
|
37
|
+
usePickFromState(["appState", "remoteConfigurations", "plugins"]);
|
|
57
38
|
|
|
58
39
|
const backgroundColor = React.useMemo(() => theme.app_background_color, []);
|
|
59
40
|
|
|
@@ -62,37 +43,6 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
62
43
|
[remoteConfigurations]
|
|
63
44
|
);
|
|
64
45
|
|
|
65
|
-
const menuPlugin = React.useMemo(
|
|
66
|
-
() =>
|
|
67
|
-
resolveNavigationPlugin({
|
|
68
|
-
category: "menu",
|
|
69
|
-
navigations: activeRiver?.navigations,
|
|
70
|
-
plugins,
|
|
71
|
-
}) as QuickBrickPlugin,
|
|
72
|
-
[]
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const NavBar = React.useMemo(
|
|
76
|
-
() => Components.NavBar || (menuPlugin.module as React.ComponentType<any>),
|
|
77
|
-
[]
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const [navBarReady, setNavBarReady] = React.useState(false);
|
|
81
|
-
|
|
82
|
-
const navBarContainer = (
|
|
83
|
-
<NavBarContainer isVisible={visible} onReady={setNavBarReady}>
|
|
84
|
-
<NavBar
|
|
85
|
-
ref={ComponentsExtraProps?.NavBar?.ref}
|
|
86
|
-
isVisible={visible && !routeIsPlayerScreen(currentRoute)}
|
|
87
|
-
menuPlugin={menuPlugin}
|
|
88
|
-
navigationProps={navigationProps}
|
|
89
|
-
{...R.omit(["ref"])(ComponentsExtraProps?.NavBar)}
|
|
90
|
-
/>
|
|
91
|
-
</NavBarContainer>
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const isTVOS = isApplePlatform();
|
|
95
|
-
|
|
96
46
|
if (!appReady) {
|
|
97
47
|
return null;
|
|
98
48
|
}
|
|
@@ -107,18 +57,14 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
107
57
|
>
|
|
108
58
|
<ScreenDataContext.Provider value={navigator.data}>
|
|
109
59
|
<PathnameContext.Provider value={navigator.currentRoute}>
|
|
110
|
-
{
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
) : null}
|
|
119
|
-
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
120
|
-
focus manager works correctly to focus nav bar items first **/}
|
|
121
|
-
{isTVOS ? navBarContainer : null}
|
|
60
|
+
<ScreenContextProvider pathname={navigator.currentRoute}>
|
|
61
|
+
<ScreenContainer
|
|
62
|
+
ComponentsExtraProps={ComponentsExtraProps}
|
|
63
|
+
NavBar={Components.NavBar}
|
|
64
|
+
>
|
|
65
|
+
{children}
|
|
66
|
+
</ScreenContainer>
|
|
67
|
+
</ScreenContextProvider>
|
|
122
68
|
</PathnameContext.Provider>
|
|
123
69
|
</ScreenDataContext.Provider>
|
|
124
70
|
</Components.Background>
|
|
@@ -53,6 +53,7 @@ export function ActionButton(props: Props) {
|
|
|
53
53
|
const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
|
|
54
54
|
const actionContext = useActions(action?.identifier);
|
|
55
55
|
|
|
56
|
+
// TODO: add subscription API for action availability
|
|
56
57
|
const actionDisabled =
|
|
57
58
|
typeof actionContext?.isActionAvailable === "function" &&
|
|
58
59
|
!actionContext.isActionAvailable(item);
|
|
@@ -68,7 +69,7 @@ export function ActionButton(props: Props) {
|
|
|
68
69
|
if (actionDisabled || !actionContext) return;
|
|
69
70
|
|
|
70
71
|
setActionState(actionContext.initialEntryState(item));
|
|
71
|
-
}, [actionDisabled, item?.id]);
|
|
72
|
+
}, [actionDisabled, item?.id, actionContext]);
|
|
72
73
|
|
|
73
74
|
const onPress = useCallback(() => {
|
|
74
75
|
actionContext.invokeAction(item, {
|
|
@@ -22,6 +22,10 @@ const styles = StyleSheet.create({
|
|
|
22
22
|
borderWidth: 0,
|
|
23
23
|
borderColor: COLOR_TRANSPARENT,
|
|
24
24
|
},
|
|
25
|
+
border: {
|
|
26
|
+
...StyleSheet.absoluteFillObject,
|
|
27
|
+
zIndex: 1,
|
|
28
|
+
},
|
|
25
29
|
});
|
|
26
30
|
|
|
27
31
|
const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
|
|
@@ -87,8 +91,9 @@ const Border = ({
|
|
|
87
91
|
|
|
88
92
|
return (
|
|
89
93
|
<View
|
|
94
|
+
pointerEvents="none"
|
|
90
95
|
style={{
|
|
91
|
-
...
|
|
96
|
+
...styles.border,
|
|
92
97
|
right: -containerPadding - paddingRight,
|
|
93
98
|
bottom: -containerPadding - paddingBottom,
|
|
94
99
|
left: -containerPadding - paddingLeft,
|
|
@@ -130,8 +135,8 @@ export const BorderContainerView = (props: Props) => {
|
|
|
130
135
|
{...props}
|
|
131
136
|
style={[style, styles.container, borderStyleForMissingBorder]}
|
|
132
137
|
>
|
|
133
|
-
{children}
|
|
134
138
|
<Border style={style} padding={padding} borderPosition={borderPosition} />
|
|
139
|
+
{children}
|
|
135
140
|
</View>
|
|
136
141
|
);
|
|
137
142
|
};
|