@applicaster/zapp-react-native-ui-components 15.1.4 → 16.0.0-alpha.1042942411
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/index.ts +1 -8
- 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/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/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/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/ActionButton.tsx +27 -48
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +213 -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 +198 -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/__tests__/index.test.tsx +217 -46
- package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +50 -119
- package/Components/MasterCell/DefaultComponents/Button.tsx +50 -58
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.android.tv.tsx +58 -0
- package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -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/CellBadge/__tests__/__snapshots__/index.test.tsx.snap +18 -0
- package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/index.test.tsx +90 -0
- package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +13 -14
- 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/ImageContainer/index.tsx +5 -3
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
- package/Components/MasterCell/DefaultComponents/PressableView.tsx +54 -0
- package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +46 -50
- package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +47 -39
- package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
- package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +19 -6
- package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +15 -0
- package/Components/MasterCell/DefaultComponents/Text/index.tsx +45 -15
- package/Components/MasterCell/DefaultComponents/UIKitAdapters/Badge.tsx +49 -0
- package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +122 -0
- package/Components/MasterCell/DefaultComponents/__tests__/Button.test.tsx +186 -0
- package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
- package/Components/MasterCell/DefaultComponents/__tests__/DynamicBadge.test.tsx +156 -0
- package/Components/MasterCell/DefaultComponents/__tests__/__snapshots__/ActionButton.test.tsx.snap +60 -0
- package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +196 -67
- package/Components/MasterCell/DefaultComponents/index.ts +13 -3
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +189 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +33 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +159 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +18 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +39 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +402 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +354 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +129 -0
- package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +123 -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/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
- 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/contexts/PressedStateContext.ts +3 -0
- package/Components/MasterCell/dataAdapter.ts +4 -1
- package/Components/MasterCell/elementMapper.tsx +54 -7
- package/Components/MasterCell/hooks/index.ts +2 -0
- package/Components/MasterCell/hooks/usePressedState.ts +4 -0
- package/Components/MasterCell/index.tsx +20 -4
- 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 +82 -16
- package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -69
- package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +25 -47
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +122 -39
- package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +671 -0
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +257 -88
- package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
- package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
- package/Components/ModalComponent/BottomSheetModalContent.tsx +17 -10
- package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
- package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
- package/Components/OfflineHandler/__tests__/index.test.tsx +101 -32
- package/Components/OfflineHandler/hooks.ts +123 -0
- package/Components/OfflineHandler/index.tsx +51 -73
- package/Components/OfflineHandler/utils/index.ts +1 -13
- package/Components/PlayerContainer/PlayerContainer.tsx +6 -5
- package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +2 -4
- package/Components/PreloaderWrapper/index.tsx +1 -1
- package/Components/River/ComponentsMap/ComponentsMap.tsx +8 -21
- package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
- package/Components/River/RefreshControl.tsx +19 -105
- 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 +4 -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/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
- package/Components/ScreenResolver/index.tsx +9 -5
- package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
- package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
- package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
- package/Components/ScreenRevealManager/withScreenRevealManager.tsx +1 -1
- 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 -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/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
- package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
- 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/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
- package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
- package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
- package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -67
- package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
- package/Components/River/TV/withTVEventHandler.tsx +0 -36
- package/Helpers/DataSourceHelper/index.js +0 -19
- /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
- /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
- /package/Components/BackgroundImage/{BackgroundImage.tv.web.tsx → BackgroundImage.tsx} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
|
|
3
3
|
import { useNavigation } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
4
|
+
import { selectAppReady } from "@applicaster/zapp-react-native-redux";
|
|
4
5
|
|
|
5
6
|
import { LayoutContainer } from "./LayoutContainer";
|
|
6
7
|
import { ScreenContainer } from "./ScreenContainer";
|
|
@@ -28,9 +29,7 @@ type Props = {
|
|
|
28
29
|
const Layout = ({ Components, ComponentsExtraProps, children }: Props) => {
|
|
29
30
|
const navigator = useNavigation();
|
|
30
31
|
|
|
31
|
-
const
|
|
32
|
-
"appState",
|
|
33
|
-
]);
|
|
32
|
+
const appReady = useAppSelector(selectAppReady);
|
|
34
33
|
|
|
35
34
|
if (!appReady) {
|
|
36
35
|
return null;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
import { pathOr } from "ramda";
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
4
|
|
|
6
5
|
import { ScreenLayoutContextProvider } from "./ScreenLayoutContextProvider";
|
|
7
6
|
import { StackNavigator } from "../../Navigator";
|
|
7
|
+
import { selectAppReady } from "@applicaster/zapp-react-native-redux";
|
|
8
8
|
|
|
9
9
|
type Components = {
|
|
10
10
|
NavBar: React.ComponentType<any>;
|
|
@@ -16,8 +16,7 @@ type Props = {
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const Layout = ({ Components }: Props) => {
|
|
19
|
-
const
|
|
20
|
-
const appReady = pathOr(false, ["appReady"], appState);
|
|
19
|
+
const appReady = useAppSelector(selectAppReady);
|
|
21
20
|
|
|
22
21
|
if (!appReady) {
|
|
23
22
|
return null;
|
|
@@ -2,7 +2,7 @@ import * as React from "react";
|
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
4
|
import { findPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
5
|
-
import {
|
|
5
|
+
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
6
6
|
import {
|
|
7
7
|
inflateString,
|
|
8
8
|
objectToReadableString,
|
|
@@ -40,7 +40,7 @@ export async function inflateUrl(url) {
|
|
|
40
40
|
|
|
41
41
|
export function LinkHandler(props: Props) {
|
|
42
42
|
const screenData = props?.screenData;
|
|
43
|
-
const
|
|
43
|
+
const plugins = usePlugins();
|
|
44
44
|
|
|
45
45
|
const ScreenPlugin = findPluginByIdentifier(
|
|
46
46
|
WEBVIEW_SCREEN_IDENTIFIER,
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Config Builder -> React Component Migration Guide
|
|
2
|
+
|
|
3
|
+
This guide explains how to migrate MasterCell config-builders to React components incrementally, without breaking existing config-node rendering.
|
|
4
|
+
|
|
5
|
+
## Why This Exists
|
|
6
|
+
|
|
7
|
+
MasterCell currently supports config-node builders such as:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
{
|
|
11
|
+
type: "View",
|
|
12
|
+
style: {},
|
|
13
|
+
additionalProps: {},
|
|
14
|
+
data: [],
|
|
15
|
+
elements: []
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This is still valid and should continue to work.
|
|
20
|
+
|
|
21
|
+
For incremental migration, MasterCell also supports inline React nodes:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
{
|
|
25
|
+
type: "ReactComponent",
|
|
26
|
+
style: {},
|
|
27
|
+
additionalProps: {
|
|
28
|
+
component: MyComponent
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This lets you migrate one builder at a time.
|
|
34
|
+
|
|
35
|
+
## Rendering Pipeline
|
|
36
|
+
|
|
37
|
+
1. Builder returns node config (`type`, `style`, `additionalProps`, `data`, `elements`).
|
|
38
|
+
2. `configInflater` converts `additionalProps` into runtime `props` and resolves `data`.
|
|
39
|
+
3. `elementMapper` renders:
|
|
40
|
+
4. String `type` nodes via component registry (`components[type]`).
|
|
41
|
+
5. `type: "ReactComponent"` nodes via `props.component`.
|
|
42
|
+
|
|
43
|
+
## Migration Pattern
|
|
44
|
+
|
|
45
|
+
1. Keep existing builder API unchanged.
|
|
46
|
+
2. Add a dedicated React component for the node being migrated.
|
|
47
|
+
3. Change builder output from string `type` to `type: "ReactComponent"`.
|
|
48
|
+
4. Pass component reference through `additionalProps.component`.
|
|
49
|
+
5. Keep behavioral props unchanged (`testID`, roles, accessibility props).
|
|
50
|
+
6. Set `renderChildren` explicitly:
|
|
51
|
+
7. `false` when React component renders everything itself.
|
|
52
|
+
8. `true` (or omit) when builder still provides `elements` children.
|
|
53
|
+
9. Set `requiresCellUUID: true` only if the component needs `cellUUID`.
|
|
54
|
+
10. Update builder-shape tests and rendering tests together.
|
|
55
|
+
|
|
56
|
+
## Example Using `TextLabelsContainer.ts`
|
|
57
|
+
|
|
58
|
+
Current builder (config-node only):
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
export const TextLabelsContainer = ({ actionIdentifier, testID, style, extraProps }) => ({
|
|
62
|
+
type: "View",
|
|
63
|
+
additionalProps: {
|
|
64
|
+
mobileActionRole: "label_container",
|
|
65
|
+
},
|
|
66
|
+
elements: [
|
|
67
|
+
{
|
|
68
|
+
type: "Text",
|
|
69
|
+
style,
|
|
70
|
+
additionalProps: {
|
|
71
|
+
label: { context: actionIdentifier, name: "label_1" },
|
|
72
|
+
state: "default",
|
|
73
|
+
mobileActionRole: "label",
|
|
74
|
+
testID: testID ? `${testID}-label` : undefined,
|
|
75
|
+
...extraProps,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Incremental migration target:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { TextLabelsContainerView } from "./TextLabelsContainerView";
|
|
86
|
+
|
|
87
|
+
export const TextLabelsContainer = ({ actionIdentifier, testID, style, extraProps }) => ({
|
|
88
|
+
type: "ReactComponent",
|
|
89
|
+
additionalProps: {
|
|
90
|
+
component: TextLabelsContainerView,
|
|
91
|
+
renderChildren: false,
|
|
92
|
+
mobileActionRole: "label_container",
|
|
93
|
+
actionIdentifier,
|
|
94
|
+
testID,
|
|
95
|
+
textStyle: style,
|
|
96
|
+
textProps: extraProps,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
And React component:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
export const TextLabelsContainerView = ({
|
|
105
|
+
actionIdentifier,
|
|
106
|
+
testID,
|
|
107
|
+
textStyle,
|
|
108
|
+
textProps,
|
|
109
|
+
mobileActionRole,
|
|
110
|
+
}) => (
|
|
111
|
+
<View mobileActionRole={mobileActionRole}>
|
|
112
|
+
<Text
|
|
113
|
+
style={textStyle}
|
|
114
|
+
label={{ context: actionIdentifier, name: "label_1" }}
|
|
115
|
+
state="default"
|
|
116
|
+
mobileActionRole="label"
|
|
117
|
+
testID={testID ? `${testID}-label` : undefined}
|
|
118
|
+
{...textProps}
|
|
119
|
+
/>
|
|
120
|
+
</View>
|
|
121
|
+
);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This keeps external behavior and props contract intact while moving implementation to React.
|
|
125
|
+
|
|
126
|
+
## Rules for Safe Incremental Migration
|
|
127
|
+
|
|
128
|
+
1. Do not change role markers used by parent logic (for example `mobileActionRole`).
|
|
129
|
+
2. Do not rename `testID` contracts during migration.
|
|
130
|
+
3. Keep output shape stable for parent builders (`null` handling, `elements` presence).
|
|
131
|
+
4. Keep data-mapping semantics identical (`label`, `state`, `entry`, etc.).
|
|
132
|
+
5. Migrate one node per PR where possible.
|
|
133
|
+
|
|
134
|
+
## Testing Checklist
|
|
135
|
+
|
|
136
|
+
1. Builder test: node type and required props (`component`, role, `testID`) are correct.
|
|
137
|
+
2. Rendering test: `configInflater + elementMapper` still renders expected UI.
|
|
138
|
+
3. Behavior test: parent container logic that clones/filters children still works.
|
|
139
|
+
4. Regression test: hidden/empty states still return `null` as before.
|
|
140
|
+
|
|
141
|
+
## Important Limitation
|
|
142
|
+
|
|
143
|
+
`additionalProps.component` is a function reference and is suitable for local JS-built trees.
|
|
144
|
+
If a tree must be fully serialized JSON, use a string `componentKey` and resolve it in a registry instead of storing a function directly.
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import React, { useCallback, useEffect } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { ViewStyle } from "react-native";
|
|
3
|
+
import {
|
|
4
|
+
ActionButton as UIKitActionButton,
|
|
5
|
+
ActionButtonProps,
|
|
6
|
+
} from "@applicaster/uikit/lib/components/uikit/action-button";
|
|
3
7
|
|
|
4
8
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
-
|
|
6
9
|
import Image from "./Image";
|
|
10
|
+
|
|
7
11
|
type Props = {
|
|
8
12
|
item: ZappEntry | ZappFeed;
|
|
9
13
|
flavour?: "flavour_1" | "flavour_2";
|
|
@@ -28,27 +32,6 @@ type Props = {
|
|
|
28
32
|
extraProps?: Record<string, any>;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
|
-
function isStringAsset(asset) {
|
|
32
|
-
return typeof asset === "string" || Array.isArray(asset);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function getAssetValue(asset, flavour, fallbackAsset = null) {
|
|
36
|
-
if (!asset) {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (typeof asset === "string") return asset;
|
|
41
|
-
|
|
42
|
-
if (Array.isArray(asset)) {
|
|
43
|
-
const flavourIndex = Number(flavour.replace("flavour_", ""));
|
|
44
|
-
if (flavour && flavourIndex > -1) return asset[flavourIndex - 1];
|
|
45
|
-
|
|
46
|
-
return asset[0];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return asset.src || fallbackAsset;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
35
|
export const ActionButton = React.memo(function ActionButtonComponent(
|
|
53
36
|
props: Props
|
|
54
37
|
) {
|
|
@@ -90,36 +73,32 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
90
73
|
}, [item?.id, setActionState, actionContext]);
|
|
91
74
|
|
|
92
75
|
if (actionDisabled || !actionContext) return null;
|
|
93
|
-
|
|
76
|
+
|
|
77
|
+
const resolvedAssetComponent = actionState?.asset;
|
|
94
78
|
|
|
95
79
|
// Default state is not yet ready
|
|
96
|
-
if (!
|
|
80
|
+
if (!resolvedAssetComponent) return null;
|
|
97
81
|
|
|
98
82
|
return (
|
|
99
|
-
<
|
|
100
|
-
|
|
83
|
+
<UIKitActionButton
|
|
84
|
+
id={`action-button-${item.id}`}
|
|
85
|
+
item={item}
|
|
86
|
+
action={action}
|
|
87
|
+
asset={asset}
|
|
88
|
+
flavour={flavour}
|
|
89
|
+
cellUUID={cellUUID}
|
|
90
|
+
extraProps={props?.extraProps}
|
|
91
|
+
show={!!resolvedAssetComponent}
|
|
92
|
+
AssetComponent={resolvedAssetComponent}
|
|
93
|
+
ImageComponent={Image}
|
|
101
94
|
onPress={onPress}
|
|
102
|
-
testID={props?.testID
|
|
103
|
-
accessibilityLabel={props?.accessibilityLabel
|
|
95
|
+
testID={props?.testID}
|
|
96
|
+
accessibilityLabel={props?.accessibilityLabel}
|
|
104
97
|
accessibilityHint={props?.accessibilityHint}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
fadeDuration={0}
|
|
111
|
-
style={asset?.style || props?.style}
|
|
112
|
-
uri={getAssetValue(actionState.asset, flavour, asset?.src)}
|
|
113
|
-
{...asset?.props}
|
|
114
|
-
/>
|
|
115
|
-
) : (
|
|
116
|
-
<AssetComponent
|
|
117
|
-
flavour={flavour}
|
|
118
|
-
asset={getAssetValue(asset, flavour)}
|
|
119
|
-
cellUUID={cellUUID}
|
|
120
|
-
{...(props?.extraProps ?? {})}
|
|
121
|
-
/>
|
|
122
|
-
)}
|
|
123
|
-
</TouchableOpacity>
|
|
98
|
+
style={props?.style as ActionButtonProps["style"]}
|
|
99
|
+
fallbackSrc={
|
|
100
|
+
actionState.state === 1 ? asset?.src.active : asset?.src.inactive
|
|
101
|
+
}
|
|
102
|
+
/>
|
|
124
103
|
);
|
|
125
104
|
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { buildActionButtonsModel } from "../model";
|
|
2
|
+
|
|
3
|
+
describe("buildActionButtonsModel", () => {
|
|
4
|
+
it("returns null when the container is disabled", () => {
|
|
5
|
+
const configuration = {
|
|
6
|
+
mobile_buttons_container_buttons_enabled: false,
|
|
7
|
+
mobile_button_1_button_enabled: true,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const value = (key) => configuration[key];
|
|
11
|
+
|
|
12
|
+
expect(
|
|
13
|
+
buildActionButtonsModel({
|
|
14
|
+
configuration,
|
|
15
|
+
value,
|
|
16
|
+
containerPrefix: "mobile_buttons_container",
|
|
17
|
+
buttonPrefix: "mobile_button",
|
|
18
|
+
})
|
|
19
|
+
).toBeNull();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("returns explicit enabled slots and semantic container data", () => {
|
|
23
|
+
const configuration = {
|
|
24
|
+
mobile_buttons_container_buttons_enabled: true,
|
|
25
|
+
mobile_buttons_container_align: "right",
|
|
26
|
+
mobile_buttons_container_margin_top: 1,
|
|
27
|
+
mobile_buttons_container_margin_right: 2,
|
|
28
|
+
mobile_buttons_container_margin_bottom: 3,
|
|
29
|
+
mobile_buttons_container_margin_left: 4,
|
|
30
|
+
mobile_buttons_container_stacking: "vertical",
|
|
31
|
+
mobile_buttons_container_horizontal_gutter: 8,
|
|
32
|
+
mobile_buttons_container_vertical_gutter: 12,
|
|
33
|
+
mobile_buttons_container_independent_styles: false,
|
|
34
|
+
mobile_button_1_button_enabled: true,
|
|
35
|
+
mobile_button_2_button_enabled: false,
|
|
36
|
+
mobile_button_3_button_enabled: true,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const value = (key) => configuration[key];
|
|
40
|
+
|
|
41
|
+
expect(
|
|
42
|
+
buildActionButtonsModel({
|
|
43
|
+
configuration,
|
|
44
|
+
value,
|
|
45
|
+
containerPrefix: "mobile_buttons_container",
|
|
46
|
+
buttonPrefix: "mobile_button",
|
|
47
|
+
})
|
|
48
|
+
).toEqual({
|
|
49
|
+
enabledSlots: [1, 3],
|
|
50
|
+
buttonsCount: 2,
|
|
51
|
+
container: {
|
|
52
|
+
horizontalAlign: "flex-end",
|
|
53
|
+
margins: {
|
|
54
|
+
top: 1,
|
|
55
|
+
right: 2,
|
|
56
|
+
bottom: 3,
|
|
57
|
+
left: 4,
|
|
58
|
+
},
|
|
59
|
+
stacking: "vertical",
|
|
60
|
+
horizontalGutter: 8,
|
|
61
|
+
verticalGutter: 12,
|
|
62
|
+
independentStyles: false,
|
|
63
|
+
},
|
|
64
|
+
buttons: [
|
|
65
|
+
{
|
|
66
|
+
slot: 1,
|
|
67
|
+
renderIndex: 0,
|
|
68
|
+
specificPrefix: "mobile_button_1",
|
|
69
|
+
stylePrefix: "mobile_button_1",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
slot: 3,
|
|
73
|
+
renderIndex: 1,
|
|
74
|
+
specificPrefix: "mobile_button_3",
|
|
75
|
+
stylePrefix: "mobile_button_1",
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
});
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import {
|
|
2
|
+
insertBetweenLabelContainers,
|
|
3
|
+
insertBetweenLabels,
|
|
4
|
+
} from "../placement";
|
|
5
|
+
|
|
6
|
+
describe("ActionButtonsCore placement", () => {
|
|
7
|
+
const buttons = { type: "View", name: "buttons" };
|
|
8
|
+
|
|
9
|
+
const slots = [
|
|
10
|
+
{ name: "text_label_1", element: { type: "View", id: "label_1" } },
|
|
11
|
+
{ name: "text_label_2", element: { type: "View", id: "label_2" } },
|
|
12
|
+
{ name: "text_label_3", element: { type: "View", id: "label_3" } },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const labels = slots.map((slot) => slot.element);
|
|
16
|
+
|
|
17
|
+
it("inserts buttons after the matching label", () => {
|
|
18
|
+
expect(
|
|
19
|
+
insertBetweenLabels({ position: "below_text_label_2" }, buttons, slots)
|
|
20
|
+
).toEqual([labels[0], labels[1], buttons, labels[2]]);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("inserts buttons before the matching label", () => {
|
|
24
|
+
expect(
|
|
25
|
+
insertBetweenLabels({ position: "above_text_label_2" }, buttons, slots)
|
|
26
|
+
).toEqual([labels[0], buttons, labels[1], labels[2]]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("inserts buttons after the matching label on a plain name match", () => {
|
|
30
|
+
expect(
|
|
31
|
+
insertBetweenLabels({ position: "text_label_2" }, buttons, slots)
|
|
32
|
+
).toEqual([labels[0], labels[1], buttons, labels[2]]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("keeps the slot when its label is empty (null element)", () => {
|
|
36
|
+
const slotsWithEmpty = [
|
|
37
|
+
{ name: "text_label_1", element: { id: "label_1" } },
|
|
38
|
+
{ name: "text_label_2", element: null },
|
|
39
|
+
{ name: "text_label_3", element: { id: "label_3" } },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
expect(
|
|
43
|
+
insertBetweenLabels(
|
|
44
|
+
{ position: "above_text_label_2" },
|
|
45
|
+
buttons,
|
|
46
|
+
slotsWithEmpty
|
|
47
|
+
)
|
|
48
|
+
).toEqual([{ id: "label_1" }, buttons, { id: "label_3" }]);
|
|
49
|
+
|
|
50
|
+
expect(
|
|
51
|
+
insertBetweenLabels(
|
|
52
|
+
{ position: "below_text_label_2" },
|
|
53
|
+
buttons,
|
|
54
|
+
slotsWithEmpty
|
|
55
|
+
)
|
|
56
|
+
).toEqual([{ id: "label_1" }, buttons, { id: "label_3" }]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("prepends buttons only when on_top is allowed", () => {
|
|
60
|
+
expect(
|
|
61
|
+
insertBetweenLabels(
|
|
62
|
+
{ position: "on_top", allowOnTop: true },
|
|
63
|
+
buttons,
|
|
64
|
+
slots
|
|
65
|
+
)
|
|
66
|
+
).toEqual([buttons, ...labels]);
|
|
67
|
+
|
|
68
|
+
expect(
|
|
69
|
+
insertBetweenLabels(
|
|
70
|
+
{ position: "on_top", allowOnTop: false },
|
|
71
|
+
buttons,
|
|
72
|
+
slots
|
|
73
|
+
)
|
|
74
|
+
).toEqual(labels);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("appends buttons when appendWhenMissing is enabled", () => {
|
|
78
|
+
expect(
|
|
79
|
+
insertBetweenLabels(
|
|
80
|
+
{ position: "unknown", appendWhenMissing: true },
|
|
81
|
+
buttons,
|
|
82
|
+
slots
|
|
83
|
+
)
|
|
84
|
+
).toEqual([...labels, buttons]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("returns labels unchanged when appendWhenMissing is disabled", () => {
|
|
88
|
+
expect(
|
|
89
|
+
insertBetweenLabels(
|
|
90
|
+
{ position: "unknown", appendWhenMissing: false },
|
|
91
|
+
buttons,
|
|
92
|
+
slots
|
|
93
|
+
)
|
|
94
|
+
).toEqual(labels);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const labelContainers = [
|
|
98
|
+
{
|
|
99
|
+
elements: [
|
|
100
|
+
{
|
|
101
|
+
elements: [{ name: "top_label_1" }, { name: "top_label_2" }],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
elements: [
|
|
107
|
+
{
|
|
108
|
+
elements: [{ name: "bottom_label_1" }],
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
it("inserts buttons after the matching label in a nested container", () => {
|
|
115
|
+
expect(
|
|
116
|
+
insertBetweenLabelContainers(
|
|
117
|
+
{ position: "below_top_label_2" },
|
|
118
|
+
buttons,
|
|
119
|
+
labelContainers
|
|
120
|
+
)
|
|
121
|
+
).toEqual([
|
|
122
|
+
{
|
|
123
|
+
elements: [
|
|
124
|
+
{
|
|
125
|
+
elements: [
|
|
126
|
+
{ name: "top_label_1" },
|
|
127
|
+
{ name: "top_label_2" },
|
|
128
|
+
buttons,
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
labelContainers[1],
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("inserts buttons before the matching label in a nested container", () => {
|
|
138
|
+
expect(
|
|
139
|
+
insertBetweenLabelContainers(
|
|
140
|
+
{ position: "above_top_label_2" },
|
|
141
|
+
buttons,
|
|
142
|
+
labelContainers
|
|
143
|
+
)
|
|
144
|
+
).toEqual([
|
|
145
|
+
{
|
|
146
|
+
elements: [
|
|
147
|
+
{
|
|
148
|
+
elements: [
|
|
149
|
+
{ name: "top_label_1" },
|
|
150
|
+
buttons,
|
|
151
|
+
{ name: "top_label_2" },
|
|
152
|
+
],
|
|
153
|
+
},
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
labelContainers[1],
|
|
157
|
+
]);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it("prepends buttons into the first container only when on_top is allowed", () => {
|
|
161
|
+
expect(
|
|
162
|
+
insertBetweenLabelContainers(
|
|
163
|
+
{ position: "on_top", allowOnTop: true },
|
|
164
|
+
buttons,
|
|
165
|
+
labelContainers
|
|
166
|
+
)
|
|
167
|
+
).toEqual([
|
|
168
|
+
{
|
|
169
|
+
elements: [buttons, ...labelContainers[0].elements],
|
|
170
|
+
},
|
|
171
|
+
labelContainers[1],
|
|
172
|
+
]);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("appends buttons into the last container when configured", () => {
|
|
176
|
+
expect(
|
|
177
|
+
insertBetweenLabelContainers(
|
|
178
|
+
{ position: "unknown", appendWhenMissing: true },
|
|
179
|
+
buttons,
|
|
180
|
+
labelContainers
|
|
181
|
+
)
|
|
182
|
+
).toEqual([
|
|
183
|
+
labelContainers[0],
|
|
184
|
+
{
|
|
185
|
+
elements: [...labelContainers[1].elements, buttons],
|
|
186
|
+
},
|
|
187
|
+
]);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("inserts buttons after a matching label in a direct two-level container", () => {
|
|
191
|
+
const directLabelContainers = [
|
|
192
|
+
{
|
|
193
|
+
elements: [{ name: "top_label_1" }, { name: "top_label_2" }],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
elements: [{ name: "bottom_label_1" }],
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
expect(
|
|
201
|
+
insertBetweenLabelContainers(
|
|
202
|
+
{ position: "below_top_label_2" },
|
|
203
|
+
buttons,
|
|
204
|
+
directLabelContainers
|
|
205
|
+
)
|
|
206
|
+
).toEqual([
|
|
207
|
+
{
|
|
208
|
+
elements: [{ name: "top_label_1" }, { name: "top_label_2" }, buttons],
|
|
209
|
+
},
|
|
210
|
+
directLabelContainers[1],
|
|
211
|
+
]);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getButtonSlotPrefix,
|
|
3
|
+
getEnabledButtonSlots,
|
|
4
|
+
getStylePrefix,
|
|
5
|
+
} from "../selectors";
|
|
6
|
+
|
|
7
|
+
describe("ActionButtonsCore selectors", () => {
|
|
8
|
+
it("returns explicit enabled button slots without collapsing sparse slots", () => {
|
|
9
|
+
const configuration = {
|
|
10
|
+
mobile_button_1_button_enabled: true,
|
|
11
|
+
mobile_button_2_button_enabled: false,
|
|
12
|
+
mobile_button_3_button_enabled: true,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
expect(getEnabledButtonSlots(configuration, "mobile_button")).toEqual([
|
|
16
|
+
1, 3,
|
|
17
|
+
]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns slot-based prefixes", () => {
|
|
21
|
+
expect(getButtonSlotPrefix("tv_buttons_button", 3)).toBe(
|
|
22
|
+
"tv_buttons_button_3"
|
|
23
|
+
);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("reuses button 1 styles when independent styles are disabled", () => {
|
|
27
|
+
expect(
|
|
28
|
+
getStylePrefix({
|
|
29
|
+
slot: 3,
|
|
30
|
+
independentStyles: false,
|
|
31
|
+
buttonPrefix: "mobile_button",
|
|
32
|
+
})
|
|
33
|
+
).toBe("mobile_button_1");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("uses the explicit slot prefix when independent styles are enabled", () => {
|
|
37
|
+
expect(
|
|
38
|
+
getStylePrefix({
|
|
39
|
+
slot: 3,
|
|
40
|
+
independentStyles: true,
|
|
41
|
+
buttonPrefix: "mobile_button",
|
|
42
|
+
})
|
|
43
|
+
).toBe("mobile_button_3");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { buildContainerLayout, getContainerMargins } from "../style";
|
|
2
|
+
|
|
3
|
+
describe("ActionButtonsCore style helpers", () => {
|
|
4
|
+
it("returns numeric margins with zero defaults", () => {
|
|
5
|
+
const configuration = {
|
|
6
|
+
mobile_buttons_container_margin_top: "4",
|
|
7
|
+
mobile_buttons_container_margin_left: 6,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const value = (key) => configuration[key];
|
|
11
|
+
|
|
12
|
+
expect(getContainerMargins(value, "mobile_buttons_container")).toEqual({
|
|
13
|
+
top: 4,
|
|
14
|
+
right: 0,
|
|
15
|
+
bottom: 0,
|
|
16
|
+
left: 6,
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("returns semantic layout data using shared alignment mapping", () => {
|
|
21
|
+
const configuration = {
|
|
22
|
+
tv_buttons_container_align: "middle",
|
|
23
|
+
tv_buttons_container_margin_top: 1,
|
|
24
|
+
tv_buttons_container_margin_right: 2,
|
|
25
|
+
tv_buttons_container_margin_bottom: 3,
|
|
26
|
+
tv_buttons_container_margin_left: 4,
|
|
27
|
+
tv_buttons_container_stacking: "vertical",
|
|
28
|
+
tv_buttons_container_horizontal_gutter: 8,
|
|
29
|
+
tv_buttons_container_vertical_gutter: 12,
|
|
30
|
+
tv_buttons_container_independent_styles: true,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const value = (key) => configuration[key];
|
|
34
|
+
|
|
35
|
+
expect(buildContainerLayout(value, "tv_buttons_container")).toEqual({
|
|
36
|
+
horizontalAlign: "center",
|
|
37
|
+
margins: {
|
|
38
|
+
top: 1,
|
|
39
|
+
right: 2,
|
|
40
|
+
bottom: 3,
|
|
41
|
+
left: 4,
|
|
42
|
+
},
|
|
43
|
+
stacking: "vertical",
|
|
44
|
+
horizontalGutter: 8,
|
|
45
|
+
verticalGutter: 12,
|
|
46
|
+
independentStyles: true,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|