@applicaster/zapp-react-native-ui-components 15.1.0-rc.8 → 16.0.0-alpha.1063022457
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/BackgroundImage/BackgroundImage.tsx +42 -0
- package/Components/BackgroundImage/BackgroundImage.tv.android.tsx +28 -0
- package/Components/BackgroundImage/BackgroundImage.tv.ios.tsx +24 -0
- package/Components/BackgroundImage/index.ts +1 -0
- package/Components/BaseFocusable/index.ios.ts +12 -2
- package/Components/Cell/FocusableWrapper.tsx +3 -0
- package/Components/Cell/TvOSCellComponent.tsx +6 -3
- package/Components/CellRendererResolver/index.ts +1 -1
- package/Components/ComponentResolver/__tests__/componentResolver.test.js +1 -1
- package/Components/Focusable/Focusable.tsx +4 -2
- package/Components/Focusable/FocusableTvOS.tsx +18 -1
- package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
- package/Components/FocusableGroup/FocusableTvOS.tsx +41 -8
- package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
- package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
- package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
- package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
- package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
- package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
- package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
- package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -0
- package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
- package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
- package/Components/GeneralContentScreen/index.ts +2 -0
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
- package/Components/Layout/TV/NavBarContainer.tsx +1 -10
- package/Components/Layout/TV/ScreenContainer.tsx +2 -6
- package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
- package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +0 -1
- package/Components/Layout/TV/index.tsx +5 -7
- package/Components/Layout/TV/index.web.tsx +5 -7
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
- package/Components/MasterCell/DefaultComponents/ActionButton.tsx +16 -5
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -10
- package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tv.android.tsx +58 -0
- package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
- package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
- package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
- package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
- package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
- package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
- package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
- package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +34 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +11 -0
- package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
- package/Components/MasterCell/DefaultComponents/index.ts +9 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +135 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +22 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +125 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +37 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +393 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +343 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +122 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
- package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
- package/Components/MasterCell/MappingFunctions/index.js +3 -2
- package/Components/MasterCell/README.md +4 -0
- package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
- package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
- package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
- package/Components/MasterCell/dataAdapter.ts +4 -1
- package/Components/MasterCell/elementMapper.tsx +52 -7
- package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
- package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
- package/Components/MasterCell/utils/index.ts +85 -15
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
- package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
- package/Components/PlayerContainer/PlayerContainer.tsx +15 -14
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +284 -0
- package/Components/River/ComponentsMap/ComponentsMap.tsx +6 -19
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +19 -88
- package/Components/River/River.tsx +9 -82
- package/Components/River/TV/River.tsx +31 -14
- package/Components/River/TV/index.tsx +8 -4
- package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
- package/Components/River/TV/utils/index.ts +4 -0
- package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
- package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +1 -0
- package/Components/River/__tests__/componentsMap.test.js +38 -0
- package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
- package/Components/River/hooks/index.ts +1 -0
- package/Components/River/hooks/usePullToRefresh.ts +51 -0
- package/Components/Screen/TV/index.web.tsx +4 -2
- package/Components/Screen/__tests__/Screen.test.tsx +65 -42
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
- package/Components/Screen/hooks.ts +2 -3
- package/Components/Screen/index.tsx +2 -3
- package/Components/Screen/orientationHandler.ts +3 -3
- package/Components/ScreenResolver/index.tsx +9 -5
- package/Components/ScreenRevealManager/Overlay.tsx +34 -0
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/Overlay.test.tsx +88 -0
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +9 -20
- package/Components/Tabs/TabContent.tsx +7 -4
- package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
- package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
- package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
- package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
- package/Components/TopCutoffOverlay/index.tsx +55 -0
- package/Components/Transitioner/index.js +3 -3
- package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
- package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
- package/Components/VideoModal/utils.ts +18 -10
- package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
- package/Components/Viewport/ViewportAware/index.tsx +16 -7
- package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
- package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -6
- package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
- package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
- package/Contexts/ScreenContext/index.tsx +25 -18
- package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
- package/Decorators/Analytics/index.tsx +6 -5
- package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
- package/Decorators/ConfigurationWrapper/const.ts +1 -0
- package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
- package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
- package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
- package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
- package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
- package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
- package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/Helpers/index.js +7 -40
- package/package.json +5 -5
- package/Components/Layout/TV/LayoutBackground.tsx +0 -28
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
- package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
- package/Components/River/TV/withTVEventHandler.tsx +0 -36
- package/Helpers/Analytics/index.js +0 -95
- package/Helpers/DataSourceHelper/index.js +0 -19
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View, ViewStyle } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
|
|
5
|
+
import {
|
|
6
|
+
selectRemoteConfigurations,
|
|
7
|
+
useAppSelector,
|
|
8
|
+
} from "@applicaster/zapp-react-native-redux";
|
|
9
|
+
|
|
10
|
+
import { getBackgroundImageUrl } from "../Layout/utils";
|
|
11
|
+
|
|
12
|
+
const baseBackgroundStyles = (imageUrl, backgroundColor) =>
|
|
13
|
+
({
|
|
14
|
+
position: "absolute",
|
|
15
|
+
left: 0,
|
|
16
|
+
top: 0,
|
|
17
|
+
right: 0,
|
|
18
|
+
bottom: 0,
|
|
19
|
+
zIndex: -1,
|
|
20
|
+
backgroundSize: "cover",
|
|
21
|
+
background: imageUrl
|
|
22
|
+
? `url(${imageUrl}) no-repeat top center ${backgroundColor}`
|
|
23
|
+
: backgroundColor,
|
|
24
|
+
}) as ViewStyle;
|
|
25
|
+
|
|
26
|
+
export const BackgroundImage = ({ children }: React.PropsWithChildren) => {
|
|
27
|
+
const theme = useTheme();
|
|
28
|
+
|
|
29
|
+
const remoteConfigurations = useAppSelector(selectRemoteConfigurations);
|
|
30
|
+
|
|
31
|
+
const backgroundColor = theme.app_background_color;
|
|
32
|
+
const backgroundImageUrl = getBackgroundImageUrl(remoteConfigurations);
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View
|
|
36
|
+
id="background"
|
|
37
|
+
style={baseBackgroundStyles(backgroundImageUrl, backgroundColor)}
|
|
38
|
+
>
|
|
39
|
+
{children}
|
|
40
|
+
</View>
|
|
41
|
+
);
|
|
42
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import {
|
|
3
|
+
View,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
ImageURISource,
|
|
6
|
+
ImageBackground,
|
|
7
|
+
} from "react-native";
|
|
8
|
+
|
|
9
|
+
import { ASSETS } from "@applicaster/zapp-react-native-ui-components/Helpers";
|
|
10
|
+
|
|
11
|
+
const imageSource: ImageURISource = {
|
|
12
|
+
uri: ASSETS.APP_BACKGROUND_IMAGE,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const styles = StyleSheet.create({
|
|
16
|
+
container: { flex: 1 },
|
|
17
|
+
backgroundImage: { width: "100%", height: "100%" },
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export function BackgroundImage({ children }: React.PropsWithChildren) {
|
|
21
|
+
return (
|
|
22
|
+
<View style={styles.container}>
|
|
23
|
+
<ImageBackground style={styles.backgroundImage} source={imageSource}>
|
|
24
|
+
{children}
|
|
25
|
+
</ImageBackground>
|
|
26
|
+
</View>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { View, StyleSheet, ImageURISource } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
|
|
5
|
+
import { ASSETS } from "@applicaster/zapp-react-native-ui-components/Helpers";
|
|
6
|
+
|
|
7
|
+
const imageSource: ImageURISource = {
|
|
8
|
+
uri: ASSETS.APP_BACKGROUND_IMAGE,
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const styles = StyleSheet.create({
|
|
12
|
+
container: { flex: 1 },
|
|
13
|
+
backgroundImage: { width: "100%", height: "100%" },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export function BackgroundImage({ children }: React.PropsWithChildren) {
|
|
17
|
+
return (
|
|
18
|
+
<View style={styles.container}>
|
|
19
|
+
<Image style={styles.backgroundImage} source={imageSource}>
|
|
20
|
+
{children}
|
|
21
|
+
</Image>
|
|
22
|
+
</View>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BackgroundImage } from "./BackgroundImage";
|
|
@@ -22,6 +22,7 @@ type Props = {
|
|
|
22
22
|
onFocus?: FocusManager.FocusEventCB;
|
|
23
23
|
onBlur?: FocusManager.FocusEventCB;
|
|
24
24
|
selected?: boolean;
|
|
25
|
+
skipFocusManagerRegistration?: boolean;
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
export class BaseFocusable<
|
|
@@ -61,10 +62,14 @@ export class BaseFocusable<
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
componentDidMount() {
|
|
64
|
-
const { id } = this.props;
|
|
65
|
+
const { id, skipFocusManagerRegistration } = this.props;
|
|
65
66
|
const component = this;
|
|
66
67
|
this.node = this.ref.current;
|
|
67
68
|
|
|
69
|
+
if (skipFocusManagerRegistration) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
68
73
|
focusManager.register({
|
|
69
74
|
id,
|
|
70
75
|
component: component,
|
|
@@ -118,7 +123,12 @@ export class BaseFocusable<
|
|
|
118
123
|
|
|
119
124
|
componentWillUnmount() {
|
|
120
125
|
this._isMounted = false;
|
|
121
|
-
const { id } = this.props;
|
|
126
|
+
const { id, skipFocusManagerRegistration } = this.props;
|
|
127
|
+
|
|
128
|
+
if (skipFocusManagerRegistration) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
122
132
|
focusManager.unregister(id, { group: this.isGroup || false });
|
|
123
133
|
}
|
|
124
134
|
|
|
@@ -10,6 +10,7 @@ type Props = {
|
|
|
10
10
|
children: (focused: boolean) => React.ReactNode;
|
|
11
11
|
onFocus: (arg1: any, index?: number) => void;
|
|
12
12
|
onBlur: Callback;
|
|
13
|
+
skipFocusManagerRegistration?: boolean;
|
|
13
14
|
};
|
|
14
15
|
|
|
15
16
|
export const FocusableWrapper = ({
|
|
@@ -20,6 +21,7 @@ export const FocusableWrapper = ({
|
|
|
20
21
|
applyWrapper,
|
|
21
22
|
onFocus,
|
|
22
23
|
onBlur,
|
|
24
|
+
skipFocusManagerRegistration,
|
|
23
25
|
}: Props) => {
|
|
24
26
|
if (applyWrapper) {
|
|
25
27
|
return (
|
|
@@ -34,6 +36,7 @@ export const FocusableWrapper = ({
|
|
|
34
36
|
// @ts-ignore
|
|
35
37
|
offsetUpdater={noop}
|
|
36
38
|
isFocusable
|
|
39
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
37
40
|
>
|
|
38
41
|
{(focused) => children(focused)}
|
|
39
42
|
</Focusable>
|
|
@@ -80,6 +80,7 @@ type Props = {
|
|
|
80
80
|
componentsMapOffset: number;
|
|
81
81
|
applyFocusableWrapper: boolean;
|
|
82
82
|
hasFocusableInside: boolean;
|
|
83
|
+
skipFocusManagerRegistration?: boolean;
|
|
83
84
|
};
|
|
84
85
|
|
|
85
86
|
type State = {
|
|
@@ -194,7 +195,6 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
194
195
|
groupId,
|
|
195
196
|
component,
|
|
196
197
|
index,
|
|
197
|
-
componentsMapOffset,
|
|
198
198
|
} = this.props;
|
|
199
199
|
|
|
200
200
|
this.setScreenLayout(componentAnchorPointY, screenLayout);
|
|
@@ -221,8 +221,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
221
221
|
const totalOffset =
|
|
222
222
|
headerOffset +
|
|
223
223
|
toNumberWithDefaultZero(componentAnchorPointY) +
|
|
224
|
-
extraAnchorPointYOffset
|
|
225
|
-
toNumberWithDefaultZero(componentsMapOffset) +
|
|
224
|
+
extraAnchorPointYOffset +
|
|
226
225
|
componentMarginTop +
|
|
227
226
|
componentPaddingTop;
|
|
228
227
|
|
|
@@ -266,6 +265,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
266
265
|
behavior,
|
|
267
266
|
applyFocusableWrapper,
|
|
268
267
|
hasFocusableInside,
|
|
268
|
+
skipFocusManagerRegistration,
|
|
269
269
|
} = this.props;
|
|
270
270
|
|
|
271
271
|
const { id } = item;
|
|
@@ -291,6 +291,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
291
291
|
onFocus={handleFocus}
|
|
292
292
|
onBlur={onBlur || this.onBlur}
|
|
293
293
|
applyWrapper={applyFocusableWrapper}
|
|
294
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
294
295
|
>
|
|
295
296
|
{(focused) => (
|
|
296
297
|
<CellWithFocusable
|
|
@@ -305,6 +306,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
305
306
|
focused={focused || this.props.focused}
|
|
306
307
|
behavior={behavior}
|
|
307
308
|
isFocusable={isFocusable}
|
|
309
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
308
310
|
/>
|
|
309
311
|
)}
|
|
310
312
|
</FocusableWrapper>
|
|
@@ -327,6 +329,7 @@ class TvOSCell extends React.Component<Props, State> {
|
|
|
327
329
|
offsetUpdater={offsetUpdater}
|
|
328
330
|
style={baseCellStyles}
|
|
329
331
|
isFocusable={isFocusable}
|
|
332
|
+
skipFocusManagerRegistration={skipFocusManagerRegistration}
|
|
330
333
|
>
|
|
331
334
|
{(focused) => (
|
|
332
335
|
<FocusableCell
|
|
@@ -83,7 +83,7 @@ export function CellRendererResolver({
|
|
|
83
83
|
|
|
84
84
|
if (!cellRendererPlugin && !isGroup(component)) {
|
|
85
85
|
logger.warning({
|
|
86
|
-
message:
|
|
86
|
+
message: `Could not resolve cell builder plugin: ${component?.component_type}`,
|
|
87
87
|
data: { component },
|
|
88
88
|
});
|
|
89
89
|
}
|
|
@@ -107,7 +107,7 @@ describe("ComponentResolverComponent", () => {
|
|
|
107
107
|
expect(mockLogger.warning).toHaveBeenNthCalledWith(
|
|
108
108
|
1,
|
|
109
109
|
expect.objectContaining({
|
|
110
|
-
message: "Could not resolve cell builder plugin",
|
|
110
|
+
message: "Could not resolve cell builder plugin: foo",
|
|
111
111
|
})
|
|
112
112
|
);
|
|
113
113
|
|
|
@@ -8,6 +8,8 @@ import { withFocusableContext } from "../../Contexts/FocusableGroupContext/withF
|
|
|
8
8
|
import { StyleSheet, ViewStyle } from "react-native";
|
|
9
9
|
import { AccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager";
|
|
10
10
|
|
|
11
|
+
import { isSearchInputId } from "@applicaster/zapp-react-native-utils/searchUtils";
|
|
12
|
+
|
|
11
13
|
type Props = {
|
|
12
14
|
initialFocus?: boolean;
|
|
13
15
|
id: string;
|
|
@@ -106,7 +108,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
106
108
|
onMouseEnter() {
|
|
107
109
|
const { id } = this.props;
|
|
108
110
|
|
|
109
|
-
if (id
|
|
111
|
+
if (!isSearchInputId(id)) {
|
|
110
112
|
this.mouse = true;
|
|
111
113
|
this.props?.handleFocus?.({ mouse: true });
|
|
112
114
|
|
|
@@ -120,7 +122,7 @@ class Focusable extends BaseFocusable<Props> {
|
|
|
120
122
|
onMouseLeave() {
|
|
121
123
|
const { id } = this.props;
|
|
122
124
|
|
|
123
|
-
if (id
|
|
125
|
+
if (!isSearchInputId(id)) {
|
|
124
126
|
this.mouse = false;
|
|
125
127
|
this.blur(null);
|
|
126
128
|
}
|
|
@@ -10,8 +10,12 @@ import {
|
|
|
10
10
|
forceFocusableFocus,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
|
|
12
12
|
import { findNodeHandle, ViewStyle } from "react-native";
|
|
13
|
+
import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
import {
|
|
16
|
+
emitFocused,
|
|
17
|
+
emitNativeRegistered,
|
|
18
|
+
} from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
15
19
|
|
|
16
20
|
type Props = {
|
|
17
21
|
id: string;
|
|
@@ -39,6 +43,7 @@ type Props = {
|
|
|
39
43
|
hasReceivedFocus: () => void;
|
|
40
44
|
offsetUpdater: (arg1: string, arg2: number) => number;
|
|
41
45
|
style: ViewStyle;
|
|
46
|
+
skipFocusManagerRegistration?: boolean;
|
|
42
47
|
};
|
|
43
48
|
|
|
44
49
|
export class Focusable extends BaseFocusable<Props> {
|
|
@@ -53,6 +58,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
53
58
|
this.nextFocusableReactTags = {};
|
|
54
59
|
this.preferredFocus = this.preferredFocus.bind(this);
|
|
55
60
|
this.measureView = this.measureView.bind(this);
|
|
61
|
+
this.onRegistered = this.onRegistered.bind(this);
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
/**
|
|
@@ -84,6 +90,9 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
84
90
|
});
|
|
85
91
|
}
|
|
86
92
|
|
|
93
|
+
const id: string = nativeEvent.itemID;
|
|
94
|
+
emitFocused(id);
|
|
95
|
+
|
|
87
96
|
onFocus(nativeEvent);
|
|
88
97
|
}
|
|
89
98
|
|
|
@@ -169,6 +178,13 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
169
178
|
});
|
|
170
179
|
}
|
|
171
180
|
|
|
181
|
+
onRegistered({ nativeEvent }) {
|
|
182
|
+
const groupId = nativeEvent?.groupId;
|
|
183
|
+
const id = nativeEvent?.itemId;
|
|
184
|
+
|
|
185
|
+
emitNativeRegistered({ id, groupId, isGroup: false });
|
|
186
|
+
}
|
|
187
|
+
|
|
172
188
|
render() {
|
|
173
189
|
const {
|
|
174
190
|
children,
|
|
@@ -203,6 +219,7 @@ export class Focusable extends BaseFocusable<Props> {
|
|
|
203
219
|
focusable={isFocusable}
|
|
204
220
|
{...this.nextFocusableReactTags}
|
|
205
221
|
{...otherProps}
|
|
222
|
+
onRegistered={this.onRegistered}
|
|
206
223
|
>
|
|
207
224
|
{typeof children === "function" ? children(focused) : children}
|
|
208
225
|
</FocusableItemNative>
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { FocusableGroupNative } from "@applicaster/zapp-react-native-ui-components/Components/NativeFocusables";
|
|
3
3
|
import { BaseFocusable } from "@applicaster/zapp-react-native-ui-components/Components/BaseFocusable";
|
|
4
|
-
|
|
4
|
+
// TODO: Enable when will be feature flags
|
|
5
|
+
// import { createLogger } from "@applicaster/zapp-react-native-utils/logger";
|
|
6
|
+
import { LayoutContext } from "@applicaster/zapp-react-native-tvos-app/Context/LayoutContext";
|
|
7
|
+
import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useRoute";
|
|
8
|
+
import { isScreenPlayable } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
|
|
9
|
+
import { emitNativeRegistered } from "@applicaster/zapp-react-native-utils/appUtils/focusManagerAux/utils/utils.ios";
|
|
5
10
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
// TODO: Enable when will be feature flags
|
|
12
|
+
// const { log_verbose } = createLogger({
|
|
13
|
+
// subsystem: "General",
|
|
14
|
+
// category: "FocusableGroup",
|
|
15
|
+
// });
|
|
10
16
|
|
|
11
17
|
type FocusableGroupNativeEvent = {
|
|
12
18
|
nativeEvent: {
|
|
@@ -33,9 +39,16 @@ type Props = {
|
|
|
33
39
|
screenData: { screenId: string; parentScreenId: string };
|
|
34
40
|
};
|
|
35
41
|
|
|
36
|
-
|
|
42
|
+
class FocusableGroupComponent extends BaseFocusable<Props> {
|
|
37
43
|
public readonly isGroup: boolean = true;
|
|
38
44
|
|
|
45
|
+
onRegistered = ({ nativeEvent }) => {
|
|
46
|
+
const groupId = nativeEvent?.groupId;
|
|
47
|
+
const id = nativeEvent?.itemId;
|
|
48
|
+
|
|
49
|
+
emitNativeRegistered({ id, groupId, isGroup: true });
|
|
50
|
+
};
|
|
51
|
+
|
|
39
52
|
render() {
|
|
40
53
|
const {
|
|
41
54
|
children,
|
|
@@ -48,12 +61,14 @@ export class FocusableGroup extends BaseFocusable<Props> {
|
|
|
48
61
|
} = this.props;
|
|
49
62
|
|
|
50
63
|
const onGroupFocus = ({ nativeEvent }: FocusableGroupNativeEvent) => {
|
|
51
|
-
|
|
64
|
+
// TODO: Enable when will be feature flags
|
|
65
|
+
// log_verbose("FOCUSABLE_GROUP: onGroupFocus", { nativeEvent });
|
|
52
66
|
this.onFocus(this.ref, nativeEvent.focusHeading);
|
|
53
67
|
};
|
|
54
68
|
|
|
55
69
|
const onGroupBlur = ({ nativeEvent }: FocusableGroupNativeEvent) => {
|
|
56
|
-
|
|
70
|
+
// TODO: Enable when will be feature flags
|
|
71
|
+
// log_verbose("FOCUSABLE_GROUP: onGroupBlur", { nativeEvent });
|
|
57
72
|
this.onBlur(this.ref, nativeEvent.focusHeading);
|
|
58
73
|
};
|
|
59
74
|
|
|
@@ -68,9 +83,27 @@ export class FocusableGroup extends BaseFocusable<Props> {
|
|
|
68
83
|
onGroupBlur={onGroupBlur}
|
|
69
84
|
style={style}
|
|
70
85
|
{...otherProps}
|
|
86
|
+
onRegistered={this.onRegistered}
|
|
71
87
|
>
|
|
72
88
|
{children}
|
|
73
89
|
</FocusableGroupNative>
|
|
74
90
|
);
|
|
75
91
|
}
|
|
76
92
|
}
|
|
93
|
+
|
|
94
|
+
export const withFocusDisabled = (Component) => {
|
|
95
|
+
return function WithFocusDisabled(props) {
|
|
96
|
+
// @ts-ignore
|
|
97
|
+
const { screenFocusBlocked } = React.useContext(LayoutContext.ReactContext);
|
|
98
|
+
|
|
99
|
+
const { pathname } = useRoute();
|
|
100
|
+
|
|
101
|
+
const isPlayerPresented = isScreenPlayable(pathname);
|
|
102
|
+
|
|
103
|
+
const blockScreenFocus = isPlayerPresented === false && screenFocusBlocked;
|
|
104
|
+
|
|
105
|
+
return <Component {...props} isFocusDisabled={blockScreenFocus} />;
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export const FocusableGroup = withFocusDisabled(FocusableGroupComponent);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { GeneralContentScreen } from "./GeneralContentScreen";
|
|
3
|
+
import { HookContentFocusGroup } from "./hookFocus";
|
|
4
|
+
import { runInBackground } from "./hookAdapter/runInBackground";
|
|
5
|
+
import { log_error } from "./hookAdapter/logger";
|
|
6
|
+
|
|
7
|
+
type HookComponentProps = HookPluginProps & {
|
|
8
|
+
configuration?: unknown;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function GeneralContentScreenHookComponent(props: HookComponentProps) {
|
|
12
|
+
const { hookPlugin, focused, parentFocus } = props;
|
|
13
|
+
|
|
14
|
+
if (!hookPlugin?.screen_id) {
|
|
15
|
+
log_error(
|
|
16
|
+
"GeneralContentScreenHookAdapter: This component should only be used as a hook, but no hookPlugin was provided."
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<HookContentFocusGroup>
|
|
24
|
+
<GeneralContentScreen
|
|
25
|
+
screenId={hookPlugin.screen_id}
|
|
26
|
+
isScreenWrappedInContainer={false}
|
|
27
|
+
focused={focused}
|
|
28
|
+
parentFocus={parentFocus}
|
|
29
|
+
/>
|
|
30
|
+
</HookContentFocusGroup>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const GeneralContentScreenHookAdapter = {
|
|
35
|
+
isFlowBlocker: () => true,
|
|
36
|
+
presentFullScreen: true,
|
|
37
|
+
Component: GeneralContentScreenHookComponent,
|
|
38
|
+
runInBackground,
|
|
39
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { GeneralContentScreenHookAdapter } from "../GeneralContentScreenHookAdapter";
|
|
4
|
+
import { log_error } from "../hookAdapter/logger";
|
|
5
|
+
|
|
6
|
+
const mockScreenSpy = jest.fn();
|
|
7
|
+
|
|
8
|
+
jest.mock("../GeneralContentScreen", () => ({
|
|
9
|
+
GeneralContentScreen: (props) => {
|
|
10
|
+
const React = require("react");
|
|
11
|
+
const { View } = require("react-native");
|
|
12
|
+
|
|
13
|
+
mockScreenSpy(props);
|
|
14
|
+
|
|
15
|
+
return <View testID="general-content-screen" />;
|
|
16
|
+
},
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
jest.mock("../hookAdapter/runInBackground", () => ({
|
|
20
|
+
runInBackground: jest.fn(),
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock("../hookAdapter/logger", () => ({
|
|
24
|
+
log_debug: jest.fn(),
|
|
25
|
+
log_error: jest.fn(),
|
|
26
|
+
log_info: jest.fn(),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
const { Component } = GeneralContentScreenHookAdapter;
|
|
30
|
+
|
|
31
|
+
describe("GeneralContentScreenHookAdapter", () => {
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
jest.clearAllMocks();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("renders GeneralContentScreen with the hook plugin screen id", () => {
|
|
37
|
+
const { getByTestId } = render(
|
|
38
|
+
<Component hookPlugin={{ screen_id: "screen-1" }} focused />
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
expect(getByTestId("general-content-screen")).toBeDefined();
|
|
42
|
+
|
|
43
|
+
expect(mockScreenSpy).toHaveBeenCalledWith(
|
|
44
|
+
expect.objectContaining({
|
|
45
|
+
screenId: "screen-1",
|
|
46
|
+
isScreenWrappedInContainer: false,
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("renders nothing and logs an error when hookPlugin is missing", () => {
|
|
52
|
+
const { toJSON } = render(<Component />);
|
|
53
|
+
|
|
54
|
+
expect(toJSON()).toBeNull();
|
|
55
|
+
expect(mockScreenSpy).not.toHaveBeenCalled();
|
|
56
|
+
expect(log_error).toHaveBeenCalled();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("renders nothing when hookPlugin has no screen_id", () => {
|
|
60
|
+
const { toJSON } = render(<Component hookPlugin={{}} />);
|
|
61
|
+
|
|
62
|
+
expect(toJSON()).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
|
|
5
|
+
import { HookContentFocusGroup } from "../hookFocus/index.web";
|
|
6
|
+
|
|
7
|
+
const mockFocusableGroupSpy = jest.fn();
|
|
8
|
+
const mockUseInitialFocusSpy = jest.fn();
|
|
9
|
+
|
|
10
|
+
const mockModalState = { isRunningInBackground: false };
|
|
11
|
+
|
|
12
|
+
jest.mock("../../FocusableGroup", () => ({
|
|
13
|
+
FocusableGroup: (props) => {
|
|
14
|
+
const React = require("react");
|
|
15
|
+
const { View } = require("react-native");
|
|
16
|
+
|
|
17
|
+
mockFocusableGroupSpy(props);
|
|
18
|
+
|
|
19
|
+
return <View testID="focusable-group">{props.children}</View>;
|
|
20
|
+
},
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
|
|
24
|
+
useContentId: () => "quick-brick-content___route",
|
|
25
|
+
useNavbarId: () => "quick-brick-navbar___route",
|
|
26
|
+
usePathname: () => "hooks-modal/profile-select",
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
jest.mock("../../Screen/TV/hooks", () => ({
|
|
30
|
+
useInitialFocus: () => mockUseInitialFocusSpy(),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
jest.mock("../../../Contexts/ZappHookModalContext", () => ({
|
|
34
|
+
useZappHookModalStore: (selector) => selector(mockModalState),
|
|
35
|
+
}));
|
|
36
|
+
|
|
37
|
+
describe("HookContentFocusGroup (web)", () => {
|
|
38
|
+
beforeEach(() => {
|
|
39
|
+
jest.clearAllMocks();
|
|
40
|
+
mockModalState.isRunningInBackground = false;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("wraps content in a preferred-focus content group and triggers initial focus when presented full screen", () => {
|
|
44
|
+
const { getByTestId } = render(
|
|
45
|
+
<HookContentFocusGroup>
|
|
46
|
+
<View testID="child" />
|
|
47
|
+
</HookContentFocusGroup>
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(getByTestId("focusable-group")).toBeDefined();
|
|
51
|
+
expect(getByTestId("child")).toBeDefined();
|
|
52
|
+
expect(mockUseInitialFocusSpy).toHaveBeenCalledTimes(1);
|
|
53
|
+
|
|
54
|
+
expect(mockFocusableGroupSpy).toHaveBeenCalledWith(
|
|
55
|
+
expect.objectContaining({
|
|
56
|
+
id: "quick-brick-content___route",
|
|
57
|
+
groupId: "hooks-modal/profile-select",
|
|
58
|
+
nextFocusUp: "quick-brick-navbar___route",
|
|
59
|
+
preferredFocus: true,
|
|
60
|
+
shouldUsePreferredFocus: true,
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("injects the content group id into the wrapped child so its cells register under it", () => {
|
|
66
|
+
render(
|
|
67
|
+
<HookContentFocusGroup>
|
|
68
|
+
<View testID="child" />
|
|
69
|
+
</HookContentFocusGroup>
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const child = mockFocusableGroupSpy.mock.calls[0][0].children;
|
|
73
|
+
|
|
74
|
+
expect(child.props.groupId).toBe("quick-brick-content___route");
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("renders the child without focus handling when running in background (not full screen)", () => {
|
|
78
|
+
mockModalState.isRunningInBackground = true;
|
|
79
|
+
|
|
80
|
+
const { getByTestId, queryByTestId } = render(
|
|
81
|
+
<HookContentFocusGroup>
|
|
82
|
+
<View testID="child" />
|
|
83
|
+
</HookContentFocusGroup>
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
expect(getByTestId("child")).toBeDefined();
|
|
87
|
+
expect(queryByTestId("focusable-group")).toBeNull();
|
|
88
|
+
expect(mockUseInitialFocusSpy).not.toHaveBeenCalled();
|
|
89
|
+
expect(mockFocusableGroupSpy).not.toHaveBeenCalled();
|
|
90
|
+
});
|
|
91
|
+
});
|