@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.
Files changed (202) hide show
  1. package/Components/BackgroundImage/index.ts +1 -8
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/FocusableWrapper.tsx +3 -0
  4. package/Components/Cell/TvOSCellComponent.tsx +6 -3
  5. package/Components/CellRendererResolver/index.ts +1 -1
  6. package/Components/Focusable/Focusable.tsx +4 -2
  7. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  8. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  9. package/Components/FocusableGroup/FocusableTvOS.tsx +30 -1
  10. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +39 -0
  11. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  12. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  15. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +124 -0
  16. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  17. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  18. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +72 -0
  20. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  22. package/Components/GeneralContentScreen/index.ts +2 -0
  23. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  24. package/Components/HandlePlayable/HandlePlayable.tsx +13 -8
  25. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  26. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  27. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  28. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  29. package/Components/Layout/TV/index.tsx +3 -4
  30. package/Components/Layout/TV/index.web.tsx +3 -4
  31. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  32. package/Components/MasterCell/CONFIG_BUILDER_TO_REACT_COMPONENT.md +144 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +27 -48
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +213 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +165 -0
  39. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +405 -0
  40. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/index.ts +1 -0
  41. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  42. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +198 -0
  43. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  44. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  45. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  46. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +217 -46
  47. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +50 -119
  48. package/Components/MasterCell/DefaultComponents/Button.tsx +50 -58
  49. package/Components/MasterCell/DefaultComponents/ButtonContainerView/components/HorizontalSeparator.tsx +8 -0
  50. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.android.tv.tsx +58 -0
  51. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.tsx +15 -0
  52. package/Components/MasterCell/DefaultComponents/{tv/ButtonContainerView/index.tsx → ButtonContainerView/index.tv.tsx} +3 -11
  53. package/Components/MasterCell/DefaultComponents/ButtonContainerView/index.web.ts +1 -0
  54. package/Components/MasterCell/DefaultComponents/ButtonContainerView/types.ts +40 -0
  55. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/__snapshots__/index.test.tsx.snap +18 -0
  56. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/index.test.tsx +90 -0
  57. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +13 -14
  58. package/Components/MasterCell/DefaultComponents/DataProvider/index.tsx +163 -0
  59. package/Components/MasterCell/DefaultComponents/FocusableView/index.android.tsx +2 -23
  60. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +4 -22
  61. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +8 -2
  62. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  63. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  64. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  65. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  66. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +1 -2
  67. package/Components/MasterCell/DefaultComponents/PressableView.tsx +54 -0
  68. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +46 -50
  69. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +47 -39
  70. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  71. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +19 -6
  72. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +15 -0
  73. package/Components/MasterCell/DefaultComponents/Text/index.tsx +45 -15
  74. package/Components/MasterCell/DefaultComponents/UIKitAdapters/Badge.tsx +49 -0
  75. package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +122 -0
  76. package/Components/MasterCell/DefaultComponents/__tests__/Button.test.tsx +186 -0
  77. package/Components/MasterCell/DefaultComponents/__tests__/DataProvider.test.tsx +141 -0
  78. package/Components/MasterCell/DefaultComponents/__tests__/DynamicBadge.test.tsx +156 -0
  79. package/Components/MasterCell/DefaultComponents/__tests__/__snapshots__/ActionButton.test.tsx.snap +60 -0
  80. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +196 -67
  81. package/Components/MasterCell/DefaultComponents/index.ts +13 -3
  82. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +189 -0
  83. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +33 -0
  84. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +33 -0
  85. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +159 -0
  86. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +18 -0
  87. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  88. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +39 -0
  89. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +402 -0
  90. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +141 -0
  91. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +354 -0
  92. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  93. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +129 -0
  94. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +123 -0
  95. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +238 -0
  96. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Asset.ts +4 -18
  97. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/Button.ts +24 -73
  98. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TextLabelsContainer.ts +37 -18
  99. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/TvActionButton.tsx +27 -0
  100. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +89 -0
  101. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/renderedTree.test.tsx +231 -0
  102. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +47 -48
  103. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  104. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  105. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +101 -144
  106. package/Components/MasterCell/MappingFunctions/index.js +3 -2
  107. package/Components/MasterCell/README.md +4 -0
  108. package/Components/MasterCell/__tests__/__snapshots__/dataAdapter.test.js.snap +24 -0
  109. package/Components/MasterCell/__tests__/configInflater.test.js +1 -0
  110. package/Components/MasterCell/__tests__/elementMapper.test.js +46 -0
  111. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  112. package/Components/MasterCell/dataAdapter.ts +4 -1
  113. package/Components/MasterCell/elementMapper.tsx +54 -7
  114. package/Components/MasterCell/hooks/index.ts +2 -0
  115. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  116. package/Components/MasterCell/index.tsx +20 -4
  117. package/Components/MasterCell/utils/__tests__/cloneChildrenWithIds.test.tsx +43 -0
  118. package/Components/MasterCell/utils/__tests__/useFilterChildren.test.tsx +80 -0
  119. package/Components/MasterCell/utils/index.ts +82 -16
  120. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +33 -69
  121. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +25 -47
  122. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +122 -39
  123. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +671 -0
  124. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +257 -88
  125. package/Components/ModalComponent/AudioPlayer/Components/index.ts +6 -4
  126. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  127. package/Components/ModalComponent/BottomSheetModalContent.tsx +17 -10
  128. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  129. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  130. package/Components/OfflineHandler/__tests__/index.test.tsx +101 -32
  131. package/Components/OfflineHandler/hooks.ts +123 -0
  132. package/Components/OfflineHandler/index.tsx +51 -73
  133. package/Components/OfflineHandler/utils/index.ts +1 -13
  134. package/Components/PlayerContainer/PlayerContainer.tsx +6 -5
  135. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +2 -4
  136. package/Components/PreloaderWrapper/index.tsx +1 -1
  137. package/Components/River/ComponentsMap/ComponentsMap.tsx +8 -21
  138. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  139. package/Components/River/RefreshControl.tsx +19 -105
  140. package/Components/River/River.tsx +9 -82
  141. package/Components/River/TV/River.tsx +31 -14
  142. package/Components/River/TV/index.tsx +8 -4
  143. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  144. package/Components/River/TV/utils/index.ts +4 -0
  145. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  146. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +4 -0
  147. package/Components/River/__tests__/componentsMap.test.js +38 -0
  148. package/Components/River/hooks/__tests__/usePullToRefresh.test.ts +132 -0
  149. package/Components/River/hooks/index.ts +1 -0
  150. package/Components/River/hooks/usePullToRefresh.ts +51 -0
  151. package/Components/Screen/TV/index.web.tsx +4 -2
  152. package/Components/Screen/__tests__/Screen.test.tsx +65 -42
  153. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  154. package/Components/Screen/hooks.ts +2 -3
  155. package/Components/Screen/index.tsx +2 -3
  156. package/Components/Screen/orientationHandler.ts +3 -3
  157. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  158. package/Components/ScreenResolver/index.tsx +9 -5
  159. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  160. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  161. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  162. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +1 -1
  163. package/Components/Tabs/TabContent.tsx +7 -4
  164. package/Components/TopCutoffOverlay/__tests__/TopCutoffOverlay.test.tsx +201 -0
  165. package/Components/TopCutoffOverlay/hooks/__tests__/useMarginTop.test.ts +130 -0
  166. package/Components/TopCutoffOverlay/hooks/index.ts +1 -0
  167. package/Components/TopCutoffOverlay/hooks/useMarginTop.ts +59 -0
  168. package/Components/TopCutoffOverlay/index.tsx +55 -0
  169. package/Components/Transitioner/index.js +3 -3
  170. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +5 -5
  171. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  172. package/Components/VideoModal/utils.ts +12 -10
  173. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  174. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  175. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  176. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  177. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  178. package/Contexts/ScreenContext/index.tsx +25 -18
  179. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  180. package/Decorators/Analytics/index.tsx +6 -5
  181. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  182. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  183. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  184. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  185. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  186. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  187. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  188. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  189. package/Helpers/DataSourceHelper/index.ts +19 -0
  190. package/package.json +5 -5
  191. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +0 -135
  192. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/types.ts +0 -25
  193. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  194. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  195. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  196. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -67
  197. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  198. package/Components/River/TV/withTVEventHandler.tsx +0 -36
  199. package/Helpers/DataSourceHelper/index.js +0 -19
  200. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  201. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  202. /package/Components/BackgroundImage/{BackgroundImage.tv.web.tsx → BackgroundImage.tsx} +0 -0
