@applicaster/zapp-react-native-ui-components 15.0.0-rc.13 → 15.0.0-rc.132

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 (189) hide show
  1. package/Components/AnimatedInOut/index.tsx +69 -26
  2. package/Components/BaseFocusable/index.ios.ts +12 -2
  3. package/Components/Cell/Cell.tsx +14 -3
  4. package/Components/Cell/CellWithFocusable.tsx +9 -0
  5. package/Components/Cell/FocusableWrapper.tsx +3 -0
  6. package/Components/Cell/TvOSCellComponent.tsx +30 -6
  7. package/Components/Focusable/Focusable.tsx +4 -2
  8. package/Components/Focusable/FocusableTvOS.tsx +18 -1
  9. package/Components/Focusable/__tests__/__snapshots__/FocusableTvOS.test.tsx.snap +1 -0
  10. package/Components/FocusableGroup/FocusableTvOS.tsx +32 -1
  11. package/Components/GeneralContentScreen/GeneralContentScreen.tsx +39 -28
  12. package/Components/GeneralContentScreen/__tests__/GeneralContentScreen.test.tsx +104 -0
  13. package/Components/GeneralContentScreen/utils/__tests__/getScreenDataSource.test.ts +19 -0
  14. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  15. package/Components/GeneralContentScreen/utils/getScreenDataSource.ts +9 -0
  16. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
  17. package/Components/HandlePlayable/HandlePlayable.tsx +33 -94
  18. package/Components/HandlePlayable/const.ts +3 -0
  19. package/Components/HandlePlayable/utils.ts +105 -0
  20. package/Components/HookRenderer/HookRenderer.tsx +40 -10
  21. package/Components/HookRenderer/__tests__/HookRenderer.test.tsx +60 -0
  22. package/Components/Layout/TV/LayoutBackground.tsx +5 -2
  23. package/Components/Layout/TV/NavBarContainer.tsx +1 -10
  24. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  25. package/Components/Layout/TV/__tests__/__snapshots__/NavBarContainer.test.tsx.snap +7 -12
  26. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +7 -12
  27. package/Components/Layout/TV/index.tsx +3 -4
  28. package/Components/Layout/TV/index.web.tsx +3 -4
  29. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  30. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/model.test.ts +80 -0
  31. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +187 -0
  32. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/selectors.test.ts +45 -0
  33. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/style.test.ts +49 -0
  34. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/model.ts +47 -0
  35. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +170 -0
  36. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/selectors.ts +26 -0
  37. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/style.ts +29 -0
  38. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/types.ts +37 -0
  39. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +16 -1
  40. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +30 -2
  41. package/Components/MasterCell/DefaultComponents/Button.tsx +0 -15
  42. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
  43. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  44. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  45. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  46. package/Components/MasterCell/DefaultComponents/LiveImage/__tests__/prepareEntry.test.ts +352 -0
  47. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +136 -0
  48. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +43 -22
  49. package/Components/MasterCell/DefaultComponents/PressableView.tsx +196 -0
  50. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
  51. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
  52. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
  53. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
  54. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  55. package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
  56. package/Components/MasterCell/DefaultComponents/Text/index.tsx +10 -14
  57. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  58. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Asset.ts +46 -0
  59. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +126 -0
  60. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ButtonContainerView.ts +23 -0
  61. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +16 -0
  62. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabel.ts +67 -0
  63. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +32 -0
  64. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +191 -0
  65. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/builders.test.ts +140 -0
  66. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +222 -0
  67. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/helpers.ts +105 -0
  68. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +104 -0
  69. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +118 -0
  70. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +73 -0
  71. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/__tests__/index.test.ts +86 -0
  72. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +35 -48
  73. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +115 -29
  74. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +39 -144
  75. package/Components/MasterCell/elementMapper.tsx +1 -0
  76. package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
  77. package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
  78. package/Components/MasterCell/index.tsx +2 -0
  79. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
  80. package/Components/MasterCell/utils/index.ts +61 -31
  81. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
  82. package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
  83. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
  84. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
  85. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  86. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  87. package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
  88. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  89. package/Components/PlayerContainer/PlayerContainer.tsx +43 -64
  90. package/Components/PlayerImageBackground/index.tsx +3 -22
  91. package/Components/PreloaderWrapper/__tests__/index.test.tsx +26 -0
  92. package/Components/PreloaderWrapper/index.tsx +15 -0
  93. package/Components/River/ComponentsMap/ComponentsMap.tsx +16 -0
  94. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  95. package/Components/River/RefreshControl.tsx +36 -13
  96. package/Components/River/RiverItem.tsx +26 -20
  97. package/Components/River/TV/River.tsx +31 -14
  98. package/Components/River/TV/index.tsx +8 -4
  99. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  100. package/Components/River/TV/utils/index.ts +4 -0
  101. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  102. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  103. package/Components/River/__tests__/componentsMap.test.js +38 -0
  104. package/Components/Screen/TV/index.web.tsx +4 -2
  105. package/Components/Screen/__tests__/Screen.test.tsx +66 -42
  106. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  107. package/Components/Screen/hooks.ts +75 -6
  108. package/Components/Screen/index.tsx +9 -4
  109. package/Components/Screen/navigationHandler.ts +49 -24
  110. package/Components/Screen/orientationHandler.ts +10 -13
  111. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +46 -0
  112. package/Components/ScreenFeedLoader/__tests__/ScreenFeedLoader.test.tsx +94 -0
  113. package/Components/ScreenFeedLoader/index.ts +1 -0
  114. package/Components/ScreenResolver/__tests__/screenResolver.test.js +24 -0
  115. package/Components/ScreenResolver/hooks/index.ts +3 -0
  116. package/Components/ScreenResolver/hooks/useGetComponent.ts +15 -0
  117. package/Components/ScreenResolver/hooks/useScreenComponentResolver.tsx +90 -0
  118. package/Components/ScreenResolver/index.tsx +15 -111
  119. package/Components/ScreenResolver/utils/__tests__/getScreenTypeProps.test.ts +45 -0
  120. package/Components/ScreenResolver/utils/getScreenTypeProps.ts +43 -0
  121. package/Components/ScreenResolver/utils/index.ts +1 -0
  122. package/Components/ScreenResolver/withDefaultScreenContext.tsx +16 -0
  123. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +25 -0
  124. package/Components/ScreenResolverFeedProvider/__tests__/ScreenResolverFeedProvider.test.tsx +44 -0
  125. package/Components/ScreenResolverFeedProvider/index.ts +1 -0
  126. package/Components/ScreenRevealManager/ScreenRevealManager.ts +40 -8
  127. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +86 -69
  128. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +44 -26
  129. package/Components/Tabs/TV/Tabs.tsx +20 -3
  130. package/Components/Tabs/TabContent.tsx +7 -4
  131. package/Components/Transitioner/Scene.tsx +10 -3
  132. package/Components/Transitioner/index.js +3 -3
  133. package/Components/VideoLive/LiveImageManager.ts +199 -54
  134. package/Components/VideoLive/PlayerLiveImageComponent.tsx +31 -33
  135. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +2 -17
  136. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +1 -0
  137. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +118 -171
  138. package/Components/VideoModal/ModalAnimation/index.ts +2 -13
  139. package/Components/VideoModal/ModalAnimation/utils.ts +1 -327
  140. package/Components/VideoModal/PlayerWrapper.tsx +14 -88
  141. package/Components/VideoModal/VideoModal.tsx +1 -5
  142. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  143. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  144. package/Components/VideoModal/hooks/useModalSize.ts +10 -5
  145. package/Components/VideoModal/playerWrapperStyle.ts +70 -0
  146. package/Components/VideoModal/playerWrapperUtils.ts +91 -0
  147. package/Components/VideoModal/utils.ts +19 -9
  148. package/Components/Viewport/ViewportAware/__tests__/viewportAware.test.js +0 -2
  149. package/Components/Viewport/ViewportAware/index.tsx +16 -7
  150. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  151. package/Components/ZappUIComponent/index.tsx +12 -6
  152. package/Components/default-cell-renderer/viewTrees/mobile/index.ts +0 -3
  153. package/Components/index.js +1 -1
  154. package/Contexts/ScreenContext/__tests__/index.test.tsx +57 -0
  155. package/Contexts/ScreenContext/index.tsx +71 -19
  156. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  157. package/Contexts/ZappHookModalContext/index.tsx +37 -61
  158. package/Contexts/ZappPipesContext/ZappPipesContextFactory.tsx +18 -7
  159. package/Contexts/index.ts +0 -2
  160. package/Decorators/Analytics/index.tsx +6 -5
  161. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  162. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  163. package/Decorators/ZappPipesDataConnector/ResolverSelector.tsx +25 -7
  164. package/Decorators/ZappPipesDataConnector/__tests__/ResolverSelector.test.tsx +212 -5
  165. package/Decorators/ZappPipesDataConnector/__tests__/UrlFeedResolver.test.tsx +39 -21
  166. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  167. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  168. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  169. package/Decorators/ZappPipesDataConnector/resolvers/UrlFeedResolver.tsx +18 -7
  170. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  171. package/Helpers/DataSourceHelper/index.ts +19 -0
  172. package/events/index.ts +3 -0
  173. package/events/scrollEndReached.ts +15 -0
  174. package/index.d.ts +7 -0
  175. package/package.json +6 -5
  176. package/Components/MasterCell/DefaultComponents/Text/utils/__tests__/withAdjustedLineHeight.test.ts +0 -46
  177. package/Components/MasterCell/DefaultComponents/Text/utils/index.ts +0 -21
  178. package/Components/PlayerContainer/ErrorDisplay/ErrorDisplay.tsx +0 -57
  179. package/Components/PlayerContainer/ErrorDisplay/index.ts +0 -9
  180. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  181. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +0 -60
  182. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +0 -417
  183. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.web.tsx +0 -294
  184. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +0 -176
  185. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.web.tsx +0 -93
  186. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +0 -500
  187. package/Components/VideoModal/ModalAnimation/__tests__/getMoveUpValue.test.ts +0 -108
  188. package/Helpers/DataSourceHelper/index.js +0 -19
  189. /package/Components/HookRenderer/{index.tsx → index.ts} +0 -0
