@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,18 +1,27 @@
1
1
  import React, { useMemo, useContext, useEffect } from "react";
2
- import { ImageStyle, StyleSheet, View, ViewStyle } from "react-native";
2
+ import {
3
+ ImageStyle,
4
+ StyleProp,
5
+ StyleSheet,
6
+ ViewProps,
7
+ ViewStyle,
8
+ } from "react-native";
9
+ import {
10
+ BorderContainer as UIKitBorderContainer,
11
+ getBorderPadding,
12
+ type BorderPosition,
13
+ type UIKitBorderContainerProps,
14
+ } from "@applicaster/uikit/lib/components/uikit/border-container";
3
15
  import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
4
- import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
5
16
  import { MeasurementPortalContext } from "../../../MeasurmentsPortal/MeasurementsPortal";
6
17
 
7
- type BorderPosition = "inside" | "outside" | "center";
8
-
9
- interface Props {
18
+ interface Props extends ViewProps {
10
19
  hasFocusableInside: (entry: ZappEntry) => boolean;
11
20
  entry: ZappEntry;
12
21
  state: CellState;
13
22
  hasTextLabels: boolean;
14
- style: ImageStyle | ViewStyle;
15
- borderPosition: BorderPosition;
23
+ style: StyleProp<ImageStyle | ViewStyle>;
24
+ borderPosition?: BorderPosition | null;
16
25
  borderPaddingTop: number;
17
26
  borderPaddingRight: number;
18
27
  borderPaddingBottom: number;
@@ -20,101 +29,24 @@ interface Props {
20
29
  children: React.ReactNode;
21
30
  }
22
31
 
23
- const COLOR_TRANSPARENT = "transparent";
24
-
25
- const styles = StyleSheet.create({
26
- container: {
27
- overflow: "visible",
28
- borderWidth: 0,
29
- borderColor: COLOR_TRANSPARENT,
30
- },
31
- border: {
32
- ...StyleSheet.absoluteFillObject,
33
- zIndex: 1,
34
- },
35
- });
32
+ const toNumberOrZero = (value: unknown) => {
33
+ const numeric = Number(value);
36
34
 
37
- export const getBorderPadding = (
38
- borderPosition: BorderPosition,
39
- borderWidth: number
40
- ) => {
41
- switch (borderPosition) {
42
- case "inside":
43
- return 0;
44
- case "center":
45
- return borderWidth / 2;
46
- case "outside":
47
- return borderWidth;
48
- default:
49
- return borderWidth;
50
- }
35
+ return Number.isFinite(numeric) ? numeric : 0;
51
36
  };
52
37
 
53
- const Border = ({
54
- style,
55
- padding,
56
- borderPosition,
57
- }: {
58
- style: ViewStyle;
59
- padding: ViewStyle;
60
- borderPosition: BorderPosition;
61
- }) => {
62
- if (!borderPosition) {
63
- return null;
64
- }
65
-
66
- const borderWidth = toNumberWithDefaultZero(style.borderWidth);
67
- const containerPadding = getBorderPadding(borderPosition, borderWidth);
38
+ const getBorderPropsFromStyle = (style: StyleProp<ImageStyle | ViewStyle>) => {
39
+ const flattenedStyle = StyleSheet.flatten(style) ?? {};
68
40
 
69
- const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
70
- const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
71
- const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
72
- const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
73
- const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
74
-
75
- const borderRadiusStyle = initialBorderRadius
76
- ? {
77
- borderTopLeftRadius:
78
- initialBorderRadius +
79
- containerPadding +
80
- Math.min(paddingTop, paddingLeft),
81
- borderTopRightRadius:
82
- initialBorderRadius +
83
- containerPadding +
84
- Math.min(paddingTop, paddingRight),
85
- borderBottomLeftRadius:
86
- initialBorderRadius +
87
- containerPadding +
88
- Math.min(paddingBottom, paddingLeft),
89
- borderBottomRightRadius:
90
- initialBorderRadius +
91
- containerPadding +
92
- Math.min(paddingBottom, paddingRight),
93
- }
94
- : {
95
- borderTopLeftRadius: 0,
96
- borderTopRightRadius: 0,
97
- borderBottomLeftRadius: 0,
98
- borderBottomRightRadius: 0,
99
- };
100
-
101
- return (
102
- <View
103
- pointerEvents="none"
104
- style={{
105
- ...styles.border,
106
- right: -containerPadding - paddingRight,
107
- bottom: -containerPadding - paddingBottom,
108
- left: -containerPadding - paddingLeft,
109
- top: -containerPadding - paddingTop,
110
- borderWidth,
111
- borderColor: style.borderColor,
112
- ...borderRadiusStyle,
113
- }}
114
- />
115
- );
41
+ return {
42
+ borderWidth: toNumberOrZero(flattenedStyle.borderWidth),
43
+ borderColor: flattenedStyle.borderColor,
44
+ borderRadius: toNumberOrZero(flattenedStyle.borderRadius),
45
+ };
116
46
  };
117
47
 
48
+ export { getBorderPadding };
49
+
118
50
  export const BorderContainerView = (props: Props) => {
119
51
  const {
120
52
  borderPosition,
@@ -128,24 +60,22 @@ export const BorderContainerView = (props: Props) => {
128
60
  entry,
129
61
  state,
130
62
  hasTextLabels,
63
+ ...viewProps
131
64
  } = props;
132
65
 
133
66
  const accessibilityManager = useAccessibilityManager();
134
67
  const isMeasurement = useContext(MeasurementPortalContext);
135
68
 
69
+ const { borderWidth, borderColor, borderRadius } =
70
+ getBorderPropsFromStyle(style);
71
+
136
72
  const isImageOnlyCell = useMemo(
137
73
  () =>
138
- !hasFocusableInside(entry) &&
139
- !hasTextLabels &&
140
74
  state === "focused" &&
141
- !isMeasurement?.measuringInProgress,
142
- [
143
- hasFocusableInside,
144
- entry,
145
- hasTextLabels,
146
- state,
147
- isMeasurement?.measuringInProgress,
148
- ]
75
+ !hasTextLabels &&
76
+ !isMeasurement?.measuringInProgress &&
77
+ !hasFocusableInside(entry),
78
+ [entry, hasTextLabels, state, isMeasurement?.measuringInProgress]
149
79
  );
150
80
 
151
81
  useEffect(() => {
@@ -154,20 +84,21 @@ export const BorderContainerView = (props: Props) => {
154
84
  }
155
85
  }, [isImageOnlyCell, entry?.title]);
156
86
 
157
- const padding =
158
- borderPosition === "outside"
159
- ? {
160
- paddingTop: borderPaddingTop,
161
- paddingRight: borderPaddingRight,
162
- paddingBottom: borderPaddingBottom,
163
- paddingLeft: borderPaddingLeft,
164
- }
165
- : {};
166
-
167
87
  return (
168
- <View pointerEvents="box-none" {...props} style={[style, styles.container]}>
169
- <Border style={style} padding={padding} borderPosition={borderPosition} />
88
+ <UIKitBorderContainer
89
+ testID="border-container-view"
90
+ {...viewProps}
91
+ style={style as UIKitBorderContainerProps["style"]}
92
+ borderPosition={borderPosition}
93
+ borderWidth={borderWidth}
94
+ borderColor={borderColor}
95
+ borderRadius={borderRadius}
96
+ borderPaddingTop={borderPaddingTop}
97
+ borderPaddingRight={borderPaddingRight}
98
+ borderPaddingBottom={borderPaddingBottom}
99
+ borderPaddingLeft={borderPaddingLeft}
100
+ >
170
101
  {children}
171
- </View>
102
+ </UIKitBorderContainer>
172
103
  );
173
104
  };
@@ -1,12 +1,10 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
 
4
- import { TouchableOpacity } from "react-native";
5
- // import { SvgUri } from "react-native-svg";
6
-
7
- import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
8
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
9
5
 
6
+ import { Button as UIKitButton } from "@applicaster/uikit/lib/components/uikit/button";
7
+
10
8
  import Image from "./Image";
11
9
  import { masterCellLogger } from "../logger";
12
10
  import { ActionButton } from "./ActionButton";
@@ -23,64 +21,43 @@ type Props = {
23
21
  style: {};
24
22
  type: "Image" | "Svg" | "Lottie";
25
23
  };
26
- initiallyActive: boolean;
27
24
  action: {
25
+ src?: Record<string, string | undefined>;
28
26
  identifier: string;
29
27
  type: string;
30
28
  };
31
29
  style: {};
32
- plugins: [];
33
30
  testID?: string;
34
31
  accessibilityLabel?: string;
35
- cellUUID?: string;
36
32
  };
37
33
 
38
- // function Svg({
39
- // uri,
40
- // style,
41
- // ...props
42
- // }: {
43
- // uri: {},
44
- // style: { width: Number, height: number },
45
- // }) {
46
- // const width = style?.width;
47
- // const height = style?.height;
48
-
49
- // return <SvgUri width={width} height={height} uri={uri} />;
50
- // }
51
-
52
34
  const components = {
53
35
  Image,
54
36
  };
55
37
 
56
- function ButtonComponent(props: Props) {
57
- const asset = props?.asset;
58
- const item = props?.item;
59
- const action = props?.action;
38
+ export function Button(props: Props) {
39
+ const { asset, item, action, style, testID, accessibilityLabel } = props;
60
40
 
61
41
  const assetType = asset?.type || "Image";
62
42
 
63
43
  const ButtonAsset = components[assetType];
64
44
 
65
- const actionContext = useActions(action?.identifier);
66
-
67
- // TODO: better handling of missing plugin
68
- if (!actionContext) {
69
- // eslint-disable-next-line no-console
70
- masterCellLogger.warning(
71
- `You're missing an action plugin(${action?.identifier}) required by your button. \n Button can't be displayed`
72
- );
45
+ type ActionContext =
46
+ | {
47
+ masterCell?: {
48
+ isSelected?: (entry: ZappEntry) => boolean;
49
+ };
50
+ state?: ZappEntry[];
51
+ removeFavourite?: (item: ZappEntry) => Promise<void>;
52
+ addFavourite?: (item: ZappEntry) => Promise<void>;
53
+ invokeAction?: (item: ZappEntry) => Promise<void>;
54
+ initialEntryState?: (...args: unknown[]) => unknown;
55
+ }
56
+ | undefined;
73
57
 
74
- return null;
75
- }
58
+ const actionContext: ActionContext = useActions(action?.identifier);
76
59
 
77
- // if the action context implements the initialEntryState function, it
78
- // means it uses the new Action Button API.
79
- if (typeof actionContext?.initialEntryState === "function") {
80
- return <ActionButton {...props} />;
81
- }
82
-
83
- const defaultIsSelected = R.includes(item, actionContext.state || []);
60
+ const defaultIsSelected = R.includes(item, actionContext?.state || []);
84
61
 
85
62
  const isSelected = actionContext?.masterCell?.isSelected
86
63
  ? actionContext?.masterCell?.isSelected(item)
@@ -90,30 +67,45 @@ function ButtonComponent(props: Props) {
90
67
  const assetSrc = isSelected ? asset?.src?.active : asset?.src?.inactive;
91
68
 
92
69
  const favouritesAction = isSelected
93
- ? actionContext.removeFavourite
94
- : actionContext.addFavourite;
70
+ ? actionContext?.removeFavourite
71
+ : actionContext?.addFavourite;
95
72
 
96
73
  const toggleAction = actionContext?.invokeAction ?? favouritesAction;
97
74
 
98
- async function onPress() {
99
- // TODO: loading state
100
- await toggleAction(item);
75
+ const onPress = React.useCallback(async () => {
76
+ try {
77
+ await toggleAction(item);
78
+ } catch (error) {
79
+ masterCellLogger.error(
80
+ `Error invoking action for button with item ${item?.id}`,
81
+ error
82
+ );
83
+ }
84
+ }, [item, toggleAction]);
85
+
86
+ if (!actionContext) {
87
+ masterCellLogger.warning(
88
+ `You're missing an action plugin(${action?.identifier}) required by your button. \n Button can't be displayed`
89
+ );
90
+
91
+ return null;
92
+ }
93
+
94
+ // Action context is the preferred API when the plugin exposes initialEntryState (new Action Button API).
95
+ if (typeof actionContext?.initialEntryState === "function") {
96
+ return <ActionButton {...props} />;
101
97
  }
102
98
 
99
+ // Legacy button rendering with image inside is the default path for action contexts without initialEntryState.
103
100
  return (
104
- <TouchableOpacity
105
- activeOpacity={1}
101
+ <UIKitButton
102
+ id={`${item?.id}`}
103
+ style={style}
104
+ testID={testID}
105
+ accessibilityLabel={accessibilityLabel}
106
106
  onPress={onPress}
107
- testID={props?.testID || `${item?.id}`}
108
- accessibilityLabel={props?.accessibilityLabel || `${item?.id}`}
109
- accessible={!!(props?.testID || props?.accessibilityLabel)}
110
- style={props?.style}
111
107
  >
112
108
  <ButtonAsset style={asset?.style} uri={assetSrc} {...asset?.props} />
113
- </TouchableOpacity>
109
+ </UIKitButton>
114
110
  );
115
111
  }
116
-
117
- export const Button = R.compose(connectToStore(R.pick(["plugins"])))(
118
- ButtonComponent
119
- );
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+
4
+ import type { HorizontalSeparatorProps } from "../types";
5
+
6
+ export const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
7
+ <View style={{ width }} />
8
+ );
@@ -0,0 +1,58 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import * as R from "ramda";
4
+ import { useInitialFocus } from "@applicaster/zapp-react-native-utils/focusManager";
5
+ import {
6
+ useIsRTL,
7
+ applyRTLStylesIfNeeded,
8
+ } from "@applicaster/zapp-react-native-utils/localizationUtils";
9
+
10
+ import {
11
+ cloneChildrenWithIds,
12
+ insertBetween,
13
+ unwrapDataProviderChild,
14
+ useFilterChildren,
15
+ } from "../../utils";
16
+
17
+ import type { ContainerProps, ContainerChildren } from "./types";
18
+ import { HorizontalSeparator } from "./components/HorizontalSeparator";
19
+
20
+ const generateId = (cellUUID, suffixId) => `${cellUUID}--${suffixId}`;
21
+
22
+ export function ButtonContainerView({
23
+ style,
24
+ children,
25
+ ...otherProps
26
+ }: ContainerProps) {
27
+ const isRTL = useIsRTL();
28
+
29
+ const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
30
+ const filteredChildren = useFilterChildren<ContainerChildren>(children);
31
+
32
+ const buttonIds = filteredChildren.map((child) => {
33
+ const wrappedChild = unwrapDataProviderChild(child) as ContainerChildren;
34
+ const { cellUUID, suffixId } = wrappedChild.props;
35
+
36
+ return generateId(cellUUID, suffixId);
37
+ });
38
+
39
+ useInitialFocus(otherProps.state === "focused", R.head(buttonIds));
40
+
41
+ if (R.isEmpty(filteredChildren)) {
42
+ return null;
43
+ }
44
+
45
+ return (
46
+ <View style={applyRTLStylesIfNeeded(style, isRTL)}>
47
+ {insertBetween(
48
+ (index) => (
49
+ <HorizontalSeparator
50
+ key={`separator_${index}`}
51
+ width={horizontalGutter}
52
+ />
53
+ ),
54
+ cloneChildrenWithIds(buttonIds, filteredChildren)
55
+ )}
56
+ </View>
57
+ );
58
+ }
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { View } from "react-native";
3
+ import { ContainerProps } from "./types";
4
+
5
+ export function ButtonContainerView({
6
+ style,
7
+ contentStyle,
8
+ children,
9
+ }: ContainerProps) {
10
+ return (
11
+ <View style={style} pointerEvents="box-none">
12
+ <View style={contentStyle}>{children}</View>
13
+ </View>
14
+ );
15
+ }
@@ -1,20 +1,13 @@
1
1
  import React from "react";
2
2
  import { View } from "react-native";
3
3
  import * as R from "ramda";
4
- import { useFilterChildren, insertBetween } from "../../../utils";
5
- import type {
6
- HorizontalSeparatorProps,
7
- ContainerProps,
8
- ContainerChildren,
9
- } from "./types";
4
+ import { insertBetween, useFilterChildren } from "../../utils";
5
+ import type { ContainerProps, ContainerChildren } from "./types";
10
6
  import {
11
7
  useIsRTL,
12
8
  applyRTLStylesIfNeeded,
13
9
  } from "@applicaster/zapp-react-native-utils/localizationUtils";
14
-
15
- const HorizontalSeparator = ({ width }: HorizontalSeparatorProps) => (
16
- <View style={{ width }} />
17
- );
10
+ import { HorizontalSeparator } from "./components/HorizontalSeparator";
18
11
 
19
12
  export function ButtonContainerView({
20
13
  style,
@@ -23,7 +16,6 @@ export function ButtonContainerView({
23
16
  }: ContainerProps) {
24
17
  const isRTL = useIsRTL();
25
18
  const horizontalGutter = R.pathOr(0, ["horizontalGutter"], otherProps);
26
-
27
19
  const filteredChildren = useFilterChildren<ContainerChildren>(children);
28
20
 
29
21
  if (R.isEmpty(filteredChildren)) {
@@ -0,0 +1 @@
1
+ export { ButtonContainerView } from "./index.tv";
@@ -0,0 +1,40 @@
1
+ import { ReactElement, ReactNode } from "react";
2
+ import { ViewStyle } from "react-native";
3
+
4
+ export type ContainerChildProps = {
5
+ item: any;
6
+ pluginIdentifier: string;
7
+ cellUUID: string;
8
+ suffixId: string;
9
+ nextFocusLeft?: string;
10
+ nextFocusRight?: string;
11
+ };
12
+
13
+ export type ContainerProps = Record<string, any> & {
14
+ buttonsToggleEnabled: boolean;
15
+ skipButtons: boolean;
16
+ style: ViewStyle;
17
+ contentStyle?: ViewStyle;
18
+ children: ContainerChildren[];
19
+ };
20
+
21
+ export type ButtonProps = Record<string, any> & {
22
+ style: ViewStyle;
23
+ children?: ReactNode;
24
+ item: any;
25
+ cellUUID: string;
26
+ groupId?: string;
27
+ suffixId: string;
28
+ normalStyles?: ViewStyle;
29
+ focusedStyles?: ViewStyle;
30
+ nextFocusLeft?: string;
31
+ nextFocusRight?: string;
32
+ pluginIdentifier: string;
33
+ disableFocus?: boolean;
34
+ };
35
+
36
+ export type HorizontalSeparatorProps = {
37
+ width: number;
38
+ };
39
+
40
+ export type ContainerChildren = ReactElement<ContainerChildProps>;
@@ -0,0 +1,18 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`CellBadge matches the rendered CellBadge snapshot after config inflation 1`] = `
4
+ <View
5
+ style={
6
+ {
7
+ "bottom": 0,
8
+ "flex": 1,
9
+ "left": 0,
10
+ "marginBottom": 5,
11
+ "marginLeft": 6,
12
+ "marginRight": 4,
13
+ "marginTop": 3,
14
+ "position": "absolute",
15
+ }
16
+ }
17
+ />
18
+ `;
@@ -0,0 +1,90 @@
1
+ import { View } from "react-native";
2
+ import { render } from "@testing-library/react-native";
3
+ import { configInflater } from "../../../dataAdapter";
4
+ import { elementMapper } from "../../../elementMapper";
5
+ import { CellBadge } from "..";
6
+ import { UIKitBadge } from "../../UIKitAdapters/Badge";
7
+
8
+ const createValue = (overrides: Record<string, unknown> = {}) => {
9
+ const values = {
10
+ cell_badge_switch: true,
11
+ cell_badge_position: "bottom_left",
12
+ cell_badge_width: 42,
13
+ cell_badge_height: 24,
14
+ cell_badge_margin_top: 3,
15
+ cell_badge_margin_right: 4,
16
+ cell_badge_margin_bottom: 5,
17
+ cell_badge_margin_left: 6,
18
+ cell_badge_default: "default.png",
19
+ cell_badge_selected: "selected.png",
20
+ cell_badge_focused: "focused.png",
21
+ cell_badge_focused_selected: "focused-selected.png",
22
+ ...overrides,
23
+ };
24
+
25
+ return (key: string) => values[key];
26
+ };
27
+
28
+ describe("CellBadge", () => {
29
+ it("returns explicit CellBadge props and image config", () => {
30
+ const result = CellBadge({
31
+ value: createValue(),
32
+ state: "focused_selected",
33
+ });
34
+
35
+ expect(result).toMatchObject({
36
+ type: "UIKitBadge",
37
+ style: {
38
+ flex: 1,
39
+ },
40
+ additionalProps: {
41
+ position: "bottom_left",
42
+ width: 42,
43
+ height: 24,
44
+ margins: {
45
+ marginTop: 3,
46
+ marginRight: 4,
47
+ marginBottom: 5,
48
+ marginLeft: 6,
49
+ },
50
+ },
51
+ elements: [
52
+ {
53
+ type: "Image",
54
+ style: {
55
+ width: 42,
56
+ height: 24,
57
+ },
58
+ additionalProps: {
59
+ resizeMode: "cover",
60
+ },
61
+ },
62
+ ],
63
+ });
64
+
65
+ expect(result.elements[0].data[0].func()).toBe("focused-selected.png");
66
+ });
67
+
68
+ it("matches the rendered CellBadge snapshot after config inflation", () => {
69
+ const inflatedConfig = configInflater(
70
+ {},
71
+ CellBadge({
72
+ value: createValue(),
73
+ state: "focused_selected",
74
+ })
75
+ );
76
+
77
+ const wrapper = render(
78
+ elementMapper({
79
+ View,
80
+ UIKitBadge,
81
+ Image: View,
82
+ })({
83
+ ...inflatedConfig,
84
+ key: "cell-badge",
85
+ })
86
+ );
87
+
88
+ expect(wrapper.toJSON()).toMatchSnapshot();
89
+ });
90
+ });
@@ -4,10 +4,7 @@ import {
4
4
  ifElseSelected,
5
5
  } from "@applicaster/zapp-react-native-utils/cellUtils";
6
6
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
7
-
8
- import { getPositionStyles } from "./utils";
9
-
10
- const View = "View";
7
+ const UIKitBadge = "UIKitBadge";
11
8
  const Image = "Image";
12
9
 
13
10
  type Props = {
@@ -30,22 +27,24 @@ export const CellBadge = ({
30
27
  const position = value("cell_badge_position");
31
28
 
32
29
  return {
33
- type: View, // Cell badge view
30
+ type: UIKitBadge, // Cell badge view
34
31
  style: {
35
32
  flex: platformSelect({
36
33
  tvos: 0,
37
34
  android_tv: 0,
38
35
  default: 1,
39
36
  }),
40
- position: "absolute",
41
- ...getPositionStyles(position, {
42
- width: value("cell_badge_width"),
43
- height: value("cell_badge_height"),
44
- }),
45
- marginTop: value("cell_badge_margin_top"),
46
- marginRight: value("cell_badge_margin_right"),
47
- marginBottom: value("cell_badge_margin_bottom"),
48
- marginLeft: value("cell_badge_margin_left"),
37
+ },
38
+ additionalProps: {
39
+ position,
40
+ width: value("cell_badge_width"),
41
+ height: value("cell_badge_height"),
42
+ margins: {
43
+ marginTop: value("cell_badge_margin_top"),
44
+ marginRight: value("cell_badge_margin_right"),
45
+ marginBottom: value("cell_badge_margin_bottom"),
46
+ marginLeft: value("cell_badge_margin_left"),
47
+ },
49
48
  },
50
49
  elements: compact([
51
50
  {