@applicaster/zapp-react-native-ui-components 15.1.0-rc.1 → 16.0.0-rc.1
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.ios.ts +12 -2
- package/Components/Cell/FocusableWrapper.tsx +3 -0
- package/Components/Cell/TvOSCellComponent.tsx +6 -3
- 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 +30 -1
- package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
- package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
- package/Components/Layout/TV/LayoutBackground.tsx +5 -2
- 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/index.tsx +3 -4
- package/Components/Layout/TV/index.web.tsx +3 -4
- package/Components/LinkHandler/LinkHandler.tsx +2 -2
- package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
- 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 +14 -13
- 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/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- 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 +12 -9
- 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/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
- package/Helpers/DataSourceHelper/index.ts +19 -0
- package/package.json +5 -5
- 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/DataSourceHelper/index.js +0 -19
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { path } from "ramda";
|
|
3
2
|
|
|
4
3
|
import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
5
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
6
5
|
import { extractAsset } from "./utils";
|
|
7
6
|
|
|
8
7
|
type Return = { uri: string } | null;
|
|
8
|
+
type Source = { context?: string; uri?: string } | null | undefined;
|
|
9
9
|
|
|
10
|
-
const getSourceContext =
|
|
11
|
-
const getSourceUri =
|
|
12
|
-
const getState = path(["state"]);
|
|
10
|
+
const getSourceContext = (source: Source) => source?.context;
|
|
11
|
+
const getSourceUri = (source: Source) => source?.uri;
|
|
13
12
|
|
|
14
|
-
export const useImageSource = ({
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
export const useImageSource = ({
|
|
14
|
+
uri,
|
|
15
|
+
entry,
|
|
16
|
+
otherProps: { source, state: uriState },
|
|
17
|
+
}): Return => {
|
|
18
|
+
const uriContext = getSourceContext(source);
|
|
17
19
|
|
|
18
20
|
const action = useActions(uriContext);
|
|
19
21
|
|
|
@@ -38,7 +40,7 @@ export const useImageSource = ({ uri, entry, otherProps }): Return => {
|
|
|
38
40
|
return { uri };
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
const uriFromSource = getSourceUri(
|
|
43
|
+
const uriFromSource = getSourceUri(source);
|
|
42
44
|
|
|
43
45
|
if (uriFromSource) {
|
|
44
46
|
return { uri: uriFromSource };
|
|
@@ -47,7 +49,7 @@ export const useImageSource = ({ uri, entry, otherProps }): Return => {
|
|
|
47
49
|
return null;
|
|
48
50
|
};
|
|
49
51
|
|
|
50
|
-
const getSource = (uri, showDefault, placeholderImage,
|
|
52
|
+
const getSource = (uri, showDefault, placeholderImage, source) => {
|
|
51
53
|
const placeholderName = placeholderImage || "";
|
|
52
54
|
|
|
53
55
|
const defaultPath = {
|
|
@@ -60,7 +62,7 @@ const getSource = (uri, showDefault, placeholderImage, otherProps) => {
|
|
|
60
62
|
return { uri };
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
const uriFromSource = getSourceUri(
|
|
65
|
+
const uriFromSource = getSourceUri(source);
|
|
64
66
|
|
|
65
67
|
if (uriFromSource) {
|
|
66
68
|
return { uri: uriFromSource };
|
|
@@ -74,10 +76,9 @@ export const useImageSourceWithDefault = ({
|
|
|
74
76
|
entry,
|
|
75
77
|
showDefault,
|
|
76
78
|
placeholderImage,
|
|
77
|
-
otherProps,
|
|
79
|
+
otherProps: { state: uriState, source },
|
|
78
80
|
}): Return => {
|
|
79
|
-
const uriContext = getSourceContext(
|
|
80
|
-
const uriState = getState(otherProps);
|
|
81
|
+
const uriContext = getSourceContext(source);
|
|
81
82
|
|
|
82
83
|
const action = useActions(uriContext);
|
|
83
84
|
|
|
@@ -98,5 +99,5 @@ export const useImageSourceWithDefault = ({
|
|
|
98
99
|
return extractAsset(!isTV(), entryStateLocal.asset, uriState);
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
return getSource(uri, showDefault, placeholderImage,
|
|
102
|
+
return getSource(uri, showDefault, placeholderImage, source);
|
|
102
103
|
};
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import merge from "
|
|
3
|
-
import { clone } from "@applicaster/zapp-react-native-utils/utils";
|
|
2
|
+
import { merge, clone } from "@applicaster/zapp-react-native-utils/utils";
|
|
4
3
|
import { Platform, View, ViewStyle } from "react-native";
|
|
5
4
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
6
5
|
import { isTV, isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TouchableOpacity } from "react-native";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
style?: Record<string, unknown>;
|
|
7
|
+
testID?: string;
|
|
8
|
+
accessibilityLabel?: string;
|
|
9
|
+
accessibilityHint?: string;
|
|
10
|
+
onPress?: () => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function PressableView({
|
|
14
|
+
children,
|
|
15
|
+
style = {},
|
|
16
|
+
testID,
|
|
17
|
+
accessibilityLabel,
|
|
18
|
+
accessibilityHint,
|
|
19
|
+
onPress,
|
|
20
|
+
}: Props) {
|
|
21
|
+
return (
|
|
22
|
+
<TouchableOpacity
|
|
23
|
+
activeOpacity={1}
|
|
24
|
+
onPress={onPress}
|
|
25
|
+
testID={testID}
|
|
26
|
+
accessibilityLabel={accessibilityLabel}
|
|
27
|
+
accessibilityHint={accessibilityHint}
|
|
28
|
+
accessible={!!(testID || accessibilityLabel)}
|
|
29
|
+
style={style}
|
|
30
|
+
>
|
|
31
|
+
{children}
|
|
32
|
+
</TouchableOpacity>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderHook } from "@testing-library/react-
|
|
1
|
+
import { renderHook, waitFor } from "@testing-library/react-native";
|
|
2
2
|
import { Image } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { useGetImageDimensions } from "../useGetImageDimensions";
|
|
@@ -13,15 +13,16 @@ jest.spyOn(Image, "getSize").mockImplementation((_uri, success) => {
|
|
|
13
13
|
|
|
14
14
|
describe("useGetImageDimensions", () => {
|
|
15
15
|
it("should return aspect ration initially when known dimensions", async () => {
|
|
16
|
-
const { result
|
|
16
|
+
const { result } = renderHook(() =>
|
|
17
17
|
useGetImageDimensions("https://some_url.com", WIDTH, undefined)
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
expect(result.current).toBeUndefined();
|
|
21
|
-
await waitForNextUpdate();
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
await waitFor(() => {
|
|
23
|
+
expect(result.current).toEqual(
|
|
24
|
+
getDimension({ width: WIDTH, height: HEIGTH })
|
|
25
|
+
);
|
|
26
|
+
});
|
|
26
27
|
});
|
|
27
28
|
});
|
|
@@ -45,6 +45,7 @@ export const useTextLabel = ({ label, entry }): string => {
|
|
|
45
45
|
const [entryStateLocal, setEntryStateLocal] =
|
|
46
46
|
React.useState(initialEntryState);
|
|
47
47
|
|
|
48
|
+
// For favourites
|
|
48
49
|
React.useEffect(() => {
|
|
49
50
|
return action?.addListeners?.(({ entryState, entry: actionEntry }) => {
|
|
50
51
|
if (entry.id === actionEntry.id) {
|
|
@@ -53,6 +54,16 @@ export const useTextLabel = ({ label, entry }): string => {
|
|
|
53
54
|
});
|
|
54
55
|
}, []);
|
|
55
56
|
|
|
57
|
+
// For rest actions
|
|
58
|
+
React.useEffect(() => {
|
|
59
|
+
// Update entryStateLocal when action state changes. Example: state change when pressing the download button.
|
|
60
|
+
if (typeof action?.addListener === "function") {
|
|
61
|
+
return action.addListener(String(entry?.id), (nextState) => {
|
|
62
|
+
setEntryStateLocal(nextState);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}, []);
|
|
66
|
+
|
|
56
67
|
if (context && name && action) {
|
|
57
68
|
return prepareHebrewText(extractLabel(entryStateLocal.label, name), isRTL);
|
|
58
69
|
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { render } from "@testing-library/react-native";
|
|
3
|
+
|
|
4
|
+
import { defaultComponents } from "../index";
|
|
5
|
+
import { elementMapper } from "../../elementMapper";
|
|
6
|
+
|
|
7
|
+
const WrapperProbe = jest.fn(({ children }) => <>{children}</>);
|
|
8
|
+
const LeafProbe = jest.fn(() => null);
|
|
9
|
+
|
|
10
|
+
const components = {
|
|
11
|
+
...defaultComponents,
|
|
12
|
+
View: WrapperProbe,
|
|
13
|
+
Text: LeafProbe,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const entry = { id: "entry-1" };
|
|
17
|
+
const item = { id: "item-1" };
|
|
18
|
+
|
|
19
|
+
const renderNode = (node) =>
|
|
20
|
+
render(
|
|
21
|
+
<React.Fragment>{elementMapper(components)(node as never)}</React.Fragment>
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
describe("DataProvider", () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
jest.clearAllMocks();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("injects resolved runtime props under dataProviderProps into the direct child and its direct children only", () => {
|
|
30
|
+
renderNode({
|
|
31
|
+
type: "DataProvider",
|
|
32
|
+
props: { _dataKey: "entry", entry },
|
|
33
|
+
elements: [
|
|
34
|
+
{
|
|
35
|
+
type: "View",
|
|
36
|
+
props: {},
|
|
37
|
+
elements: [
|
|
38
|
+
{
|
|
39
|
+
type: "View",
|
|
40
|
+
props: {},
|
|
41
|
+
elements: [{ type: "Text", props: {} }],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
expect(WrapperProbe).toHaveBeenNthCalledWith(
|
|
49
|
+
1,
|
|
50
|
+
expect.objectContaining({
|
|
51
|
+
dataProviderProps: expect.objectContaining({ entry }),
|
|
52
|
+
}),
|
|
53
|
+
expect.anything()
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
expect(WrapperProbe).toHaveBeenNthCalledWith(
|
|
57
|
+
2,
|
|
58
|
+
expect.objectContaining({
|
|
59
|
+
dataProviderProps: expect.objectContaining({ entry }),
|
|
60
|
+
}),
|
|
61
|
+
expect.anything()
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(LeafProbe).toHaveBeenCalledWith(
|
|
65
|
+
expect.not.objectContaining({
|
|
66
|
+
dataProviderProps: expect.anything(),
|
|
67
|
+
}),
|
|
68
|
+
expect.anything()
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("derives the namespaced values from the resolved runtime props", () => {
|
|
73
|
+
renderNode({
|
|
74
|
+
type: "DataProvider",
|
|
75
|
+
props: { _dataKey: "item", item },
|
|
76
|
+
elements: [
|
|
77
|
+
{
|
|
78
|
+
type: "View",
|
|
79
|
+
props: {},
|
|
80
|
+
elements: [{ type: "View", props: {} }],
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
expect(WrapperProbe).toHaveBeenNthCalledWith(
|
|
86
|
+
1,
|
|
87
|
+
expect.objectContaining({
|
|
88
|
+
dataProviderProps: expect.objectContaining({ item }),
|
|
89
|
+
}),
|
|
90
|
+
expect.anything()
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
expect(WrapperProbe).toHaveBeenNthCalledWith(
|
|
94
|
+
2,
|
|
95
|
+
expect.objectContaining({
|
|
96
|
+
dataProviderProps: expect.objectContaining({ item }),
|
|
97
|
+
}),
|
|
98
|
+
expect.anything()
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("does not overwrite explicit dataProviderProps on the direct child", () => {
|
|
103
|
+
const explicitEntry = { id: "explicit" };
|
|
104
|
+
|
|
105
|
+
renderNode({
|
|
106
|
+
type: "DataProvider",
|
|
107
|
+
props: { _dataKey: "entry", entry },
|
|
108
|
+
elements: [
|
|
109
|
+
{
|
|
110
|
+
type: "View",
|
|
111
|
+
props: {
|
|
112
|
+
dataProviderProps: {
|
|
113
|
+
entry: explicitEntry,
|
|
114
|
+
source: "child",
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
elements: [{ type: "View", props: {} }],
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(WrapperProbe).toHaveBeenNthCalledWith(
|
|
123
|
+
1,
|
|
124
|
+
expect.objectContaining({
|
|
125
|
+
dataProviderProps: expect.objectContaining({
|
|
126
|
+
entry: explicitEntry,
|
|
127
|
+
source: "child",
|
|
128
|
+
}),
|
|
129
|
+
}),
|
|
130
|
+
expect.anything()
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
expect(WrapperProbe).toHaveBeenNthCalledWith(
|
|
134
|
+
2,
|
|
135
|
+
expect.objectContaining({
|
|
136
|
+
dataProviderProps: expect.objectContaining({ entry }),
|
|
137
|
+
}),
|
|
138
|
+
expect.anything()
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -6,26 +6,29 @@ import PureImage from "./Image";
|
|
|
6
6
|
import { CollapsibleTextContainer } from "../SharedUI/CollapsibleTextContainer/CollapsibleTextContainer";
|
|
7
7
|
import { Toggle } from "./Toggle";
|
|
8
8
|
import { Button } from "./Button";
|
|
9
|
-
import { FocusableView } from "./FocusableView";
|
|
10
9
|
import ProgressBar from "../SharedUI/ProgressBar/ProgressBar";
|
|
11
10
|
import { LiveImage } from "./LiveImage";
|
|
12
11
|
import { DynamicBadge } from "./DynamicBadge";
|
|
13
12
|
import { SecondaryImage } from "./SecondaryImage/Image";
|
|
14
13
|
import { BorderContainerView } from "./BorderContainerView";
|
|
15
14
|
import { CellBadge } from "./CellBadge";
|
|
15
|
+
import { DataProvider } from "./DataProvider";
|
|
16
16
|
import { TvActionButtons } from "./tv/TvActionButtons";
|
|
17
|
-
import {
|
|
17
|
+
import { TvActionButton } from "./tv/TvActionButtons/TvActionButton";
|
|
18
|
+
import { ButtonContainerView } from "./ButtonContainerView";
|
|
18
19
|
import { ImageBorderContainer } from "./ImageBorderContainer";
|
|
20
|
+
import { PressableView } from "./PressableView";
|
|
21
|
+
import { ActionButton as MobileActionButton } from "./mobile/MobileActionButtons/ActionButton";
|
|
19
22
|
|
|
20
23
|
export const defaultComponents = {
|
|
21
24
|
View,
|
|
22
25
|
CollapsibleTextContainer,
|
|
23
26
|
Text,
|
|
24
27
|
Button,
|
|
28
|
+
PressableView,
|
|
25
29
|
Image,
|
|
26
30
|
PureImage,
|
|
27
31
|
ButtonContainerView,
|
|
28
|
-
FocusableView,
|
|
29
32
|
ProgressBar,
|
|
30
33
|
Toggle,
|
|
31
34
|
LiveImage,
|
|
@@ -34,5 +37,8 @@ export const defaultComponents = {
|
|
|
34
37
|
DynamicBadge,
|
|
35
38
|
SecondaryImage,
|
|
36
39
|
TvActionButtons,
|
|
40
|
+
TvActionButton,
|
|
41
|
+
MobileActionButton,
|
|
37
42
|
CellBadge,
|
|
43
|
+
DataProvider,
|
|
38
44
|
};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { PressableView } from "../../PressableView";
|
|
3
|
+
|
|
4
|
+
import { ActionButtonController } from "../../ActionButtonsCore/components";
|
|
5
|
+
|
|
6
|
+
import { masterCellLogger } from "../../../logger";
|
|
7
|
+
import { resolveLabelText, selectByAssetFlavour } from "./utils";
|
|
8
|
+
|
|
9
|
+
type ChildElementProps = {
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
mobileActionRole?: "asset" | "label" | "label_container";
|
|
12
|
+
uri?: string;
|
|
13
|
+
asset?: React.ReactNode;
|
|
14
|
+
state?: "default" | "focused";
|
|
15
|
+
entry?: any;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const isValidElement = (
|
|
19
|
+
child: React.ReactNode
|
|
20
|
+
): child is React.ReactElement<ChildElementProps> =>
|
|
21
|
+
React.isValidElement(child);
|
|
22
|
+
|
|
23
|
+
export const ActionButton = ({ style, children, action, entry, ...props }) => {
|
|
24
|
+
return (
|
|
25
|
+
<ActionButtonController
|
|
26
|
+
action={action}
|
|
27
|
+
entry={entry}
|
|
28
|
+
onMissingActionContext={(identifier) => {
|
|
29
|
+
masterCellLogger.warning(
|
|
30
|
+
`You're missing an action plugin(${identifier || action?.identifier}) required by your mobile action button.`
|
|
31
|
+
);
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
{({ actionContext, actionState, isActive, onPress }) => {
|
|
35
|
+
const supportsEntryState =
|
|
36
|
+
typeof actionContext?.initialEntryState === "function";
|
|
37
|
+
|
|
38
|
+
const shouldRenderAsset = Boolean(
|
|
39
|
+
supportsEntryState ? actionState?.mobileButtonAssets : false
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const shouldRenderLabel = Boolean(
|
|
43
|
+
supportsEntryState && resolveLabelText(actionState?.label)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const _cloneChildrenWithState = (
|
|
47
|
+
nodes?: React.ReactNode
|
|
48
|
+
): React.ReactNode => {
|
|
49
|
+
return React.Children.map(nodes, (child) => {
|
|
50
|
+
if (!isValidElement(child)) {
|
|
51
|
+
return child;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const role = child.props.mobileActionRole;
|
|
55
|
+
|
|
56
|
+
const nextChildren = _cloneChildrenWithState(child.props.children);
|
|
57
|
+
|
|
58
|
+
// Prevents of the asset rendering when the action doesn't provide asset for current entry state
|
|
59
|
+
if (role === "asset" && !shouldRenderAsset) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Prevents of the label rendering when the action doesn't provide label for current entry state.
|
|
64
|
+
// Also prevents of the label container when it doesn't have children to avoid unnecessary empty space.
|
|
65
|
+
if (role === "label" && !shouldRenderLabel) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Prevents of the label container rendering when it doesn't have children to avoid unnecessary empty space.
|
|
70
|
+
if (
|
|
71
|
+
role === "label_container" &&
|
|
72
|
+
React.Children.count(nextChildren) === 0
|
|
73
|
+
) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const nextProps: Partial<ChildElementProps> = {};
|
|
78
|
+
|
|
79
|
+
// Inject asset or uri to Asset component with role asset
|
|
80
|
+
if (role === "asset") {
|
|
81
|
+
const resolvedAsset = selectByAssetFlavour(
|
|
82
|
+
actionState,
|
|
83
|
+
action.flavour,
|
|
84
|
+
isActive
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (typeof resolvedAsset === "function") {
|
|
88
|
+
const AssetComponent =
|
|
89
|
+
resolvedAsset as CellActionAssetComponent;
|
|
90
|
+
|
|
91
|
+
nextProps.asset = (
|
|
92
|
+
<AssetComponent
|
|
93
|
+
flavour={action.flavour || "flavour_1"}
|
|
94
|
+
width={action.width}
|
|
95
|
+
height={action.height}
|
|
96
|
+
/>
|
|
97
|
+
);
|
|
98
|
+
} else {
|
|
99
|
+
nextProps.uri = resolvedAsset;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Inject state and entry to Text component with role label
|
|
104
|
+
if (role === "label") {
|
|
105
|
+
nextProps.state = isActive ? "focused" : "default";
|
|
106
|
+
nextProps.entry = entry;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (nextChildren !== child.props.children) {
|
|
110
|
+
nextProps.children = nextChildren;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return React.cloneElement(child, nextProps);
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
if (!shouldRenderAsset && !shouldRenderLabel) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<PressableView
|
|
123
|
+
testID={props.testID || `${entry?.id}`}
|
|
124
|
+
style={isActive ? { ...style, ...props.focusedStyles } : style}
|
|
125
|
+
onPress={onPress}
|
|
126
|
+
accessibilityLabel={props.accessibilityLabel || `${entry?.id}`}
|
|
127
|
+
accessibilityHint={props.accessibilityHint}
|
|
128
|
+
>
|
|
129
|
+
{_cloneChildrenWithState(children)}
|
|
130
|
+
</PressableView>
|
|
131
|
+
);
|
|
132
|
+
}}
|
|
133
|
+
</ActionButtonController>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { AssetComponent } from "./AssetComponent";
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
testID?: string;
|
|
5
|
+
style?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type AssetNode = {
|
|
9
|
+
type: "ReactComponent";
|
|
10
|
+
style?: Record<string, unknown>;
|
|
11
|
+
additionalProps: {
|
|
12
|
+
component: typeof AssetComponent;
|
|
13
|
+
requiresCellUUID: true;
|
|
14
|
+
renderChildren: false;
|
|
15
|
+
mobileActionRole: "asset";
|
|
16
|
+
testID?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Asset used in conjunction with ActionButton inside mobile action buttons, uri is provided by ActionButton */
|
|
21
|
+
export const Asset = ({ testID, style }: Props): AssetNode => {
|
|
22
|
+
return {
|
|
23
|
+
type: "ReactComponent",
|
|
24
|
+
style,
|
|
25
|
+
additionalProps: {
|
|
26
|
+
component: AssetComponent,
|
|
27
|
+
requiresCellUUID: true,
|
|
28
|
+
renderChildren: false,
|
|
29
|
+
mobileActionRole: "asset",
|
|
30
|
+
testID: testID ? `${testID}-asset` : undefined,
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { View } from "react-native";
|
|
3
|
+
import { ImageContainer } from "../../ImageContainer";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
style?: Record<string, unknown>;
|
|
7
|
+
uri?: string;
|
|
8
|
+
asset?: React.ReactNode;
|
|
9
|
+
testID?: string;
|
|
10
|
+
cellUUID?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const AssetComponent = (props: Props & Record<string, unknown>) => {
|
|
14
|
+
return props.asset && React.isValidElement(props.asset) ? (
|
|
15
|
+
<View style={props.style} testID={props.testID}>
|
|
16
|
+
{React.cloneElement(props.asset, { cellUUID: props.cellUUID })}
|
|
17
|
+
</View>
|
|
18
|
+
) : (
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
<ImageContainer {...props} uri={props.uri} />
|
|
21
|
+
);
|
|
22
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
|
|
2
|
+
import { compact } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
3
|
+
|
|
4
|
+
import { Asset } from "./Asset";
|
|
5
|
+
import { TextLabelsContainer } from "./TextLabelsContainer";
|
|
6
|
+
import {
|
|
7
|
+
getAssetStyles,
|
|
8
|
+
getContentDirection,
|
|
9
|
+
getContentsAlignment,
|
|
10
|
+
getPressableStyles,
|
|
11
|
+
getTextLabelStyles,
|
|
12
|
+
} from "./utils";
|
|
13
|
+
|
|
14
|
+
const displayModeStyle = (value) => {
|
|
15
|
+
const mode = value("display_mode") || "dynamic";
|
|
16
|
+
|
|
17
|
+
if (mode === "fixed") {
|
|
18
|
+
return {
|
|
19
|
+
width: toNumberWithDefault(140, value("width")),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (mode === "fill") {
|
|
24
|
+
return {
|
|
25
|
+
flex: 1,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return {};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
type Props = {
|
|
33
|
+
index: number;
|
|
34
|
+
value: Function;
|
|
35
|
+
stylePrefix: string;
|
|
36
|
+
specificPrefix: string;
|
|
37
|
+
spacingStyle: Record<string, unknown>;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const Button = ({
|
|
41
|
+
index,
|
|
42
|
+
value,
|
|
43
|
+
stylePrefix,
|
|
44
|
+
specificPrefix,
|
|
45
|
+
spacingStyle,
|
|
46
|
+
}: Props) => {
|
|
47
|
+
// Retrives values depending to the slot
|
|
48
|
+
const getSlotBasedValue = (property: string) =>
|
|
49
|
+
value(`${specificPrefix}_${property}`);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Retrieves values depending to the independ_style toggle:
|
|
53
|
+
* (e.g for button value button_2_label_enabled returns button_1_label_enabled when independent_style: false)
|
|
54
|
+
*/
|
|
55
|
+
const getValue = (property: string) => value(`${stylePrefix}_${property}`);
|
|
56
|
+
|
|
57
|
+
const assetEnabled = getValue("asset_enabled");
|
|
58
|
+
const labelEnabled = getValue("label_enabled");
|
|
59
|
+
|
|
60
|
+
if (!getSlotBasedValue("button_enabled")) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!assetEnabled && !labelEnabled) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const testID = `mobile_action_button_${index + 1}`;
|
|
69
|
+
const actionIdentifier = getSlotBasedValue("assign_action");
|
|
70
|
+
const assetAlignment = getValue("asset_alignment") || "left";
|
|
71
|
+
const actionAssetFlavour = getValue("action_asset_flavour");
|
|
72
|
+
const contentsAlignment = getValue("contents_alignment") || "center";
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
type: "MobileActionButton",
|
|
76
|
+
style: {
|
|
77
|
+
flexDirection: getContentDirection(assetAlignment),
|
|
78
|
+
alignContent: "center",
|
|
79
|
+
alignItems: getContentsAlignment(contentsAlignment, assetAlignment),
|
|
80
|
+
justifyContent: getContentsAlignment(contentsAlignment, assetAlignment),
|
|
81
|
+
...getPressableStyles(getValue),
|
|
82
|
+
...displayModeStyle(getValue),
|
|
83
|
+
...spacingStyle,
|
|
84
|
+
},
|
|
85
|
+
additionalProps: {
|
|
86
|
+
action: {
|
|
87
|
+
identifier: actionIdentifier,
|
|
88
|
+
flavour: actionAssetFlavour,
|
|
89
|
+
width: toNumberWithDefault(24, getValue("asset_width")),
|
|
90
|
+
height: toNumberWithDefault(24, getValue("asset_height")),
|
|
91
|
+
},
|
|
92
|
+
focusedStyles: {
|
|
93
|
+
backgroundColor: getValue("focused_background_color"),
|
|
94
|
+
borderColor: getValue("focused_border_color"),
|
|
95
|
+
},
|
|
96
|
+
testID,
|
|
97
|
+
},
|
|
98
|
+
elements: compact([
|
|
99
|
+
assetEnabled
|
|
100
|
+
? Asset({
|
|
101
|
+
style: getAssetStyles(getValue),
|
|
102
|
+
testID,
|
|
103
|
+
})
|
|
104
|
+
: null,
|
|
105
|
+
|
|
106
|
+
labelEnabled
|
|
107
|
+
? TextLabelsContainer({
|
|
108
|
+
style: getTextLabelStyles(getValue),
|
|
109
|
+
extraProps: {
|
|
110
|
+
normalStyles: {
|
|
111
|
+
color: getValue("font_color"),
|
|
112
|
+
},
|
|
113
|
+
focusedStyles: {
|
|
114
|
+
color: getValue("focused_font_color"),
|
|
115
|
+
},
|
|
116
|
+
transformText: getValue("text_transform") || "default",
|
|
117
|
+
numberOfLines: getValue("number_of_lines"),
|
|
118
|
+
},
|
|
119
|
+
actionIdentifier,
|
|
120
|
+
testID,
|
|
121
|
+
})
|
|
122
|
+
: null,
|
|
123
|
+
]),
|
|
124
|
+
};
|
|
125
|
+
};
|