@@ -0,0 +1,73 @@
1
+ import {
2
+ insertBetweenLabelContainers,
3
+ insertBetweenLabels,
4
+ } from "../../../ActionButtonsCore/placement";
5
+
6
+ export const insertButtonsBetweenLabels = (
7
+ configuration: Record<string, unknown>,
8
+ buttons,
9
+ labels = []
10
+ ) =>
11
+ insertBetweenLabels(
12
+ {
13
+ position: configuration?.mobile_buttons_container_position as
14
+ | string
15
+ | undefined,
16
+ allowOnTop: false,
17
+ appendWhenMissing: false,
18
+ },
19
+ buttons,
20
+ labels // "text_label_1", "text_label_2", "text_label_3", "text_label_4"
21
+ );
22
+
23
+ export const insertButtonsBetweenLabelContainers = (
24
+ configuration: Record<string, unknown>,
25
+ buttons,
26
+ labelContainers = []
27
+ ) =>
28
+ insertBetweenLabelContainers(
29
+ {
30
+ position: configuration?.mobile_buttons_container_position as
31
+ | string
32
+ | undefined,
33
+ allowOnTop: false,
34
+ appendWhenMissing: true,
35
+ },
36
+ buttons,
37
+ labelContainers // top_label_1, top_label_2, bottom_label_1, bottom_label_2, etc.
38
+ );
39
+
40
+ export const mobileOverImagePositionStyles = (position: string) => {
41
+ switch (position) {
42
+ case "top_left":
43
+ return {
44
+ justifyContent: "flex-start",
45
+ alignItems: "flex-start",
46
+ };
47
+ case "top_right":
48
+ return {
49
+ justifyContent: "flex-start",
50
+ alignItems: "flex-end",
51
+ };
52
+ case "bottom_left":
53
+ return {
54
+ justifyContent: "flex-end",
55
+ alignItems: "flex-start",
56
+ };
57
+ case "bottom_right":
58
+ return {
59
+ justifyContent: "flex-end",
60
+ alignItems: "flex-end",
61
+ };
62
+ case "center":
63
+ default:
64
+ return {
65
+ justifyContent: "center",
66
+ alignItems: "center",
67
+ top: 0,
68
+ left: 0,
69
+ right: 0,
70
+ bottom: 0,
71
+ };
72
+ }
73
+ };
@@ -0,0 +1,86 @@
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.style.justifyContent).toBe("center");
41
+ expect(result.additionalProps.buttonsCount).toBe(2);
42
+ expect(result.elements).toHaveLength(2);
43
+
44
+ expect(result.elements[0].additionalProps.pluginIdentifier).toBe(
45
+ "action_1"
46
+ );
47
+
48
+ expect(result.elements[0].additionalProps.suffixId).toBe(
49
+ "tv_buttons_button_1"
50
+ );
51
+
52
+ expect(result.elements[0].additionalProps.preferredFocus).toBe(true);
53
+
54
+ expect(result.elements[1].additionalProps.pluginIdentifier).toBe(
55
+ "action_3"
56
+ );
57
+
58
+ expect(result.elements[1].additionalProps.suffixId).toBe(
59
+ "tv_buttons_button_3"
60
+ );
61
+
62
+ expect(result.elements[1].additionalProps.preferredFocus).toBe(false);
63
+ });
64
+
65
+ it("keeps shared button styles from slot 1 while preserving slot 3 identity", () => {
66
+ const value = (key) => baseConfiguration[key];
67
+
68
+ const result = TvActionButtons({
69
+ value,
70
+ platformValue,
71
+ configuration: baseConfiguration,
72
+ state: "focused",
73
+ skipButtons: false,
74
+ });
75
+
76
+ expect(result.elements[1].style.paddingTop).toBe(11);
77
+
78
+ expect(result.elements[1].additionalProps.pluginIdentifier).toBe(
79
+ "action_3"
80
+ );
81
+
82
+ expect(result.elements[1].additionalProps.suffixId).toBe(
83
+ "tv_buttons_button_3"
84
+ );
85
+ });
86
+ });
@@ -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,53 @@ 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 Button({
62
+ prefix: stylePrefix,
63
+ value,
64
+ platformValue,
65
+ pluginIdentifier: memoizedGetPluginIdentifier(
66
+ configuration,
67
+ PREFIX,
68
+ slot
69
+ ) as string,
70
+ suffixId: specificPrefix,
71
+ preferredFocus: renderIndex === 0,
72
+ });
73
+ }
74
+ )
88
75
  ),
