@applicaster/zapp-react-native-ui-components 16.0.0-rc.12 → 16.0.0-rc.121

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 (166) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/Cell/Cell.tsx +3 -6
  5. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  6. package/Components/CellRendererResolver/index.ts +1 -1
  7. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  8. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  9. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  10. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  11. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  12. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  15. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  16. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  17. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  18. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -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/Layout/FullWidthRow.tsx +38 -0
  24. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  25. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  26. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  27. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  28. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  29. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  30. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  31. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  32. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  35. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  36. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -6
  37. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  38. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  39. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  40. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  41. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  42. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  43. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  44. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  45. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  46. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +34 -21
  47. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  48. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  49. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  50. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  51. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  52. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  53. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  54. package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
  55. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  56. package/Components/MasterCell/dataAdapter.ts +15 -5
  57. package/Components/MasterCell/hooks/index.ts +2 -0
  58. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  59. package/Components/MasterCell/index.tsx +57 -8
  60. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  61. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  62. package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
  63. package/Components/MasterCell/utils/index.ts +103 -46
  64. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  65. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  66. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  67. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  68. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  69. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  70. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  71. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  72. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1028 -0
  73. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  74. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +164 -0
  75. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  76. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  77. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +433 -0
  78. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +563 -0
  79. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  80. package/Components/ModalComponent/BottomSheetModalContent.tsx +440 -50
  81. package/Components/ModalComponent/Button/index.tsx +5 -3
  82. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  83. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  84. package/Components/ModalComponent/Header/index.tsx +20 -7
  85. package/Components/ModalComponent/Header/utils.ts +1 -1
  86. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  87. package/Components/ModalComponent/SortableList.tsx +156 -0
  88. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  89. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  90. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  91. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  92. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  93. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  94. package/Components/ModalComponent/__tests__/showTextInput.test.ts +120 -0
  95. package/Components/ModalComponent/index.tsx +18 -9
  96. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  97. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  98. package/Components/ModalComponent/presets/index.ts +27 -0
  99. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  100. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  101. package/Components/ModalComponent/presets/types.ts +35 -0
  102. package/Components/ModalComponent/showTextInput.ts +54 -0
  103. package/Components/ModalComponent/utils.ts +82 -21
  104. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  105. package/Components/OfflineHandler/__tests__/index.test.tsx +186 -17
  106. package/Components/OfflineHandler/hooks.ts +111 -0
  107. package/Components/OfflineHandler/index.tsx +91 -75
  108. package/Components/OfflineHandler/utils/index.ts +1 -13
  109. package/Components/PlayerContainer/PlayerContainer.tsx +28 -11
  110. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  111. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  112. package/Components/PreloaderWrapper/index.tsx +1 -1
  113. package/Components/River/ComponentsMap/ComponentsMap.tsx +91 -62
  114. package/Components/River/River.tsx +4 -0
  115. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +142 -89
  116. package/Components/River/__tests__/componentsMap.test.js +8 -3
  117. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  118. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  119. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  120. package/Components/Screen/index.tsx +13 -4
  121. package/Components/Screen/navigationHandler.ts +8 -8
  122. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  123. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  124. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  125. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  126. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  127. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  128. package/Components/TopCutoffOverlay/index.tsx +2 -2
  129. package/Components/Transitioner/AnimationManager.js +1 -1
  130. package/Components/Transitioner/Scene.tsx +4 -3
  131. package/Components/Transitioner/Transitioner.tsx +98 -49
  132. package/Components/Transitioner/__tests__/transitioner.test.js +372 -3
  133. package/Components/VideoLive/LiveImageManager.ts +9 -3
  134. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  135. package/Components/VideoModal/PlayerWrapper.tsx +4 -1
  136. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  137. package/Components/VideoModal/__tests__/playerWrapperStyle.test.ts +58 -0
  138. package/Components/VideoModal/__tests__/playerWrapperUtils.test.ts +16 -0
  139. package/Components/VideoModal/playerWrapperStyle.ts +21 -3
  140. package/Components/VideoModal/playerWrapperUtils.ts +7 -1
  141. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  142. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  143. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  144. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  145. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  146. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  147. package/Decorators/ZappPipesDataConnector/__tests__/screenToolbox.test.ts +347 -0
  148. package/Decorators/ZappPipesDataConnector/__tests__/screenToolboxUnavailable.test.ts +53 -0
  149. package/Decorators/ZappPipesDataConnector/index.tsx +19 -4
  150. package/Decorators/ZappPipesDataConnector/screenToolbox.ts +384 -0
  151. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  152. package/Helpers/index.js +0 -39
  153. package/Hooks/useEntryActionsExpander.ts +63 -0
  154. package/assets/empty-Info.png +0 -0
  155. package/package.json +6 -5
  156. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  157. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  158. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  159. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  160. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  161. package/Components/Screen/utils.ts +0 -16
  162. package/Helpers/Analytics/index.js +0 -95
  163. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  164. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  165. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  166. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -6,6 +6,7 @@ import {
6
6
  } from "./utils";