@@ -1,10 +1,10 @@
1
- import { TextLabel } from "./TextLabel";
2
1
  import { compact } from "@applicaster/zapp-react-native-utils/cellUtils";
2
+ import { getTextLabelStyles } from "./utils";
3
3
 
4
4
  const View = "View";
5
+ const Text = "Text";
5
6
 
6
7
  export const TextLabelsContainer = ({
7
- prefix,
8
8
  value,
9
9
  platformValue,
10
10
  pluginIdentifier,
@@ -14,21 +14,40 @@ export const TextLabelsContainer = ({
14
14
  style: {
15
15
  flexDirection: "column",
16
16
  },
17
- elements: compact([
18
- TextLabel({
19
- prefix: `${prefix}_label_1`,
20
- value,
21
- platformValue,
22
- pluginIdentifier,
23
- name: "label_1",
24
- }),
25
- TextLabel({
26
- prefix: `${prefix}_label_2`,
27
- value,
28
- platformValue,
29
- pluginIdentifier,
30
- name: "label_2",
31
- }),
32
- ]),
17
+ elements: compact(
18
+ ["label_1", "label_2"].map((name) => {
19
+ const getValue = (suffix: string) => value(`${name}_${suffix}`);
20
+
21
+ const getPlatformValue = (suffix: string) =>
22
+ platformValue(`${name}_${suffix}`);
23
+
24
+ return getValue("toggle")
25
+ ? {
26
+ type: Text,
27
+ style: getTextLabelStyles(getValue, getPlatformValue),
28
+ data: [
29
+ {
30
+ func: "identity",
31
+ args: [],
32
+ propName: "entry",
33
+ },
34
+ ],
35
+ additionalProps: {
36
+ label: { context: pluginIdentifier, name },
37
+ numberOfLines: getValue("number_of_lines"),
38
+ transformText: getValue("text_transform"),
39
+ focusedStyles: {
40
+ backgroundColor: getValue("focused_background_color"),
41
+ color: getValue("focused_font_color"),
42
+ },
43
+ normalStyles: {
44
+ backgroundColor: getValue("background_color"),
45
+ color: getValue("font_color"),
46
+ },
47
+ },
48
+ }
49
+ : null;
50
+ })
51
+ ),
33
52
  };
34
53
  };
@@ -0,0 +1,27 @@
1
+ import React from "react";
2
+ import { ActionButtonController } from "../../ActionButtonsCore/components";
3
+ import { FocusableView } from "../../FocusableView";
4
+
5
+ type Props = Record<string, any> & {
6
+ children?: React.ReactNode;
7
+ style?: object;
8
+ entry?: any;
9
+ };
10
+
11
+ export function TvActionButton({
12
+ children,
13
+ pluginIdentifier,
14
+ style,
15
+ entry,
16
+ ...props
17
+ }: Props) {
18
+ return (
19
+ <ActionButtonController pluginIdentifier={pluginIdentifier} entry={entry}>
20
+ {({ onPress }) => (
21
+ <FocusableView {...props} style={style} onPress={onPress}>
22
+ {children}
23
+ </FocusableView>
24
+ )}
25
+ </ActionButtonController>
26
+ );
27
+ }
@@ -0,0 +1,89 @@
1
+ import { TvActionButtons } from "..";
2
+
3
+ describe("TvActionButtons", () => {
4
+ const baseConfiguration = {
5
+ tv_buttons_container_buttons_enabled: true,
6
+ tv_buttons_container_align: "middle",
7
+ tv_buttons_container_margin_top: 1,
8
+ tv_buttons_container_margin_right: 2,
9
+ tv_buttons_container_margin_bottom: 3,
10
+ tv_buttons_container_margin_left: 4,
11
+ tv_buttons_container_horizontal_gutter: 10,
12
+ tv_buttons_container_independent_styles: false,
13
+ tv_buttons_button_1_button_enabled: true,
14
+ tv_buttons_button_1_assign_action: "action_1",
15
+ tv_buttons_button_1_background_padding_top: 11,
16
+ tv_buttons_button_1_background_padding_right: 12,
17
+ tv_buttons_button_1_background_padding_bottom: 13,
18
+ tv_buttons_button_1_background_padding_left: 14,
19
+ tv_buttons_button_3_button_enabled: true,
20
+ tv_buttons_button_3_assign_action: "action_3",
21
+ tv_buttons_button_3_background_padding_top: 31,
22
+ tv_buttons_button_3_background_padding_right: 32,
23
+ tv_buttons_button_3_background_padding_bottom: 33,
24
+ tv_buttons_button_3_background_padding_left: 34,
25
+ };
26
+
27
+ const platformValue = jest.fn();
28
+
29
+ it("renders sparse enabled slots with slot-based action lookup and ids", () => {
30
+ const value = (key) => baseConfiguration[key];
31
+
32
+ const result = TvActionButtons({
33
+ value,
34
+ platformValue,
35
+ configuration: baseConfiguration,
36
+ state: "focused",
37
+ skipButtons: false,
38
+ });
39
+
40
+ expect(result.type).toBe("ButtonContainerView");
41
+ expect(result.style.justifyContent).toBe("center");
42
+ expect(result.additionalProps.buttonsCount).toBe(2);
43
+ expect(result.elements).toHaveLength(2);
44
+ expect(result.elements[0].type).toBe("DataProvider");
45
+
46
+ expect(result.elements[0].data).toEqual([
47
+ {
48
+ func: "identity",
49
+ args: [],
50
+ propName: "entry",
51
+ },
52
+ ]);
53
+
54
+ const firstButton = result.elements[0].elements[0];
55
+ const secondButton = result.elements[1].elements[0];
56
+
57
+ expect(firstButton.additionalProps.pluginIdentifier).toBe("action_1");
58
+
59
+ expect(firstButton.additionalProps.suffixId).toBe("tv_buttons_button_1");
60
+
61
+ expect(firstButton.additionalProps.preferredFocus).toBe(true);
62
+
63
+ expect(secondButton.additionalProps.pluginIdentifier).toBe("action_3");
64
+
65
+ expect(secondButton.additionalProps.suffixId).toBe("tv_buttons_button_3");
66
+
67
+ expect(secondButton.additionalProps.preferredFocus).toBe(false);
68
+ });
69
+
70
+ it("keeps shared button styles from slot 1 while preserving slot 3 identity", () => {
71
+ const value = (key) => baseConfiguration[key];
72
+
73
+ const result = TvActionButtons({
74
+ value,
75
+ platformValue,
76
+ configuration: baseConfiguration,
77
+ state: "focused",
78
+ skipButtons: false,
79
+ });
80
+
81
+ const secondButton = result.elements[1].elements[0];
82
+
83
+ expect(secondButton.style.paddingTop).toBe(11);
84
+
85
+ expect(secondButton.additionalProps.pluginIdentifier).toBe("action_3");
86
+
87
+ expect(secondButton.additionalProps.suffixId).toBe("tv_buttons_button_3");
88
+ });
89
+ });
@@ -0,0 +1,231 @@
1
+ import React from "react";
2
+ import { Text as RNText, View } from "react-native";
3
+ import { render } from "@testing-library/react-native";
4
+ import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
+
6
+ import { configInflater } from "../../../../dataAdapter";
7
+ import { elementMapper } from "../../../../elementMapper";
8
+ import { defaultComponents } from "../../../index";
9
+ import { TvActionButtons } from "..";
10
+
11
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks/actions", () => ({
12
+ useActions: jest.fn(),
13
+ }));
14
+
15
+ jest.mock("@applicaster/zapp-react-native-utils/reactHooks", () => ({
16
+ useIsScreenActive: jest.fn(() => false),
17
+ }));
18
+
19
+ jest.mock("@applicaster/zapp-react-native-utils/theme", () => ({
20
+ useTheme: () => ({}),
21
+ }));
22
+
23
+ jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
24
+ useIsRTL: jest.fn(() => false),
25
+ applyRTLStylesIfNeeded: jest.fn((style) => style),
26
+ }));
27
+
28
+ jest.mock(
29
+ "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks",
30
+ () => ({
31
+ useAccessibilityManager: jest.fn(() => ({
32
+ addHeading: jest.fn(),
33
+ })),
34
+ })
35
+ );
36
+
37
+ jest.mock(
38
+ "@applicaster/zapp-react-native-ui-components/Components/Focusable",
39
+ () => ({
40
+ Focusable: ({ children, ...props }) => {
41
+ const { View: MockView } = require("react-native");
42
+
43
+ return (
44
+ <MockView testID="tv-focusable" {...props}>
45
+ {typeof children === "function" ? children(false) : children}
46
+ </MockView>
47
+ );
48
+ },
49
+ })
50
+ );
51
+
52
+ const mockUseActions = useActions as jest.Mock;
53
+
54
+ const item = {
55
+ id: "entry-1",
56
+ } as ZappEntry;
57
+
58
+ const buildActionContext = (entryState = {}) => ({
59
+ initialEntryState: jest.fn(() => ({
60
+ label: {
61
+ label_1: "Play",
62
+ },
63
+ ...entryState,
64
+ })),
65
+ invokeAction: jest.fn(),
66
+ addListener: jest.fn(() => jest.fn()),
67
+ addListeners: jest.fn(() => jest.fn()),
68
+ });
69
+
70
+ const tvConfiguration = {
71
+ tv_buttons_container_buttons_enabled: true,
72
+ tv_buttons_container_align: "middle",
73
+ tv_buttons_container_margin_top: 1,
74
+ tv_buttons_container_margin_right: 2,
75
+ tv_buttons_container_margin_bottom: 3,
76
+ tv_buttons_container_margin_left: 4,
77
+ tv_buttons_container_horizontal_gutter: 10,
78
+ tv_buttons_container_independent_styles: true,
79
+ tv_buttons_button_1_button_enabled: true,
80
+ tv_buttons_button_1_assign_action: "navigation_action",
81
+ tv_buttons_button_1_background_padding_top: 11,
82
+ tv_buttons_button_1_background_padding_right: 12,
83
+ tv_buttons_button_1_background_padding_bottom: 13,
84
+ tv_buttons_button_1_background_padding_left: 14,
85
+ tv_buttons_button_1_background_color: "rgba(1,1,1,1)",
86
+ tv_buttons_button_1_background_border_color: "rgba(2,2,2,1)",
87
+ tv_buttons_button_1_background_border_thickness: 1,
88
+ tv_buttons_button_1_background_corner_radius: 8,
89
+ tv_buttons_button_1_focused_background_color: "rgba(3,3,3,1)",
90
+ tv_buttons_button_1_focused_background_border_color: "rgba(4,4,4,1)",
91
+ tv_buttons_button_1_display_mode: "dynamic",
92
+ tv_buttons_button_1_asset_enabled: true,
93
+ tv_buttons_button_1_asset_width: 40,
94
+ tv_buttons_button_1_asset_height: 40,
95
+ tv_buttons_button_1_asset_margin_top: 0,
96
+ tv_buttons_button_1_asset_margin_right: 0,
97
+ tv_buttons_button_1_asset_margin_bottom: 0,
98
+ tv_buttons_button_1_asset_margin_left: 0,
99
+ tv_buttons_button_1_asset_alignment: "left",
100
+ tv_buttons_button_1_label_1_toggle: true,
101
+ tv_buttons_button_1_label_1_margin_top: 0,
102
+ tv_buttons_button_1_label_1_margin_right: 0,
103
+ tv_buttons_button_1_label_1_margin_bottom: 0,
104
+ tv_buttons_button_1_label_1_margin_left: 0,
105
+ tv_buttons_button_1_label_1_padding_top: 0,
106
+ tv_buttons_button_1_label_1_padding_right: 0,
107
+ tv_buttons_button_1_label_1_padding_bottom: 0,
108
+ tv_buttons_button_1_label_1_padding_left: 0,
109
+ tv_buttons_button_1_label_1_text_alignment: "left",
110
+ tv_buttons_button_1_label_1_text_transform: "default",
111
+ tv_buttons_button_1_label_1_font_color: "rgba(10,10,10,1)",
112
+ tv_buttons_button_1_label_1_focused_font_color: "rgba(20,20,20,1)",
113
+ tv_buttons_button_1_label_1_font_size: 18,
114
+ tv_buttons_button_1_label_1_line_height: 24,
115
+ tv_buttons_button_1_label_1_font_family: "Ubuntu-Bold",
116
+ tv_buttons_button_1_label_1_letter_spacing: -0.2,
117
+ };
118
+
119
+ const mergeStyle = (style) =>
120
+ (Array.isArray(style) ? style : [style]).reduce(
121
+ (acc, part) => ({ ...acc, ...(part || {}) }),
122
+ {}
123
+ );
124
+
125
+ describe("TvActionButtons rendered tree", () => {
126
+ beforeEach(() => {
127
+ jest.clearAllMocks();
128
+ });
129
+
130
+ it("preserves the rendered primitive tree when the tv builder is wrapped with DataProvider", () => {
131
+ mockUseActions.mockImplementation((identifier) => {
132
+ if (identifier === "") {
133
+ return {
134
+ actions: {
135
+ navigation_action: {
136
+ module: {
137
+ context: {
138
+ _currentValue: {
139
+ isActionAvailable: jest.fn(() => true),
140
+ },
141
+ },
142
+ },
143
+ },
144
+ },
145
+ };
146
+ }
147
+
148
+ return buildActionContext();
149
+ });
150
+
151
+ const node = configInflater(
152
+ item,
153
+ TvActionButtons({
154
+ value: (key) => tvConfiguration[key],
155
+ platformValue: (key) => tvConfiguration[key],
156
+ configuration: tvConfiguration,
157
+ state: "focused",
158
+ skipButtons: false,
159
+ })
160
+ );
161
+
162
+ const { UNSAFE_getAllByType, getByText } = render(
163
+ <React.Fragment>
164
+ {elementMapper(defaultComponents)(node as never)}
165
+ </React.Fragment>
166
+ );
167
+
168
+ const views = UNSAFE_getAllByType(View);
169
+ const text = getByText("Play");
170
+
171
+ const labelContainer = views.find(
172
+ (view) => mergeStyle(view.props.style).flexDirection === "column"
173
+ );
174
+
175
+ const buttonView = views.find((view) => {
176
+ const style = mergeStyle(view.props.style);
177
+
178
+ return style.display === "flex" && style.alignItems === "center";
179
+ });
180
+
181
+ const outerContainer = views.find((view) => {
182
+ const style = mergeStyle(view.props.style);
183
+
184
+ return style.flexDirection === "row" && style.justifyContent === "center";
185
+ });
186
+
187
+ expect(mergeStyle(outerContainer?.props.style)).toEqual(
188
+ expect.objectContaining({
189
+ flexDirection: "row",
190
+ justifyContent: "center",
191
+ marginTop: 1,
192
+ marginRight: 2,
193
+ marginBottom: 3,
194
+ marginLeft: 4,
195
+ })
196
+ );
197
+
198
+ expect(mergeStyle(buttonView?.props.style)).toEqual(
199
+ expect.objectContaining({
200
+ display: "flex",
201
+ alignItems: "center",
202
+ paddingTop: 11,
203
+ paddingRight: 12,
204
+ paddingBottom: 13,
205
+ paddingLeft: 14,
206
+ backgroundColor: "rgba(1,1,1,1)",
207
+ borderColor: "rgba(2,2,2,1)",
208
+ borderWidth: 1,
209
+ borderRadius: 8,
210
+ })
211
+ );
212
+
213
+ expect(mergeStyle(labelContainer?.props.style)).toEqual(
214
+ expect.objectContaining({
215
+ flexDirection: "column",
216
+ })
217
+ );
218
+
219
+ expect(UNSAFE_getAllByType(RNText)).toHaveLength(1);
220
+
221
+ expect(mergeStyle(text.props.style)).toEqual(
222
+ expect.objectContaining({
223
+ fontSize: 18,
224
+ lineHeight: 24,
225
+ letterSpacing: -0.2,
226
+ fontFamily: "Ubuntu-Bold",
227
+ color: "rgba(10,10,10,1)",
228
+ })
229
+ );
230
+ });
231
+ });
@@ -1,12 +1,6 @@
1
- import { times } from "@applicaster/zapp-react-native-utils/utils";
2
- import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
3
-
4
1
  import { Button } from "./Button";