89
76
  };
90
77
  };
@@ -1,53 +1,123 @@
1
- import { getPluginIdentifier } from "..";
1
+ import { getPluginIdentifier, memoizedGetPluginIdentifier } from "..";
2
2
 
3
3
  describe("getPluginIdentifier", () => {
4
4
  const prefix = "tv_buttons";
5
5
 
6
- it("get first plugin identifier", () => {
6
+ beforeAll(() => {
7
+ memoizedGetPluginIdentifier.clear();
8
+ });
9
+
10
+ it("returns the requested slot action identifier", () => {
11
+ expect.assertions(2);
12
+
7
13
  const configuration = {
8
- tv_buttons_button_1_other: "value",
9
14
  tv_buttons_button_1_assign_action:
10
15
  "tv_buttons_button_1_assign_action_value",
11
- tv_buttons_button_2_assign_action:
12
- "tv_buttons_button_2_assign_action_value",
16
+ tv_buttons_button_3_assign_action:
17
+ "tv_buttons_button_3_assign_action_value",
13
18
  };
14
19
 
15
- const index = 0;
20
+ const slot = 3;
21
+ const result = getPluginIdentifier(configuration, prefix, slot);
16
22
 
17
- const result = getPluginIdentifier(configuration, prefix, index);
23
+ const memoizedResult = memoizedGetPluginIdentifier(
24
+ configuration,
25
+ prefix,
26
+ slot
27
+ );
18
28
 
19
- expect(result).toEqual(configuration.tv_buttons_button_1_assign_action);
29
+ expect(result).toEqual(configuration.tv_buttons_button_3_assign_action);
30
+ expect(memoizedResult).toEqual(result);
20
31
  });
21
32
 
22
- it("get second plugin identifier", () => {
33
+ it("returns undefined for a disabled or missing slot", () => {
34
+ expect.assertions(2);
35
+
23
36
  const configuration = {
24
- tv_buttons_button_1_other: "value_1",
25
- tv_buttons_button_1_assign_action:
26
- "tv_buttons_button_1_assign_action_value",
27
- tv_buttons_button_2_other: "value_2",
28
- tv_buttons_button_2_assign_action:
29
- "tv_buttons_button_2_assign_action_value",
37
+ tv_buttons_button_1_assign_action: "tv_buttons_button_1_assign_action",
30
38
  };
31
39
 
32
- const index = 1;
40
+ const slot = 2;
41
+ const result = getPluginIdentifier(configuration, prefix, slot);
33
42
 
34
- const result = getPluginIdentifier(configuration, prefix, index);
43
+ const memoizedResult = memoizedGetPluginIdentifier(
44
+ configuration,
45
+ prefix,
46
+ slot
47
+ );
35
48
 
36
- expect(result).toEqual(configuration.tv_buttons_button_2_assign_action);
49
+ expect(result).toBeUndefined();
50
+ expect(memoizedResult).toEqual(result);
51
+ });
52
+
53
+ it("returns undefined for empty string values", () => {
54
+ expect.assertions(2);
55
+
56
+ const configuration = {
57
+ tv_buttons_button_2_assign_action: "",
58
+ };
59
+
60
+ const slot = 2;
61
+
62
+ const result = getPluginIdentifier(configuration, prefix, slot);
63
+
64
+ const memoizedResult = memoizedGetPluginIdentifier(
65
+ configuration,
66
+ prefix,
67
+ slot
68
+ );
69
+
70
+ expect(result).toBeUndefined();
71
+ expect(memoizedResult).toBeUndefined();
37
72
  });
38
73
 
39
- it("get undefined if no assign_actions at all", () => {
40
- const configuration = {};
74
+ it("returns undefined for negative slots", () => {
75
+ expect.assertions(2);
76
+
77
+ const configuration = {
78
+ tv_buttons_button_1_assign_action: "a",
79
+ tv_buttons_button_2_assign_action: "b",
80
+ };
41
81
 
42
- const index = 0;
82
+ const slot = -1;
83
+ const result = getPluginIdentifier(configuration, prefix, slot);
43
84
 
44
- const result = getPluginIdentifier(configuration, prefix, index);
85
+ const memoizedResult = memoizedGetPluginIdentifier(
86
+ configuration,
87
+ prefix,
88
+ slot
89
+ );
45
90
 
46
91
  expect(result).toBeUndefined();
92
+ expect(memoizedResult).toBeUndefined();
93
+ });
94
+
95
+ it("handles non-string values by returning the configured slot value", () => {
96
+ expect.assertions(2);
97
+
98
+ const configuration = {
99
+ tv_buttons_button_2_assign_action: true,
100
+ };
101
+
102
+ const slot = 2;
103
+ const result = getPluginIdentifier(configuration, prefix, slot);
104
+
105
+ const memoizedResult = memoizedGetPluginIdentifier(
106
+ configuration,
107
+ prefix,
108
+ slot
109
+ );
110
+
111
+ expect(result).toBe(true);
112
+ expect(memoizedResult).toBe(true);
47
113
  });
48
114
  });
49
115
 
50
- describe("getPluginIdentifier - when configuration has same values for different keys", () => {
116
+ describe("getPluginIdentifier - when configuration has duplicate values", () => {
117
+ beforeAll(() => {
118
+ memoizedGetPluginIdentifier.clear();
119
+ });
120
+
51
121
  const prefix = "tv_buttons";
52
122
 
53
123
  const configuration = {
@@ -55,19 +125,35 @@ describe("getPluginIdentifier - when configuration has same values for different
55
125
  tv_buttons_button_2_assign_action: "navigation_action",
56
126
  };
57
127
 
58
- it("get first plugin identifier", () => {
59
- const index = 0;
128
+ it("returns the requested slot even when values are identical", () => {
129
+ expect.assertions(2);
60
130
 
61
- const result = getPluginIdentifier(configuration, prefix, index);
131
+ const slot = 1;
132
+ const result = getPluginIdentifier(configuration, prefix, slot);
133
+
134
+ const memoizedResult = memoizedGetPluginIdentifier(
135
+ configuration,
136
+ prefix,
137
+ slot
138
+ );
62
139
 
63
140
  expect(result).toEqual(configuration.tv_buttons_button_1_assign_action);
141
+ expect(memoizedResult).toEqual(result);
64
142
  });
65
143
 
66
- it("get second plugin identifier", () => {
67
- const index = 1;
144
+ it("returns the second slot when values are identical", () => {
145
+ expect.assertions(2);
146
+
147
+ const slot = 2;
148
+ const result = getPluginIdentifier(configuration, prefix, slot);
68
149
 
69
- const result = getPluginIdentifier(configuration, prefix, index);
150
+ const memoizedResult = memoizedGetPluginIdentifier(
151
+ configuration,
152
+ prefix,
153
+ slot
154
+ );
70
155
 
71
156
  expect(result).toEqual(configuration.tv_buttons_button_2_assign_action);
157
+ expect(memoizedResult).toEqual(result);
72
158
  });
73
159
  });
@@ -1,173 +1,68 @@
1
- import * as R from "ramda";
2
-
1
+ import memoizee from "memoizee";
3
2
  import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
4
- import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
3
+ import { getEnabledButtonSlots } from "../../../ActionButtonsCore/selectors";
4
+ import {
5
+ insertBetweenLabelContainers,
6
+ insertBetweenLabels,
7
+ } from "../../../ActionButtonsCore/placement";
5
8
 
6
9
  export const getButtonsCount = (
7
10
  configuration: Record<string, unknown>,
8
11
  prefix: string
9
12
  ): number => {
10
- const button1Key = `${prefix}_button_1_button_enabled`;
11
- const button2Key = `${prefix}_button_2_button_enabled`;
12
- const button3Key = `${prefix}_button_3_button_enabled`;
13
-
14
- return R.toPairs(configuration).filter(
15
- ([key, value]) =>
16
- [button1Key, button2Key, button3Key].includes(key) && value
17
- ).length;
13
+ return getEnabledButtonSlots(configuration, `${prefix}_button`).length;
18
14
  };
19
15
 
20
16
  export const getPluginIdentifier = (
21
17
  configuration: Record<string, unknown>,
22
18
  prefix: string,
23
- index: number
24
- ): string => {
25
- const match = `${prefix}_button_\\d_assign_action`;
26
- const re = new RegExp(match);
27
-
28
- const rejectNils = R.compose(R.path([index]), R.filter(isNotNil));
29
-
30
- return rejectNils(
31
- R.toPairs(configuration)
32
- .filter(([key]) => R.startsWith(`${prefix}_button`, key))
33
- .map(([key, value]) => {
34
- const matched = key.match(re);
35
-
36
- return matched ? value : undefined;
37
- })
38
- );
39
- };
40
-
41
- type Label = {
42
- name: string;
43
- };
19
+ slot: number
20
+ ): string | undefined => {
21
+ const value = configuration[
22
+ `${prefix}_button_${slot}_assign_action`
23
+ ] as string;
44
24
 
45
- type LabelContainer = {
46
- elements: Array<Label>;
25
+ return value == null || value === "" ? undefined : value;
47
26
  };
48
27
 
49
- type LabelExtraContainer = {
50
- elements: Array<LabelContainer>;
51
- };
52
-
53
- const withButtons = (labelName, buttons, labels) => {
54
- return labels.reduce((acc, label) => {
55
- if (label.name === labelName) {
56
- return [...acc, label, buttons];
57
- }
58
-
59
- return [...acc, label];
60
- }, []);
61
- };
62
-
63
- const withButtonsInContainer = (labelName, buttons, views) => {
64
- return views.map((view) => {
65
- return {
66
- ...view,
67
- elements: view.elements.reduce((acc, label) => {
68
- if (label.name === labelName) {
69
- return [...acc, label, buttons];
70
- }
71
-
72
- return [...acc, label];
73
- }, []),
74
- };
75
- });
76
- };
28
+ export const memoizedGetPluginIdentifier = memoizee(getPluginIdentifier);
77
29
 
78
30
  export const insertButtonsBetweenLabels = (
79
31
  configuration: Record<string, unknown>,
80
32
  buttons,
81
- labels: Label[] = []
82
- ) => {
83
- if (R.isNil(buttons)) {
84
- return labels;
85
- }
86
-
87
- const position = R.pathOr(
88
- undefined,
89
- ["tv_buttons_container_position"],
90
- configuration
33
+ labels = []
34
+ ) =>
35
+ insertBetweenLabels(
36
+ {
37
+ position: configuration.tv_buttons_container_position as
38
+ | string
39
+ | undefined,
40
+ allowOnTop: true,
41
+ appendWhenMissing: true,
42
+ },
43
+ buttons,
44
+ labels
91
45
  );
92
46
 
93
- if (position === "on_top") {
94
- return [buttons, ...labels];
95
- }
96
-
97
- const labelsWithButtons = withButtons(position, buttons, labels);
98
-
99
- if (R.equals(labelsWithButtons, labels)) {
100
- // put buttons after all labels by default
101
- return [...labels, buttons];
102
- }
103
-
104
- return labelsWithButtons;
105
- };
106
-
107
47
  export const insertButtonsBetweenLabelContainers = (
108
48
  configuration: Record<string, unknown>,
109
49
  buttons,
110
- labelContainers: Array<LabelExtraContainer> = []
111
- ) => {
112
- if (R.isNil(buttons)) {
113
- return labelContainers;
114
- }
115
-
116
- if (R.isEmpty(labelContainers)) {
117
- return [buttons];
118
- }
119
-
120
- const position = R.pathOr(
121
- undefined,
122
- ["tv_buttons_container_position"],
123
- configuration
50
+ labelContainers = []
51
+ ) =>
52
+ insertBetweenLabelContainers(
53
+ {
54
+ position: configuration.tv_buttons_container_position as
55
+ | string
56
+ | undefined,
57
+ allowOnTop: true,
58
+ appendWhenMissing: true,
59
+ },
60
+ buttons,
61
+ labelContainers
124
62
  );
125
63
 
126
- if (position === "on_top") {
127
- const firstContainerLabels = R.lensPath([0, "elements"]);
128
-
129
- return R.over(firstContainerLabels, R.prepend(buttons), labelContainers);
130
- }
131
-
132
- const labelContainersWithButtons = labelContainers.map((labelContainer) => {
133
- return {
134
- ...labelContainer,
135
- elements: withButtonsInContainer(
136
- position,
137
- buttons,
138
- labelContainer.elements
139
- ),
140
- };
141
- });
142
-
143
- if (R.equals(labelContainersWithButtons, labelContainers)) {
144
- // put buttons in last container
145
- const lastContainerLabels = R.lensPath([
146
- labelContainers.length - 1,
147
- "elements",
148
- ]);
149
-
150
- return R.over(lastContainerLabels, R.append(buttons), labelContainers);
151
- }
152
-
153
- return labelContainersWithButtons;
154
- };
155
-
156
- export const mapSelfAlignment = (horizontalPosition) => {
157
- switch (horizontalPosition) {
158
- case "left":
159
- return "flex-start";
160
- case "right":
161
- return "flex-end";
162
- case "center":
163
- return "center";
164
- default:
165
- return "flex-start";
166
- }
167
- };
168
-
169
64
  export const getTextTransform = (textTransform: string) => {
170
- if (R.isNil(textTransform) || R.isEmpty(textTransform)) {
65
+ if (textTransform == null || textTransform === "") {
171
66
  return "none";
172
67
  }
173
68
 
@@ -19,6 +19,7 @@ const focusableTypes = new Set([
19
19
  "View",
20
20
  "ButtonContainerView",
21
21
  "FocusableView",
22
+ "PressableView",
22
23
  "CollapsibleTextContainer",
23
24
  "BorderContainerView",
24
25
  ]);