7
7
  import { Button } from "./Button";
8
8
  import { buildActionButtonsModel } from "../../ActionButtonsCore/model";
9
+ import { ViewStyle } from "react-native";
9
10
 
10
11
  const CONTAINER_PREFIX = "mobile_buttons_container";
11
12
  const BUTTON_PREFIX = "mobile_button";
@@ -42,9 +43,24 @@ export const MobileActionButtons = ({
42
43
  return null;
43
44
  }
44
45
 
46
+ const {
47
+ container: { stacking, verticalGutter, horizontalGutter },
48
+ } = model;
49
+
50
+ const isVertical = stacking === "vertical";
51
+ const isOverImage = position === "over_image";
52
+ const gutter = isVertical ? verticalGutter : horizontalGutter;
53
+
54
+ const containerPaddings = {
55
+ paddingTop: model.container.margins.top,
56
+ paddingRight: model.container.margins.right,
57
+ paddingBottom: model.container.margins.bottom,
58
+ paddingLeft: model.container.margins.left,
59
+ };
60
+
45
61
  const style = {
46
62
  alignItems: "center",
47
- ...(placement === "over_image"
63
+ ...(isOverImage
48
64
  ? {
49
65
  position: "absolute",
50
66
  zIndex: 10,
@@ -60,28 +76,18 @@ export const MobileActionButtons = ({
60
76
  };
61
77
 
62
78
  const contentStyle = {
63
- flexDirection: model.container.stacking === "vertical" ? "column" : "row",
64
- alignSelf:
65
- placement !== "over_image" ? model.container.horizontalAlign : undefined,
79
+ ...containerPaddings,
80
+ flexDirection: isVertical ? "column" : "row",
81
+ width: isVertical ? "100%" : undefined,
82
+ alignSelf: !isOverImage ? model.container.horizontalAlign : undefined,
66
83
  alignItems: model.container.horizontalAlign,
67
- marginTop: model.container.margins.top,
68
- marginRight: model.container.margins.right,
69
- marginBottom: model.container.margins.bottom,
70
- marginLeft: model.container.margins.left,
71
84
  };
72
85
 
73
86
  const elements = compact(
74
87
  model.buttons.map(({ renderIndex, specificPrefix, stylePrefix }) => {
75
88
  const isNotLast = renderIndex < model.buttons.length - 1;
76
89
 
77
- const {
78
- container: { stacking, verticalGutter, horizontalGutter },
79
- } = model;
80
-
81
- const isVertical = stacking === "vertical";
82
- const gutter = isVertical ? verticalGutter : horizontalGutter;
83
-
84
- const spacingStyle = {
90
+ const spacingStyle: ViewStyle = {
85
91
  [isVertical ? "marginBottom" : "marginRight"]: isNotLast ? gutter : 0,
86
92
  };
87
93
 
@@ -91,6 +97,7 @@ export const MobileActionButtons = ({
91
97
  stylePrefix,
92
98
  specificPrefix,
93
99
  spacingStyle,
100
+ isVertical,
94
101
  });
95
102
 
96
103
  if (!button) return null;
@@ -5,14 +5,19 @@ import {
5
5
  } from "..";
6
6
 
7
7
  describe("mobile action insertion helpers", () => {
8
- const labels = [{ name: "text_label_1" }, { name: "text_label_2" }];
8
+ const slots = [
9
+ { name: "text_label_1", element: { type: "View", id: "text_label_1" } },
10
+ { name: "text_label_2", element: { type: "View", id: "text_label_2" } },
11
+ ];
12
+
13
+ const labels = slots.map((slot) => slot.element);
9
14
  const buttons = { type: "View", name: "buttons" };
10
15
 
11
16
  it("inserts label buttons below target label", () => {
12
17
  const result = insertButtonsBetweenLabels(
13
18
  { mobile_buttons_container_position: "below_text_label_1" },
14
19
  buttons,
15
- labels
20
+ slots
16
21
  );
17
22
 
18
23
  expect(result).toEqual([labels[0], buttons, labels[1]]);
@@ -22,7 +27,7 @@ describe("mobile action insertion helpers", () => {
22
27
  const result = insertButtonsBetweenLabels(
23
28
  { mobile_buttons_container_position: "unknown" },
24
29
  buttons,
25
- labels
30
+ slots
26
31
  );
27
32
 
28
33
  expect(result).toEqual([labels[0], labels[1]]);
@@ -8,7 +8,7 @@ import { Platform } from "react-native";
8
8
  export const insertButtonsBetweenLabels = (
9
9
  configuration: Record<string, unknown>,
10
10
  buttons,
11
- labels = []
11
+ slots = [] // { name, element } per canonical text label slot
12
12
  ) =>
13
13
  insertBetweenLabels(
14
14
  {
@@ -19,7 +19,7 @@ export const insertButtonsBetweenLabels = (
19
19
  appendWhenMissing: false,
20
20
  },
21
21
  buttons,
22
- labels // "text_label_1", "text_label_2", "text_label_3", "text_label_4"
22
+ slots
23
23
  );
24
24
 
25
25
  export const insertButtonsBetweenLabelContainers = (
@@ -3,18 +3,20 @@ import { insertButtonsBetweenLabels } from "..";
3
3
  describe("insertButtonsBetweenLabels - depthlevel 1", () => {
4
4
  const buttons = "buttons";
5
5
 
6
- const labels = [
7
- { name: "label_1" },
8
- { name: "label_2" },
9
- { name: "label_3" },
6
+ const slots = [
7
+ { name: "label_1", element: { id: "label_1" } },
8
+ { name: "label_2", element: { id: "label_2" } },
9
+ { name: "label_3", element: { id: "label_3" } },
10
10
  ];
11
11
 
12
+ const labels = slots.map((slot) => slot.element);
13
+
12
14
  it("put buttons on top", () => {
13
15
  const configuration = {
14
16
  tv_buttons_container_position: "on_top",
15
17
  };
16
18
 
17
- const result = insertButtonsBetweenLabels(configuration, buttons, labels);
19
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
18
20
 
19
21
  expect(result).toEqual([buttons, ...labels]);
20
22
  });
@@ -24,7 +26,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
24
26
  tv_buttons_container_position: "unknown_position",
25
27
  };
26
28
 
27
- const result = insertButtonsBetweenLabels(configuration, buttons, labels);
29
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
28
30
 
29
31
  expect(result).toEqual([...labels, buttons]);
30
32
  });
@@ -34,7 +36,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
34
36
  tv_buttons_container_position: "label_1",
35
37
  };
36
38
 
37
- const result = insertButtonsBetweenLabels(configuration, buttons, labels);
39
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
38
40
 
39
41
  expect(result).toEqual([labels[0], buttons, labels[1], labels[2]]);
40
42
  });
@@ -44,7 +46,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
44
46
  tv_buttons_container_position: "label_2",
45
47
  };
46
48
 
47
- const result = insertButtonsBetweenLabels(configuration, buttons, labels);
49
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
48
50
 
49
51
  expect(result).toEqual([labels[0], labels[1], buttons, labels[2]]);
50
52
  });
@@ -54,22 +56,52 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
54
56
  tv_buttons_container_position: "label_3",
55
57
  };
56
58
 
57
- const result = insertButtonsBetweenLabels(configuration, buttons, labels);
59
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
58
60
 
59
61
  expect(result).toEqual([labels[0], labels[1], labels[2], buttons]);
60
62
  });
61
63
 
62
- it("put underneath all", () => {
64
+ it("put above label_2", () => {
65
+ const configuration = {
66
+ tv_buttons_container_position: "above_label_2",
67
+ };
68
+
69
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
70
+
71
+ expect(result).toEqual([labels[0], buttons, labels[1], labels[2]]);
72
+ });
73
+
74
+ it("keeps the slot when its label is empty (null element)", () => {
75
+ const slotsWithEmpty = [
76
+ { name: "label_1", element: { id: "label_1" } },
77
+ { name: "label_2", element: null },
78
+ { name: "label_3", element: { id: "label_3" } },
79
+ ];
80
+
81
+ const configuration = {
82
+ tv_buttons_container_position: "above_label_2",
83
+ };
84
+
85
+ const result = insertButtonsBetweenLabels(
86
+ configuration,
87
+ buttons,
88
+ slotsWithEmpty
89
+ );
90
+
91
+ expect(result).toEqual([{ id: "label_1" }, buttons, { id: "label_3" }]);
92
+ });
93
+
94
+ it("appends buttons when the target is unknown", () => {
63
95
  const configuration = {
64
96
  tv_buttons_container_position: "label_X",
65
97
  };
66
98
 
67
- const result = insertButtonsBetweenLabels(configuration, buttons, labels);
99
+ const result = insertButtonsBetweenLabels(configuration, buttons, slots);
68
100
 
69
101
  expect(result).toEqual([labels[0], labels[1], labels[2], buttons]);
70
102
  });
71
103
 
72
- it("put underneath all", () => {
104
+ it("appends buttons when there are no slots", () => {
73
105
  const configuration = {
74
106
  tv_buttons_container_position: "label_X",
75
107
  };
@@ -88,7 +120,7 @@ describe("insertButtonsBetweenLabels - depthlevel 1", () => {
88
120
  tv_buttons_container_position: "label_X",
89
121
  };
90
122
 
91
- const result = insertButtonsBetweenLabels(configuration, null, labels);
123
+ const result = insertButtonsBetweenLabels(configuration, null, slots);
92
124
 
93
125
  expect(result).toEqual(labels);
94
126
  });
@@ -34,7 +34,7 @@ export const memoizedGetPluginIdentifier = memoizee(getPluginIdentifier);
34
34
  export const insertButtonsBetweenLabels = (
35
35
  configuration: Record<string, unknown>,
36
36
  buttons,
37
- labels = []
37
+ slots = [] // { name, element } per canonical text label slot
38
38
  ) =>
39
39
  insertBetweenLabels(
40
40
  {
@@ -45,7 +45,7 @@ export const insertButtonsBetweenLabels = (
45
45
  appendWhenMissing: true,
46
46
  },
47
47
  buttons,
48
- labels
48
+ slots
49
49
  );
50
50
 
51
51
  export const insertButtonsBetweenLabelContainers = (
@@ -0,0 +1,32 @@
1
+ import { CollapsibleTextContainer } from "../index";
2
+
3
+ const getValue = (key: string) => {
4
+ const values: Record<string, unknown> = {
5
+ text_alignment: "left",
6
+ margin_top: 0,
7
+ margin_right: 0,
8
+ margin_bottom: 0,
9
+ margin_left: 0,
10
+ padding_top: 0,
11
+ padding_right: 0,
12
+ padding_bottom: 0,
13
+ padding_left: 0,
14
+ corner_radius: 0,
15
+ data_key: "title",
16
+ custom_data_key: undefined,
17
+ };
18
+
19
+ return values[key];
20
+ };
21
+
22
+ describe("CollapsibleTextContainer view tree", () => {
23
+ it("aligns the label to the text alignment instead of stretching the cell", () => {
24
+ const result = CollapsibleTextContainer({
25
+ getValue,
26
+ elements: [],
27
+ additionalStyles: { backgroundColor: "transparent" },
28
+ });
29
+
30
+ expect(result.style.alignSelf).toBe("flex-start");
31
+ });
32
+ });
@@ -76,6 +76,66 @@ describe("masterCellBuilder", () => {
76
76
  });
77
77
  });
78
78
 
79
+ describe("cellOptions.interactive", () => {
80
+ // renders a single View tagged with the state the cell was built with
81
+ const dataAdapter =
82
+ () =>
83
+ ({ state }) => [
84
+ { type: "View", style: {}, props: { testID: `state-${state}` } },
85
+ ];
86
+
87
+ // Only the root View gets this testID: cell elements pass their own testID, which overrides it
88
+ const ROOT_TEST_ID = "master-cell-root";
89
+
90
+ // forwardRef: MasterCell attaches a ref to its root View
91
+ const TestView = React.forwardRef((props, ref) => (
92
+ <View testID={ROOT_TEST_ID} {...props} ref={ref} />
93
+ ));
94
+
95
+ const renderCell = (cellOptions, state) => {
96
+ const MasterCell = masterCellBuilder({
97
+ cellConfiguration: singleView,
98
+ dataAdapter,
99
+ components: { View: TestView },
100
+ cellOptions,
101
+ });
102
+
103
+ return renderWithProviders(<MasterCell item={entry} state={state} />);
104
+ };
105
+
106
+ const getRoot = (wrapper) => wrapper.getByTestId(ROOT_TEST_ID);
107
+
108
+ it("keeps focused state and default root props when omitted", () => {
109
+ const wrapper = renderCell(undefined, "focused");
110
+ const root = getRoot(wrapper);
111
+
112
+ expect(wrapper.getByTestId("state-focused")).toBeDefined();
113
+ expect(root.props.onStartShouldSetResponder).toBeUndefined();
114
+ expect(root.props.collapsable).toBeUndefined();
115
+ });
116
+
117
+ it("claims body touches on a non-collapsable root when false", () => {
118
+ const wrapper = renderCell({ interactive: false }, "default");
119
+ const root = getRoot(wrapper);
120
+
121
+ expect(root.props.onStartShouldSetResponder()).toBe(true);
122
+ expect(root.props.collapsable).toBe(false);
123
+ });
124
+
125
+ it("renders focused as default when false", () => {
126
+ const wrapper = renderCell({ interactive: false }, "focused");
127
+
128
+ expect(wrapper.getByTestId("state-default")).toBeDefined();
129
+ expect(wrapper.queryByTestId("state-focused")).toBeNull();
130
+ });
131
+
132
+ it("renders focused_selected as selected when false", () => {
133
+ const wrapper = renderCell({ interactive: false }, "focused_selected");
134
+
135
+ expect(wrapper.getByTestId("state-selected")).toBeDefined();
136
+ });
137
+ });
138
+
79
139
  describe.skip("when using custom default components", () => {
80
140
  // the collapsable prop is added here to easily check from the snapshot that the mock
81
141
  // component is being used instead of the default one
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+
3
+ export const PressedStateContext = React.createContext<boolean>(false);
@@ -1,5 +1,6 @@
1
1
  import * as R from "ramda";
2
2
  import { isFunction } from "@applicaster/zapp-react-native-utils/functionUtils";
3
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
3
4
 
4
5
  import { functionForName } from "./MappingFunctions";
5
6
  import { resolveColor } from "./utils";
@@ -52,7 +53,8 @@ export function configInflater(
52
53
  additionalProps?: Record<string, any>;
53
54
  data?: Array<{ propName: string; func: Function; args: any[] }>;
54
55
  elements?: any[];
55
- }
56
+ },
57
+ allowDynamicColorsOutsideExtensions: boolean
56
58
  ) {
57
59
  const props = data.reduce(
58
60
  (acc, curr) => {
@@ -68,13 +70,13 @@ export function configInflater(
68
70
 
69
71
  if (Array.isArray(elements)) {
70
72
  adjustedElements = elements.map((element) =>
71
- configInflater(entry, element as any)
73
+ configInflater(entry, element as any, allowDynamicColorsOutsideExtensions)
72
74
  );
73
75
  }
74
76
 
75
77
  return {
76
78
  type,
77
- style: resolveColor(entry, style),
79
+ style: resolveColor(entry, style, allowDynamicColorsOutsideExtensions),
78
80
  props,
79
81
  elements: adjustedElements,
80
82
  };
@@ -98,9 +100,17 @@ function resolveElementsNode(entry, state, elements) {
98
100
  }
99
101
 
100
102
  export function defaultDataAdapter(elements) {
101
- return function elementsBuilder({ entry, state = "default" }) {
103
+ return function elementsBuilder({
104
+ entry,
105
+ state = "default",
106
+ allowDynamicColorsOutsideExtensions,
107
+ }) {
102
108
  return resolveElementsNode(entry, state, elements).map((element) =>
103
- configInflater(entry, element)
109
+ configInflater(
110
+ entry,
111
+ element,
112
+ toBooleanWithDefaultFalse(allowDynamicColorsOutsideExtensions)
113
+ )
104
114
  );
105
115
  };
106
116
  }
@@ -1 +1,3 @@
1
1
  export { useAsyncRendering } from "./useAsyncRendering";
2
+
3
+ export { usePressedState } from "./usePressedState";
@@ -0,0 +1,4 @@
1
+ import React from "react";
2
+ import { PressedStateContext } from "../contexts/PressedStateContext";
3
+
4
+ export const usePressedState = () => React.useContext(PressedStateContext);
@@ -2,12 +2,19 @@ import * as R from "ramda";
2
2
  import * as React from "react";
3
3
  import { v4 as uuid } from "uuid";
4
4
 
5
+ import { isHorizontalList } from "@applicaster/zapp-react-native-utils/componentsUtils";
6
+ import { isVideoPreviewEnabled } from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/utils";
5
7
  import { useRoute } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
8
+ import { useTheme } from "@applicaster/zapp-react-native-utils/theme";
6
9
  import { useScreenState } from "@applicaster/zapp-react-native-utils/screenState";
7
10
  import { defaultComponents } from "./DefaultComponents";
8
11
  import { defaultDataAdapter } from "./dataAdapter";
9
12
  import { elementMapper, mapElementWithKey } from "./elementMapper";
10
- import { getEntryState, hasFocusableInsideBuilder } from "./utils";
13
+ import {
14
+ getEntryState,
15
+ hasFocusableInsideBuilder,
16
+ withoutFocusedState,
17
+ } from "./utils";
11
18
 
12
19
  type Props = {
13
20
  item: ZappEntry;
@@ -16,6 +23,16 @@ type Props = {
16
23
 
17
24
  const defaultContainerStyle = { flex: 1 };
18
25
 
26
+ /**
27
+ * Root props of a non-interactive cell: the root view claims touches on the cell body,
28
+ * so the host list's touchable never fires (buttons, being deeper, still receive theirs).
29
+ * `collapsable: false` keeps Android / Fabric from flattening the view away.
30
+ */
31
+ const nonInteractiveRootProps = {
32
+ onStartShouldSetResponder: () => true,
33
+ collapsable: false,
34
+ };
35
+
19
36
  /**
20
37
  * Constructs a MasterCell with the provided options
21
38
  * @param {Object} options
@@ -24,6 +41,8 @@ const defaultContainerStyle = { flex: 1 };
24
41
  * @param {Function} options.dataAdapter optional dataAdapter to use instead of the default one.
25
42
  * See dataAdapter.js for more info
26
43
  * @param {containerStyles} options.containerStyle style to apply at the top level of the master cell
44
+ * @param {Object} options.cellOptions optional cell options. `interactive: false` makes the cell body
45
+ * ignore taps and render without focused / pressed states (inner buttons keep working). Defaults to `true`.
27
46
  * @returns {Function} Functional React Component to render the masterCell with the provided options
28
47
  */
29
48
 
@@ -34,8 +53,9 @@ type MasterCellBuilderOptions = {
34
53
  containerStyle: any;
35
54
  cellOptions?: {
36
55
  isRTL?: boolean;
37
- generateId?: () => string;
38
56
  skipButtons?: boolean;
57
+ component?: ZappComponent;
58
+ interactive?: boolean;
39
59
  };
40
60
  };
41
61
 
@@ -46,7 +66,7 @@ export function masterCellBuilder({
46
66
  containerStyle = defaultContainerStyle,
47
67
  cellOptions = {},
48
68
  }: MasterCellBuilderOptions) {
49
- const { generateId = uuid, skipButtons = false } = cellOptions;
69
+ const { skipButtons = false, component, interactive = true } = cellOptions;
50
70
 
51
71
  const componentsMap = R.merge(defaultComponents, components);
52
72
  const { View } = componentsMap;
@@ -65,6 +85,7 @@ export function masterCellBuilder({
65
85
  */
66
86
  function MasterCell({ item, state, ...otherProps }: Props) {
67
87
  const { screenData } = useRoute();
88
+ const theme = useTheme();
68
89
 
69
90
  const screenId =
70
91
  screenData && "targetScreen" in screenData
@@ -84,15 +105,39 @@ export function masterCellBuilder({
84
105
  () =>
85
106
  elementsBuilder({
86
107
  entry: item,
87
- state: getEntryState(state, entryIsSelected),
108
+ state: getEntryState(
109
+ interactive ? state : withoutFocusedState(state),
110
+ entryIsSelected
111
+ ),
112
+ allowDynamicColorsOutsideExtensions:
113
+ theme?.allow_dynamic_colors_outside_extensions,
88
114
  }),
89
- [state, item, entryIsSelected] // Assuming that item won't mutate
115
+ [
116
+ state,
117
+ item,
118
+ entryIsSelected,
119
+ theme?.allow_dynamic_colors_outside_extensions,
120
+ ] // Assuming that item won't mutate
90
121
  );
91
122
 
92
123
  const wrapperRef = React.useRef(null);
93
124
 
94
- const cellUUID = React.useMemo<string>(generateId, [
95
- generateId,
125
+ const cellUUID = React.useMemo<string>(() => {
126
+ const isLiveImage = isVideoPreviewEnabled({
127
+ enable_video_preview: component?.rules?.enable_video_preview,
128
+ player_screen_id: component?.rules?.player_screen_id,
129
+ });
130
+
131
+ const isHL = isHorizontalList(component);
132
+
133
+ return isHL && isLiveImage
134
+ ? `${component?.id}-${component?.styles?.cell_plugin_configuration_id}`
135
+ : uuid();
136
+ }, [
137
+ component?.id,
138
+ component?.styles?.cell_plugin_configuration_id,
139
+ component?.rules?.enable_video_preview,
140
+ component?.rules?.player_screen_id,
96
141
  screenId,
97
142
  item?.id,
98
143
  ]);
@@ -109,7 +154,11 @@ export function masterCellBuilder({
109
154
  );
110
155
 
111
156
  return (
112
- <View style={containerStyle} ref={wrapperRef}>
157
+ <View
158
+ style={containerStyle}
159
+ ref={wrapperRef}
160
+ {...(interactive ? undefined : nonInteractiveRootProps)}
161
+ >
113
162
  {elements.map(remapElements)}
114
163
  </View>
115
164
  );