5
- import {
6
- getButtonsCount,
7
- getPluginIdentifier,
8
- mapSelfAlignment,
9
- } from "./utils";
2
+ import { memoizedGetPluginIdentifier } from "./utils";
3
+ import { buildActionButtonsModel } from "../../ActionButtonsCore/model";
10
4
 
11
5
  import { compact } from "@applicaster/zapp-react-native-utils/cellUtils";
12
6
  import { PREFIX, BUTTON_PREFIX } from "./const";
@@ -16,11 +10,9 @@ export {
16
10
  insertButtonsBetweenLabelContainers,
17
11
  } from "./utils";
18
12
 
19
- const buttonId = (index: number) => `${BUTTON_PREFIX}_${index}`;
20
-
21
13
  type Props = {
22
- value: Function;
23
- platformValue: Function;
14
+ value: (key: string) => unknown;
15
+ platformValue: (key: string) => unknown;
24
16
  configuration: Record<string, unknown>;
25
17
  state: string;
26
18
  skipButtons: boolean;
@@ -33,58 +25,65 @@ export const TvActionButtons = ({
33
25
  state,
34
26
  skipButtons,
35
27
  }: Props) => {
36
- if (skipButtons || !value(`${PREFIX}_container_buttons_enabled`)) {
28
+ if (skipButtons) {
37
29
  return null;
38
30
  }
39
31
 
40
- const buttonsCount = getButtonsCount(configuration, PREFIX);
32
+ const model = buildActionButtonsModel({
33
+ configuration,
34
+ value,
35
+ containerPrefix: `${PREFIX}_container`,
36
+ buttonPrefix: BUTTON_PREFIX,
37
+ });
41
38
 
42
- if (buttonsCount <= 0) {
39
+ if (!model) {
43
40
  return null;
44
41
  }
45
42
 
46
- const independentStyles = value(`${PREFIX}_container_independent_styles`);
47
-
48
43
  return {
49
44
  type: "ButtonContainerView",
50
45
  style: {
51
46
  flexDirection: "row",
52
-
53
- justifyContent: mapSelfAlignment(value(`${PREFIX}_container_align`)),
54
-
55
- marginTop: toNumberWithDefaultZero(
56
- value(`${PREFIX}_container_margin_top`)
57
- ),
58
- marginRight: toNumberWithDefaultZero(
59
- value(`${PREFIX}_container_margin_right`)
60
- ),
61
- marginBottom: toNumberWithDefaultZero(
62
- value(`${PREFIX}_container_margin_bottom`)
63
- ),
64
- marginLeft: toNumberWithDefaultZero(
65
- value(`${PREFIX}_container_margin_left`)
66
- ),
47
+ justifyContent: model.container.horizontalAlign,
48
+ marginTop: model.container.margins.top,
49
+ marginRight: model.container.margins.right,
50
+ marginBottom: model.container.margins.bottom,
51
+ marginLeft: model.container.margins.left,
67
52
  },
68
53
  additionalProps: {
69
- horizontalGutter: toNumberWithDefaultZero(
70
- value(`${PREFIX}_container_horizontal_gutter`)
71
- ),
54
+ horizontalGutter: model.container.horizontalGutter,
72
55
  state,
73
- buttonsCount,
56
+ buttonsCount: model.buttonsCount,
74
57
  },
75
58
  elements: compact(
76
- times((index) => {
77
- const prefixSpecificButton = buttonId(index + 1);
78
-
79
- return Button({
80
- prefix: independentStyles ? prefixSpecificButton : buttonId(1),
81
- value,
82
- platformValue,
83
- pluginIdentifier: getPluginIdentifier(configuration, PREFIX, index),
84
- suffixId: prefixSpecificButton,
85
- preferredFocus: index === 0,
86
- });
87
- }, buttonsCount)
59
+ model.buttons.map(
60
+ ({ slot, renderIndex, specificPrefix, stylePrefix }) => {
61
+ return {
62
+ type: "DataProvider",
63
+ data: [
64
+ {
65
+ func: "identity",
66
+ args: [],
67
+ propName: "entry",
68
+ },
69
+ ],
70
+ elements: [
71
+ Button({
72
+ prefix: stylePrefix,
73
+ value,
74
+ platformValue,
75
+ pluginIdentifier: memoizedGetPluginIdentifier(
76
+ configuration,
77
+ PREFIX,
78
+ slot
79
+ ) as string,
80
+ suffixId: specificPrefix,
81
+ preferredFocus: renderIndex === 0,
82
+ }),
83
+ ],
84
+ };
85
+ }
86
+ )
88
87
  ),
89
88
  };
90
89
  };