@applicaster/zapp-react-native-ui-components 12.0.0-alpha.7496053101 → 12.0.0-alpha.7528906949
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 +5 -5
- package/Components/Cell/CellWithFocusable.tsx +23 -9
- package/Components/Cell/TvOSCellComponent.tsx +3 -7
- package/Components/Focusable/Focusable.tsx +6 -1
- package/Components/FocusableGroup/index.tsx +11 -2
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
- package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
- package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
- package/Components/Layout/TV/__tests__/index.test.tsx +19 -75
- package/Components/Layout/TV/index.tsx +12 -11
- package/Components/Layout/TV/index.web.tsx +33 -0
- package/Components/Layout/index.tsx +15 -11
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +2 -0
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +88 -112
- package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -1
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
- package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
- package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
- package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
- package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
- package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
- package/Components/MasterCell/utils/index.ts +33 -35
- package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
- package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
- package/Components/Navigator/StackNavigator.tsx +54 -0
- package/Components/Navigator/index.ts +1 -0
- package/Components/PlayerContainer/PlayerContainer.tsx +23 -3
- package/Components/PlayerImageBackground/index.tsx +34 -8
- package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
- package/Components/River/RefreshControl.tsx +55 -1
- package/Components/River/TV/River.tsx +0 -23
- package/Components/River/__tests__/componentsMap.test.js +6 -0
- package/Components/RouteManager/index.tsx +2 -2
- 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 +186 -0
- package/Components/Screen/__tests__/Screen.test.tsx +3 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -0
- package/Components/Screen/index.tsx +65 -26
- package/Components/ScreenResolver/index.tsx +3 -1
- package/Components/Transitioner/Scene.tsx +24 -19
- package/Components/Transitioner/Transitioner.tsx +21 -28
- package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +6 -2
- package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +1 -0
- package/Components/VideoLive/LiveImageManager.ts +28 -1
- package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +20 -5
- package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
- package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +78 -0
- package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +352 -0
- package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +158 -0
- package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +455 -0
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +162 -0
- package/Components/VideoModal/ModalAnimation/index.ts +15 -0
- package/Components/VideoModal/ModalAnimation/utils.ts +301 -0
- package/Components/VideoModal/PlayerDetails.tsx +3 -3
- package/Components/VideoModal/PlayerWrapper.tsx +77 -47
- package/Components/VideoModal/VideoModal.tsx +23 -40
- package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
- package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +157 -105
- package/Components/VideoModal/utils.ts +8 -5
- package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
- package/Components/Viewport/ViewportTracker/index.tsx +1 -0
- package/Components/Viewport/utils.js +1 -1
- 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/MenuToggleContext/{index.js → index.ts} +16 -0
- package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
- package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
- package/Contexts/ScreenContext/index.tsx +94 -25
- package/Contexts/ScreenDataContext/index.tsx +20 -0
- package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
- package/events/index.ts +1 -0
- package/package.json +5 -5
- package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
- package/Components/VideoModal/hooks/usePosition.ts +0 -50
- /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
- /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
- /package/Contexts/{index.js → index.ts} +0 -0
|
@@ -66,14 +66,14 @@ export class BaseFocusable<
|
|
|
66
66
|
const { initialFocus } = this.props;
|
|
67
67
|
|
|
68
68
|
if (initialFocus) {
|
|
69
|
-
focusManager.
|
|
69
|
+
focusManager.setFocus(this.getId());
|
|
70
70
|
}
|
|
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
|
|
2
|
+
import { join, path, merge } from "ramda";
|
|
3
3
|
import { View } from "react-native";
|
|
4
4
|
|
|
5
5
|
import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
|
|
@@ -105,8 +105,8 @@ export class CellComponent extends React.Component<Props> {
|
|
|
105
105
|
onPress(item, index);
|
|
106
106
|
} else {
|
|
107
107
|
const { item, navigator, component, appData } = this.props;
|
|
108
|
-
const itemTargetScreen =
|
|
109
|
-
const componentTargetScreen =
|
|
108
|
+
const itemTargetScreen = path(["screen_type"], item);
|
|
109
|
+
const componentTargetScreen = path(["data", "target"], component);
|
|
110
110
|
const itemType = getItemType(item, appData?.layoutVersion);
|
|
111
111
|
|
|
112
112
|
sendSelectCellEvent(item, component, item.title, index);
|
|
@@ -116,7 +116,7 @@ export class CellComponent extends React.Component<Props> {
|
|
|
116
116
|
componentTargetScreen &&
|
|
117
117
|
!itemTargetScreen
|
|
118
118
|
) {
|
|
119
|
-
navigator.push(
|
|
119
|
+
navigator.push(merge(item, { screen_type: componentTargetScreen }));
|
|
120
120
|
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
@@ -207,7 +207,7 @@ export class CellComponent extends React.Component<Props> {
|
|
|
207
207
|
|
|
208
208
|
const { id } = item;
|
|
209
209
|
|
|
210
|
-
const focusableId =
|
|
210
|
+
const focusableId = join("-", [component?.id, id, index]);
|
|
211
211
|
|
|
212
212
|
const handleFocus = (focusable, mouse) => {
|
|
213
213
|
const focusFn = onFocus || noop;
|
|
@@ -3,15 +3,25 @@ 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
|
|
|
9
|
-
const useCellFocusedState = (
|
|
11
|
+
const useCellFocusedState = (
|
|
12
|
+
skipFocusManagerRegistration: boolean,
|
|
13
|
+
groupId: string,
|
|
14
|
+
id: string
|
|
15
|
+
) => {
|
|
10
16
|
const [currentCellFocused, setCurrentCellFocused] = React.useState(false);
|
|
11
17
|
|
|
12
18
|
React.useEffect(() => {
|
|
13
|
-
const isGroupItemFocused = () =>
|
|
14
|
-
|
|
19
|
+
const isGroupItemFocused = () => {
|
|
20
|
+
if (!skipFocusManagerRegistration) {
|
|
21
|
+
const isFocused = focusManager.isGroupItemFocused(groupId, id);
|
|
22
|
+
setCurrentCellFocused(isFocused);
|
|
23
|
+
}
|
|
24
|
+
};
|
|
15
25
|
|
|
16
26
|
const handler = () => {
|
|
17
27
|
// tvOS hack for properly checking focus
|
|
@@ -29,7 +39,7 @@ const useCellFocusedState = (groupId: string, id: string) => {
|
|
|
29
39
|
return () => {
|
|
30
40
|
focusManager.removeHandler(FOCUS_EVENTS.FOCUS, handler);
|
|
31
41
|
};
|
|
32
|
-
}, [groupId]);
|
|
42
|
+
}, [groupId, skipFocusManagerRegistration]);
|
|
33
43
|
|
|
34
44
|
return currentCellFocused;
|
|
35
45
|
};
|
|
@@ -46,7 +56,7 @@ type Props = {
|
|
|
46
56
|
skipFocusManagerRegistration?: boolean;
|
|
47
57
|
isFocusable?: boolean;
|
|
48
58
|
behavior: Behavior;
|
|
49
|
-
|
|
59
|
+
focused?: boolean;
|
|
50
60
|
};
|
|
51
61
|
|
|
52
62
|
export function CellWithFocusable(props: Props) {
|
|
@@ -62,16 +72,19 @@ export function CellWithFocusable(props: Props) {
|
|
|
62
72
|
skipFocusManagerRegistration,
|
|
63
73
|
isFocusable,
|
|
64
74
|
behavior,
|
|
65
|
-
|
|
75
|
+
focused,
|
|
66
76
|
} = props;
|
|
67
77
|
|
|
68
|
-
const isFocused = useCellFocusedState(
|
|
78
|
+
const isFocused = useCellFocusedState(
|
|
79
|
+
skipFocusManagerRegistration,
|
|
80
|
+
groupId,
|
|
81
|
+
id
|
|
82
|
+
);
|
|
69
83
|
|
|
70
84
|
const state = useCellState({
|
|
71
85
|
id: item.id,
|
|
72
86
|
behavior,
|
|
73
|
-
focused: isFocused,
|
|
74
|
-
selected,
|
|
87
|
+
focused: isFocused || toBooleanWithDefaultFalse(focused),
|
|
75
88
|
});
|
|
76
89
|
|
|
77
90
|
const [focusedButtonId, setFocusedButtonId] = React.useState(undefined);
|
|
@@ -102,6 +115,7 @@ export function CellWithFocusable(props: Props) {
|
|
|
102
115
|
preferredFocus={preferredFocus}
|
|
103
116
|
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
104
117
|
isFocusable={isFocusable}
|
|
118
|
+
focused={focused}
|
|
105
119
|
/>
|
|
106
120
|
);
|
|
107
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;
|
|
@@ -26,9 +28,10 @@ type Props = {
|
|
|
26
28
|
nextFocusLeft?: any;
|
|
27
29
|
nextFocusRight?: any;
|
|
28
30
|
onLayout?: any;
|
|
31
|
+
excludeFromFocusSearching?: boolean;
|
|
29
32
|
};
|
|
30
33
|
|
|
31
|
-
|
|
34
|
+
class FocusableGroup extends BaseFocusable<Props> {
|
|
32
35
|
constructor(props) {
|
|
33
36
|
super(props);
|
|
34
37
|
this.isGroup = true;
|
|
@@ -81,8 +84,14 @@ export class FocusableGroup extends BaseFocusable<Props> {
|
|
|
81
84
|
style={style}
|
|
82
85
|
ref={this.ref}
|
|
83
86
|
>
|
|
84
|
-
{
|
|
87
|
+
<FocusableGroupContextProvider id={id}>
|
|
88
|
+
{children}
|
|
89
|
+
</FocusableGroupContextProvider>
|
|
85
90
|
</div>
|
|
86
91
|
);
|
|
87
92
|
}
|
|
88
93
|
}
|
|
94
|
+
|
|
95
|
+
const FocusableGroupWithFocusableContext = withFocusableContext(FocusableGroup);
|
|
96
|
+
|
|
97
|
+
export { FocusableGroupWithFocusableContext as FocusableGroup };
|
|
@@ -50,7 +50,11 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
50
50
|
);
|
|
51
51
|
|
|
52
52
|
expect(transformed_preset).toBeDefined();
|
|
53
|
-
|
|
53
|
+
|
|
54
|
+
expect(transformed_preset.id).toEqual(
|
|
55
|
+
`${mockComp.id}-${mockPreset.feed_url}-0`
|
|
56
|
+
); // id was transformed
|
|
57
|
+
|
|
54
58
|
expect(transformed_preset.data.source).toEqual(mockPreset.feed_url); // source was replaced with preset feed_url.
|
|
55
59
|
});
|
|
56
60
|
});
|
|
@@ -117,7 +121,10 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
117
121
|
|
|
118
122
|
expect(Array.isArray(result)).toBe(true);
|
|
119
123
|
expect(result).toHaveLength(2);
|
|
120
|
-
|
|
124
|
+
|
|
125
|
+
expect(result[0].id).toEqual(
|
|
126
|
+
`${mockSmartComp.id}-${mockFeeds.preset_source.data.entry[0].feed_url}-0`
|
|
127
|
+
);
|
|
121
128
|
|
|
122
129
|
expect(result[0].data.source).toEqual(
|
|
123
130
|
mockFeeds.preset_source.data.entry[0].feed_url
|
|
@@ -287,7 +294,7 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
287
294
|
mockComponents[0],
|
|
288
295
|
{
|
|
289
296
|
...mockLayoutPresets.preset_one,
|
|
290
|
-
id: mockComponents[1].id
|
|
297
|
+
id: `${mockComponents[1].id}-${mockPresetEntry[0].feed_url}-1`,
|
|
291
298
|
data: { source: mockPresetEntry[0].feed_url },
|
|
292
299
|
},
|
|
293
300
|
];
|
|
@@ -344,12 +351,12 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
344
351
|
const mockTransformedComponents = [
|
|
345
352
|
{
|
|
346
353
|
...mockLayoutPresets.preset_one,
|
|
347
|
-
id: mockComponents[0].id
|
|
354
|
+
id: `${mockComponents[0].id}-${mockPresetEntry1[0].feed_url}-0`,
|
|
348
355
|
data: { source: mockPresetEntry1[0].feed_url },
|
|
349
356
|
},
|
|
350
357
|
{
|
|
351
358
|
...mockLayoutPresets.preset_one,
|
|
352
|
-
id: mockComponents[1].id
|
|
359
|
+
id: `${mockComponents[1].id}-${mockPresetEntry2[0].feed_url}-1`,
|
|
353
360
|
data: { source: mockPresetEntry2[0].feed_url },
|
|
354
361
|
},
|
|
355
362
|
];
|
|
@@ -412,7 +419,7 @@ describe("getTransformedPreset should return the passed components if smartCompo
|
|
|
412
419
|
const mockTransformedComponents = mockPresetEntry.map((preset, i) => {
|
|
413
420
|
return {
|
|
414
421
|
...mockLayoutPresets[preset.preset_name],
|
|
415
|
-
id: mockComponents[0].id
|
|
422
|
+
id: `${mockComponents[0].id}-${preset.feed_url}-${i}`,
|
|
416
423
|
data: { source: preset.feed_url },
|
|
417
424
|
};
|
|
418
425
|
});
|
|
@@ -41,7 +41,7 @@ export const getTransformedPreset = (
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const updatedPresetComponent = produce(presetComponent, (draft) => {
|
|
44
|
-
draft.id = `${componentId}
|
|
44
|
+
draft.id = `${componentId}-${preset?.feed_url}-${index}`;
|
|
45
45
|
draft.data.source = preset?.feed_url;
|
|
46
46
|
|
|
47
47
|
if (draft.ui_components) {
|
|
@@ -19,6 +19,7 @@ type Props = {
|
|
|
19
19
|
modalHeight?: number;
|
|
20
20
|
modalWidth?: number;
|
|
21
21
|
mode: VideoModalMode;
|
|
22
|
+
groupId?: string;
|
|
22
23
|
};
|
|
23
24
|
|
|
24
25
|
const YOUTUBE_PLUGIN_ID = "youtube-player-qb";
|
|
@@ -93,6 +94,7 @@ export function HandlePlayable({
|
|
|
93
94
|
modalHeight,
|
|
94
95
|
modalWidth,
|
|
95
96
|
mode,
|
|
97
|
+
groupId,
|
|
96
98
|
}: Props): React.ReactElement | null {
|
|
97
99
|
const state = usePickFromState();
|
|
98
100
|
|
|
@@ -167,6 +169,7 @@ export function HandlePlayable({
|
|
|
167
169
|
isModal={isModal}
|
|
168
170
|
mode={mode}
|
|
169
171
|
disableCastAction={playerModuleProperties?.disableCastAction}
|
|
172
|
+
groupId={groupId}
|
|
170
173
|
/>
|
|
171
174
|
);
|
|
172
175
|
}
|
|
@@ -60,7 +60,7 @@ export function ScreenLayoutContextProvider(props: {
|
|
|
60
60
|
initialState
|
|
61
61
|
);
|
|
62
62
|
|
|
63
|
-
const
|
|
63
|
+
const screenLayoutValue = React.useMemo(
|
|
64
64
|
() => ({
|
|
65
65
|
...screenLayout,
|
|
66
66
|
setScreenLayout,
|
|
@@ -69,7 +69,7 @@ export function ScreenLayoutContextProvider(props: {
|
|
|
69
69
|
);
|
|
70
70
|
|
|
71
71
|
return (
|
|
72
|
-
<ScreenLayoutContext.Provider value={
|
|
72
|
+
<ScreenLayoutContext.Provider value={screenLayoutValue}>
|
|
73
73
|
{props.children}
|
|
74
74
|
</ScreenLayoutContext.Provider>
|
|
75
75
|
);
|
|
@@ -2,58 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`Layout TV renders 1`] = `
|
|
4
4
|
<View
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
"height": 1080,
|
|
9
|
-
"width": 1920,
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
testID="layout-container"
|
|
5
|
+
excludeFromFocusSearching={true}
|
|
6
|
+
id="/river/A1234"
|
|
7
|
+
preferredFocus={true}
|
|
13
8
|
>
|
|
14
9
|
<View
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
style={
|
|
20
|
-
Array [
|
|
21
|
-
Object {
|
|
22
|
-
"position": "absolute",
|
|
23
|
-
"top": 0,
|
|
24
|
-
"width": 1920,
|
|
25
|
-
"zIndex": 10,
|
|
26
|
-
},
|
|
27
|
-
Object {
|
|
28
|
-
"maxHeight": 10,
|
|
29
|
-
},
|
|
30
|
-
]
|
|
10
|
+
Components={
|
|
11
|
+
Object {
|
|
12
|
+
"Background": [Function],
|
|
13
|
+
"NavBar": [Function],
|
|
31
14
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
isVisible={true}
|
|
36
|
-
menuPlugin={
|
|
37
|
-
Object {
|
|
38
|
-
"module": [Function],
|
|
39
|
-
"name": "resolved-menu-plug",
|
|
40
|
-
"type": "menu",
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
navigationProps={
|
|
44
|
-
Object {
|
|
45
|
-
"assets": undefined,
|
|
46
|
-
"home": undefined,
|
|
47
|
-
"nav_items": undefined,
|
|
48
|
-
"rules": undefined,
|
|
49
|
-
"selected": undefined,
|
|
50
|
-
"styles": undefined,
|
|
51
|
-
"title": "",
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
testID="nav-bar-component"
|
|
55
|
-
/>
|
|
56
|
-
</View>
|
|
57
|
-
</View>
|
|
15
|
+
}
|
|
16
|
+
route="/river/A1234"
|
|
17
|
+
/>
|
|
58
18
|
</View>
|
|
59
19
|
`;
|
|
@@ -1,79 +1,48 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import
|
|
2
|
+
import { omit } from "ramda";
|
|
3
3
|
import { View } from "react-native";
|
|
4
4
|
import { render } from "@testing-library/react-native";
|
|
5
5
|
import { Provider } from "react-redux";
|
|
6
|
-
import { createStore } from "redux";
|
|
7
6
|
import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
|
|
7
|
+
import configureStore from "redux-mock-store";
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const withoutChildren = omit(["children"]);
|
|
10
10
|
|
|
11
|
-
jest.mock("
|
|
12
|
-
|
|
11
|
+
jest.mock("../../../Screen/TV/index.web", () => ({
|
|
12
|
+
Screen: View,
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const Layout = require("../index.web").default;
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
const ResolvedMenuPlug = () => <View testID="resolved-menu-plug" />;
|
|
20
|
-
|
|
21
|
-
const plugins = [
|
|
22
|
-
{
|
|
23
|
-
module: ResolvedMenuPlug,
|
|
24
|
-
type: "menu",
|
|
25
|
-
name: "resolved-menu-plug",
|
|
26
|
-
},
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const bgImage = "http://applicaster.com";
|
|
30
|
-
const isNavBarVisible = true;
|
|
31
|
-
|
|
32
|
-
const store = createStore(() => ({
|
|
17
|
+
const mockStore = configureStore()({
|
|
33
18
|
appState: { appReady: true },
|
|
34
|
-
|
|
35
|
-
plugins,
|
|
36
|
-
}));
|
|
19
|
+
});
|
|
37
20
|
|
|
38
21
|
describe("Layout TV", () => {
|
|
39
|
-
const themes = {
|
|
40
|
-
myTheme: { screen_margin_top: 10, screen_margin_bottom: 10 },
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const selectedThemeId = "myTheme";
|
|
44
|
-
|
|
45
22
|
const navigator = {
|
|
46
|
-
activeRiver: {
|
|
47
|
-
navigations: [{ category: "menu", navigation_type: "menu" }],
|
|
48
|
-
},
|
|
49
|
-
canGoBack: () => false,
|
|
50
23
|
currentRoute: "/river/A1234",
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
24
|
+
mainStack: [
|
|
25
|
+
{
|
|
26
|
+
route: "/river/A1234",
|
|
27
|
+
state: "state",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
55
30
|
};
|
|
56
31
|
|
|
57
32
|
const wrapper = ({ children }) => (
|
|
58
|
-
<Provider store={
|
|
33
|
+
<Provider store={mockStore}>
|
|
59
34
|
<NavigationContext.Provider value={navigator}>
|
|
60
|
-
|
|
61
|
-
{children}
|
|
62
|
-
</ThemeContext.Provider>
|
|
35
|
+
{children}
|
|
63
36
|
</NavigationContext.Provider>
|
|
64
37
|
</Provider>
|
|
65
38
|
);
|
|
66
39
|
|
|
67
|
-
const Screen = (props) => (
|
|
68
|
-
<View testID="screen-component" {...R.omit(["children"])(props)} />
|
|
69
|
-
);
|
|
70
|
-
|
|
71
40
|
const NavBar = (props) => (
|
|
72
|
-
<View testID="nav-bar-component" {...
|
|
41
|
+
<View testID="nav-bar-component" {...withoutChildren(props)} />
|
|
73
42
|
);
|
|
74
43
|
|
|
75
44
|
const Background = (props: Record<any, any>) => (
|
|
76
|
-
<View testID="background-component" {...
|
|
45
|
+
<View testID="background-component" {...withoutChildren(props)}>
|
|
77
46
|
{props.children}
|
|
78
47
|
</View>
|
|
79
48
|
);
|
|
@@ -84,33 +53,8 @@ describe("Layout TV", () => {
|
|
|
84
53
|
};
|
|
85
54
|
|
|
86
55
|
it("renders", () => {
|
|
87
|
-
const { toJSON } = render(
|
|
88
|
-
<Layout Components={Components}>
|
|
89
|
-
<Screen />
|
|
90
|
-
</Layout>,
|
|
91
|
-
{ wrapper }
|
|
92
|
-
);
|
|
56
|
+
const { toJSON } = render(<Layout Components={Components} />, { wrapper });
|
|
93
57
|
|
|
94
58
|
expect(toJSON()).toMatchSnapshot();
|
|
95
59
|
});
|
|
96
|
-
|
|
97
|
-
describe("props passed to background component", () => {
|
|
98
|
-
it("passes correct props", () => {
|
|
99
|
-
const { getByTestId } = render(
|
|
100
|
-
<Layout
|
|
101
|
-
Components={Components}
|
|
102
|
-
ComponentsExtraProps={{
|
|
103
|
-
Background: { testProp: true },
|
|
104
|
-
}}
|
|
105
|
-
>
|
|
106
|
-
<Screen />
|
|
107
|
-
</Layout>,
|
|
108
|
-
{ wrapper }
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const bgComponent = getByTestId("background-component");
|
|
112
|
-
expect(bgComponent.props.backgroundImageUrl).toBe(bgImage);
|
|
113
|
-
expect(bgComponent.props.testProp).toBe(true);
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
60
|
});
|
|
@@ -14,12 +14,12 @@ import { getBackgroundImageUrl } from "../utils";
|
|
|
14
14
|
import { LayoutContainer } from "./LayoutContainer";
|
|
15
15
|
import { NavBarContainer } from "./NavBarContainer";
|
|
16
16
|
import { ScreenContainer } from "./ScreenContainer";
|
|
17
|
-
import { TransitionOverlay } from "./TransitionOverlay";
|
|
18
17
|
|
|
19
18
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
20
19
|
import { isApplePlatform } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
21
20
|
import { PathnameContext } from "../../../Contexts/PathnameContext";
|
|
22
21
|
import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
22
|
+
import { ScreenDataContext } from "../../../Contexts/ScreenDataContext";
|
|
23
23
|
|
|
24
24
|
type Components = {
|
|
25
25
|
NavBar: React.ComponentType<any>;
|
|
@@ -105,21 +105,22 @@ const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
|
105
105
|
backgroundImageUrl={backgroundImageUrl}
|
|
106
106
|
{...R.omit(["ref"])(ComponentsExtraProps?.Background)}
|
|
107
107
|
>
|
|
108
|
-
{
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
<ScreenDataContext.Provider value={navigator.data}>
|
|
109
|
+
<PathnameContext.Provider value={navigator.currentRoute}>
|
|
110
|
+
{!isTVOS ? navBarContainer : null}
|
|
111
|
+
{navBarReady ? (
|
|
112
|
+
/**
|
|
111
113
|
Need to re-mount this component on each navigation event
|
|
112
114
|
It's a hack to work around the fact that we don't support
|
|
113
115
|
correct screen rendering in our navigator (shared currentRoute state)
|
|
114
116
|
**/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
) : null}
|
|
119
|
-
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
117
|
+
<ScreenContainer>{children}</ScreenContainer>
|
|
118
|
+
) : null}
|
|
119
|
+
{/** Render NavBarContainer over ScreenContainer to let tvOS native
|
|
120
120
|
focus manager works correctly to focus nav bar items first **/}
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
{isTVOS ? navBarContainer : null}
|
|
122
|
+
</PathnameContext.Provider>
|
|
123
|
+
</ScreenDataContext.Provider>
|
|
123
124
|
</Components.Background>
|
|
124
125
|
</ScreenLayoutContextProvider>
|
|
125
126
|
</LayoutContainer>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { pathOr } from "ramda";
|
|
3
|
+
|
|
4
|
+
import { usePickFromState } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
|
+
|
|
6
|
+
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
7
|
+
import { StackNavigator } from "../../Navigator";
|
|
8
|
+
|
|
9
|
+
type Components = {
|
|
10
|
+
NavBar: React.ComponentType<any>;
|
|
11
|
+
Background: React.ComponentType<any>;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type Props = {
|
|
15
|
+
Components: Components;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const Layout = ({ Components }: Props) => {
|
|
19
|
+
const { appState } = usePickFromState(["appState"]);
|
|
20
|
+
const appReady = pathOr(false, ["appReady"], appState);
|
|
21
|
+
|
|
22
|
+
if (!appReady) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<ScreenLayoutContextProvider>
|
|
28
|
+
<StackNavigator Components={Components} />
|
|
29
|
+
</ScreenLayoutContextProvider>
|
|
30
|
+
);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default Layout;
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
isTV,
|
|
4
|
+
platformSelect,
|
|
5
|
+
} from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
6
|
|
|
6
7
|
import Mobile from "./Mobile";
|
|
7
|
-
import TV from "./TV";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
import TVWeb from "./TV/index.web";
|
|
10
|
+
import TVDefault from "./TV/index";
|
|
11
|
+
|
|
12
|
+
const TV = platformSelect({
|
|
13
|
+
samsung_tv: TVWeb,
|
|
14
|
+
lg_tv: TVWeb,
|
|
15
|
+
web: TVWeb,
|
|
16
|
+
default: TVDefault,
|
|
17
|
+
});
|
|
11
18
|
|
|
19
|
+
export default function Layout(props) {
|
|
12
20
|
if (isTV()) {
|
|
13
|
-
return
|
|
14
|
-
<ScreenContextProvider pathname={pathname}>
|
|
15
|
-
<TV {...props} />
|
|
16
|
-
</ScreenContextProvider>
|
|
17
|
-
);
|
|
21
|
+
return <TV {...props} />;
|
|
18
22
|
} else {
|
|
19
23
|
return <Mobile {...props} />;
|
|
20
24
|
}
|
|
@@ -25,6 +25,7 @@ type Props = {
|
|
|
25
25
|
testID?: string;
|
|
26
26
|
accessibilityLabel?: string;
|
|
27
27
|
cellUUID?: string;
|
|
28
|
+
extraProps?: Record<string, any>;
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
function isStringAsset(asset) {
|
|
@@ -108,6 +109,7 @@ export function ActionButton(props: Props) {
|
|
|
108
109
|
flavour={flavour}
|
|
109
110
|
asset={getAssetValue(asset, flavour)}
|
|
110
111
|
cellUUID={cellUUID}
|
|
112
|
+
{...(props?.extraProps ?? {})}
|
|
111
113
|
/>
|
|
112
114
|
)}
|
|
113
115
|
</TouchableOpacity>
|