@applicaster/zapp-react-native-ui-components 12.0.0-alpha.4757075613 → 12.0.0-alpha.4796013211

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 (114) hide show
  1. package/Components/BaseFocusable/index.tsx +3 -3
  2. package/Components/Cell/Cell.tsx +30 -9
  3. package/Components/Cell/CellWithFocusable.tsx +23 -9
  4. package/Components/Cell/TvOSCellComponent.tsx +3 -7
  5. package/Components/Focusable/Focusable.tsx +6 -1
  6. package/Components/FocusableGroup/index.tsx +11 -2
  7. package/Components/FreezeWithCallback/Freeze.tsx +31 -0
  8. package/Components/FreezeWithCallback/LICENSE +21 -0
  9. package/Components/FreezeWithCallback/Suspender.tsx +17 -0
  10. package/Components/FreezeWithCallback/__tests__/index.test.tsx +170 -0
  11. package/Components/FreezeWithCallback/index.tsx +3 -0
  12. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +13 -6
  13. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +1 -1
  14. package/Components/HandlePlayable/HandlePlayable.tsx +3 -0
  15. package/Components/Layout/TV/ScreenContainer.tsx +79 -5
  16. package/Components/Layout/TV/ScreenLayoutContextProvider.tsx +2 -2
  17. package/Components/Layout/TV/__tests__/ScreenContainer.test.tsx +38 -28
  18. package/Components/Layout/TV/__tests__/__snapshots__/ScreenContainer.test.tsx.snap +25 -13
  19. package/Components/Layout/TV/__tests__/__snapshots__/index.test.tsx.snap +10 -50
  20. package/Components/Layout/TV/__tests__/index.test.tsx +20 -75
  21. package/Components/Layout/TV/index.tsx +14 -67
  22. package/Components/Layout/TV/index.web.tsx +33 -0
  23. package/Components/Layout/index.tsx +15 -11
  24. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
  25. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +86 -116
  26. package/Components/MasterCell/DefaultComponents/DynamicBadge.tsx +4 -2
  27. package/Components/MasterCell/DefaultComponents/FocusableView/index.tsx +16 -1
  28. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -3
  29. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/index.ts +71 -0
  30. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -1
  31. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +34 -1
  32. package/Components/MasterCell/DefaultComponents/Text/index.tsx +14 -2
  33. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  34. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +24 -9
  35. package/Components/MasterCell/DefaultComponents/index.ts +2 -0
  36. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.android.tsx +4 -1
  37. package/Components/MasterCell/DefaultComponents/tv/ButtonContainerView/index.tsx +10 -2
  38. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -3
  39. package/Components/MasterCell/SharedUI/ProgressBar/hooks/useFillInPercent.ts +7 -1
  40. package/Components/MasterCell/dataAdapter.ts +21 -7
  41. package/Components/MasterCell/hooks/useAsyncRendering/index.ts +1 -1
  42. package/Components/MasterCell/index.tsx +5 -3
  43. package/Components/MasterCell/utils/__tests__/insertBetween.test.js +9 -0
  44. package/Components/MasterCell/utils/index.ts +33 -35
  45. package/Components/MeasurmentsPortal/BasicMeasurementsPortal.tsx +11 -7
  46. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +10 -1
  47. package/Components/Navigator/StackNavigator.tsx +57 -0
  48. package/Components/Navigator/index.ts +1 -0
  49. package/Components/PlayerContainer/PlayerContainer.tsx +20 -3
  50. package/Components/PlayerImageBackground/index.tsx +34 -8
  51. package/Components/River/ComponentsMap/ComponentsMap.tsx +92 -22
  52. package/Components/River/RefreshControl.tsx +55 -1
  53. package/Components/River/TV/River.tsx +0 -23
  54. package/Components/River/__tests__/componentsMap.test.js +6 -0
  55. package/Components/RouteManager/index.tsx +2 -2
  56. package/Components/Screen/TV/hooks/index.ts +1 -0
  57. package/Components/Screen/TV/hooks/useInitialFocus.ts +70 -0
  58. package/Components/Screen/TV/index.web.tsx +178 -0
  59. package/Components/Screen/__tests__/Screen.test.tsx +3 -49
  60. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +4 -0
  61. package/Components/Screen/index.tsx +62 -72
  62. package/Components/Screen/navigationHandler.ts +6 -3
  63. package/Components/Screen/orientationHandler.ts +18 -7
  64. package/Components/Screen/utils.ts +16 -0
  65. package/Components/ScreenResolver/index.tsx +9 -12
  66. package/Components/Transitioner/Scene.tsx +39 -26
  67. package/Components/Transitioner/Transitioner.tsx +46 -48
  68. package/Components/Transitioner/__tests__/Scene.test.js +7 -0
  69. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +12 -4
  70. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -0
  71. package/Components/VideoLive/LiveImageManager.ts +28 -1
  72. package/Components/VideoLive/PlayerLiveImageComponent.tsx +52 -24
  73. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +20 -5
  74. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +25 -28
  75. package/Components/VideoModal/ModalAnimation/AnimatedPlayerModalWrapper.tsx +66 -0
  76. package/Components/VideoModal/ModalAnimation/AnimatedScrollModal.tsx +376 -0
  77. package/Components/VideoModal/ModalAnimation/AnimatedVideoPlayerComponent.tsx +165 -0
  78. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +473 -0
  79. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +182 -0
  80. package/Components/VideoModal/ModalAnimation/index.ts +15 -0
  81. package/Components/VideoModal/ModalAnimation/utils.ts +302 -0
  82. package/Components/VideoModal/PlayerDetails.tsx +3 -3
  83. package/Components/VideoModal/PlayerWrapper.tsx +77 -47
  84. package/Components/VideoModal/VideoModal.tsx +23 -40
  85. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +13 -1
  86. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +203 -105
  87. package/Components/VideoModal/hooks/useModalSize.ts +10 -12
  88. package/Components/VideoModal/utils.ts +8 -5
  89. package/Components/Viewport/ViewportTracker/ViewportTracker.tsx +31 -40
  90. package/Components/Viewport/ViewportTracker/index.tsx +1 -0
  91. package/Components/Viewport/utils.js +1 -1
  92. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +4 -3
  93. package/Components/default-cell-renderer/__tests__/__snapshots__/defaultCellRenderer.test.tsx.snap +4 -4
  94. package/Contexts/FocusableGroupContext/FocusableGroupContext.ts +3 -0
  95. package/Contexts/FocusableGroupContext/FocusableGroupContextProvider.tsx +16 -0
  96. package/Contexts/FocusableGroupContext/__tests__/FocusableGroupContextProvider.test.tsx +22 -0
  97. package/Contexts/FocusableGroupContext/__tests__/withFocusableContext.test.tsx +26 -0
  98. package/Contexts/FocusableGroupContext/index.ts +7 -0
  99. package/Contexts/FocusableGroupContext/withFocusableContext.tsx +17 -0
  100. package/Contexts/MenuToggleContext/{index.js → index.ts} +16 -0
  101. package/Contexts/ScreenContext/__tests__/__snapshots__/index.test.tsx.snap +3 -0
  102. package/Contexts/ScreenContext/__tests__/index.test.tsx +27 -0
  103. package/Contexts/ScreenContext/index.tsx +94 -25
  104. package/Contexts/ScreenDataContext/index.tsx +20 -0
  105. package/Contexts/ZappPipesContext/ZappPipesEntryContext.ts +1 -1
  106. package/const.ts +6 -0
  107. package/events/index.ts +1 -0
  108. package/index.js +3 -0
  109. package/package.json +5 -5
  110. package/Components/Layout/TV/TransitionOverlay.tsx +0 -64
  111. package/Components/VideoModal/hooks/usePosition.ts +0 -50
  112. /package/Components/FocusableGroup/{index.android.js → index.android.ts} +0 -0
  113. /package/Components/FocusableGroup/{index.ios.js → index.ios.ts} +0 -0
  114. /package/Contexts/{index.js → index.ts} +0 -0
@@ -25,6 +25,7 @@ type Props = {
25
25
  testID?: string;
26
26
  accessibilityLabel?: string;
27
27
  cellUUID?: string;
28
+ extraProps?: Record<string, any>;
28
29
  };
29
30
 
30
31
  function isStringAsset(asset) {
@@ -52,6 +53,7 @@ export function ActionButton(props: Props) {
52
53
  const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
53
54
  const actionContext = useActions(action?.identifier);
54
55
 
56
+ // TODO: add subscription API for action availability
55
57
  const actionDisabled =
56
58
  typeof actionContext?.isActionAvailable === "function" &&
57
59
  !actionContext.isActionAvailable(item);
@@ -67,7 +69,7 @@ export function ActionButton(props: Props) {
67
69
  if (actionDisabled || !actionContext) return;
68
70
 
69
71
  setActionState(actionContext.initialEntryState(item));
70
- }, [actionDisabled, item?.id]);
72
+ }, [actionDisabled, item?.id, actionContext]);
71
73
 
72
74
  const onPress = useCallback(() => {
73
75
  actionContext.invokeAction(item, {
@@ -108,6 +110,7 @@ export function ActionButton(props: Props) {
108
110
  flavour={flavour}
109
111
  asset={getAssetValue(asset, flavour)}
110
112
  cellUUID={cellUUID}
113
+ {...(props?.extraProps ?? {})}
111
114
  />
112
115
  )}
113
116
  </TouchableOpacity>
@@ -7,50 +7,28 @@ type BorderPosition = "inside" | "outside" | "center";
7
7
  interface Props {
8
8
  style: ImageStyle | ViewStyle;
9
9
  borderPosition: BorderPosition;
10
+ borderPaddingTop: number;
11
+ borderPaddingRight: number;
12
+ borderPaddingBottom: number;
13
+ borderPaddingLeft: number;
10
14
  children: React.ReactNode;
11
15
  }
12
16
 
13
- const MARGINS = [
14
- "marginTop",
15
- "marginBottom",
16
- "marginLeft",
17
- "marginRight",
18
- ] as const;
19
-
20
- const PADDINGS = [
21
- "paddingTop",
22
- "paddingBottom",
23
- "paddingLeft",
24
- "paddingRight",
25
- ] as const;
26
-
27
- const isPercentageValue = (value: number | string) => {
28
- if (typeof value === "string" && value.endsWith("%")) {
29
- return true;
30
- }
31
-
32
- return false;
33
- };
34
-
35
- const calculateContainerSpacing = ({
36
- spacingKey,
37
- style,
38
- additionalSpacing,
39
- }: {
40
- spacingKey: typeof MARGINS[number] | typeof PADDINGS[number];
41
- style: ImageStyle | ViewStyle;
42
- additionalSpacing: number;
43
- }) => {
44
- return {
45
- [spacingKey]:
46
- toNumberWithDefaultZero(style[spacingKey]) + additionalSpacing,
47
- };
48
- };
49
-
50
- const getContainerBorderWidth = (
51
- borderPosition: BorderPosition,
52
- borderWidth
53
- ) => {
17
+ const COLOR_TRANSPARENT = "transparent";
18
+
19
+ const styles = StyleSheet.create({
20
+ container: {
21
+ overflow: "visible",
22
+ borderWidth: 0,
23
+ borderColor: COLOR_TRANSPARENT,
24
+ },
25
+ border: {
26
+ ...StyleSheet.absoluteFillObject,
27
+ zIndex: 1,
28
+ },
29
+ });
30
+
31
+ const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
54
32
  switch (borderPosition) {
55
33
  case "inside":
56
34
  return 0;
@@ -63,104 +41,96 @@ const getContainerBorderWidth = (
63
41
  }
64
42
  };
65
43
 
66
- const getContainerPadding = (borderPosition: BorderPosition, borderWidth) => {
67
- switch (borderPosition) {
68
- case "inside":
69
- return borderWidth;
70
- case "center":
71
- return borderWidth / 2;
72
- case "outside":
73
- return 0;
74
- default:
75
- return 0;
76
- }
77
- };
78
-
79
- const InsideBorder = ({
44
+ const Border = ({
80
45
  style,
46
+ padding,
81
47
  borderPosition,
82
48
  }: {
83
49
  style: ViewStyle;
50
+ padding: ViewStyle;
84
51
  borderPosition: BorderPosition;
85
52
  }) => {
86
- if (borderPosition === "outside") {
53
+ if (!borderPosition) {
87
54
  return null;
88
55
  }
89
56
 
90
- const insideBorderWidth =
91
- borderPosition === "center" ? style.borderWidth / 2 : style.borderWidth;
57
+ const borderWidth = toNumberWithDefaultZero(style.borderWidth);
58
+ const containerPadding = getBorderPadding(borderPosition, borderWidth);
59
+
60
+ const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
61
+ const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
62
+ const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
63
+ const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
64
+ const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
65
+
66
+ const borderRadiusStyle = initialBorderRadius
67
+ ? {
68
+ borderTopLeftRadius:
69
+ initialBorderRadius +
70
+ containerPadding +
71
+ Math.min(paddingTop, paddingLeft),
72
+ borderTopRightRadius:
73
+ initialBorderRadius +
74
+ containerPadding +
75
+ Math.min(paddingTop, paddingRight),
76
+ borderBottomLeftRadius:
77
+ initialBorderRadius +
78
+ containerPadding +
79
+ Math.min(paddingBottom, paddingLeft),
80
+ borderBottomRightRadius:
81
+ initialBorderRadius +
82
+ containerPadding +
83
+ Math.min(paddingBottom, paddingRight),
84
+ }
85
+ : {
86
+ borderTopLeftRadius: 0,
87
+ borderTopRightRadius: 0,
88
+ borderBottomLeftRadius: 0,
89
+ borderBottomRightRadius: 0,
90
+ };
92
91
 
93
92
  return (
94
93
  <View
94
+ pointerEvents="none"
95
95
  style={{
96
- ...StyleSheet.absoluteFillObject,
97
- borderWidth: insideBorderWidth,
96
+ ...styles.border,
97
+ right: -containerPadding - paddingRight,
98
+ bottom: -containerPadding - paddingBottom,
99
+ left: -containerPadding - paddingLeft,
100
+ top: -containerPadding - paddingTop,
101
+ borderWidth,
98
102
  borderColor: style.borderColor,
99
- borderRadius: style.borderRadius,
103
+ ...borderRadiusStyle,
100
104
  }}
101
105
  />
102
106
  );
103
107
  };
104
108
 
105
109
  export const BorderContainerView = (props: Props) => {
106
- const { style, children } = props;
107
- // Return border position prop after re-implementing
108
- const borderPosition = "outside";
109
-
110
- const borderWidth = toNumberWithDefaultZero(style.borderWidth);
111
- const spacing = getContainerPadding(borderPosition, borderWidth);
112
-
113
- const borderStyleForMissingBorder =
114
- style?.borderWidth === 0 || !style?.borderColor ? { borderRadius: 0 } : {};
115
-
116
- const updatedMargins = MARGINS.reduce((acc, margin) => {
117
- const marginValue = calculateContainerSpacing({
118
- spacingKey: margin,
119
- style,
120
- additionalSpacing: spacing,
121
- });
122
-
123
- return { ...acc, ...marginValue };
124
- }, {});
125
-
126
- const updatedPaddings = PADDINGS.reduce((acc, padding) => {
127
- const paddingValue = calculateContainerSpacing({
128
- spacingKey: padding,
129
- style,
130
- additionalSpacing: spacing,
131
- });
132
-
133
- return { ...acc, ...paddingValue };
134
- }, {});
135
-
136
- const updatedWidth = style.width
137
- ? isPercentageValue(style.width)
138
- ? style.width
139
- : toNumberWithDefaultZero(style.width) - spacing * 2
140
- : undefined;
141
-
142
- const updatedHeight = style.height
143
- ? isPercentageValue(style.height)
144
- ? style.height
145
- : toNumberWithDefaultZero(style.height) - spacing * 2
146
- : undefined;
147
-
148
- const containerStyle = {
149
- borderWidth: getContainerBorderWidth(borderPosition, borderWidth),
150
- padding: spacing,
151
- ...updatedMargins,
152
- ...updatedPaddings,
153
- width: updatedWidth,
154
- height: updatedHeight,
155
- };
110
+ const {
111
+ borderPosition,
112
+ borderPaddingTop,
113
+ borderPaddingRight,
114
+ borderPaddingBottom,
115
+ borderPaddingLeft,
116
+ style,
117
+ children,
118
+ } = props;
119
+
120
+ const padding =
121
+ borderPosition === "outside"
122
+ ? {
123
+ paddingTop: borderPaddingTop,
124
+ paddingRight: borderPaddingRight,
125
+ paddingBottom: borderPaddingBottom,
126
+ paddingLeft: borderPaddingLeft,
127
+ }
128
+ : {};
156
129
 
157
130
  return (
158
- <View {...props} style={[style, containerStyle]}>
131
+ <View pointerEvents="box-none" {...props} style={[style, styles.container]}>
132
+ <Border style={style} padding={padding} borderPosition={borderPosition} />
159
133
  {children}
160
- <InsideBorder
161
- style={{ ...style, ...borderStyleForMissingBorder }}
162
- borderPosition={borderPosition}
163
- />
164
134
  </View>
165
135
  );
166
136
  };
@@ -4,6 +4,7 @@ import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerM
4
4
 
5
5
  type DynamicBadgeProps = {
6
6
  cellUUID?: string;
7
+ uri?: string;
7
8
  };
8
9
 
9
10
  const DynamicBadgeComponent = (props: DynamicBadgeProps) => {
@@ -12,11 +13,12 @@ const DynamicBadgeComponent = (props: DynamicBadgeProps) => {
12
13
  // TODO: Initial value should depends on player state (playing or not)
13
14
  const [showImage, setShowImage] = React.useState(true);
14
15
  const player = usePlayer(playerId);
16
+ const uri = props?.uri;
15
17
 
16
18
  React.useEffect(() => {
17
19
  if (playerId && player) {
18
20
  return player.addListener({
19
- id: "dynamic-badges",
21
+ id: `dynamic-badges-${uri}`,
20
22
  listener: {
21
23
  onPlayerClose: () => {
22
24
  setShowImage(true);
@@ -35,7 +37,7 @@ const DynamicBadgeComponent = (props: DynamicBadgeProps) => {
35
37
  } else {
36
38
  setShowImage(true);
37
39
  }
38
- }, [player]);
40
+ }, [player, uri]);
39
41
 
40
42
  if (!showImage) {
41
43
  return null;
@@ -4,6 +4,8 @@ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Componen
4
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
5
5
  import * as R from "ramda";
6
6
  import { getXray } from "@applicaster/zapp-react-native-utils/logger";
7
+ import { toBooleanWithDefaultFalse } from "@applicaster/zapp-react-native-utils/booleanUtils";
8
+ import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
7
9
 
8
10
  const { Logger } = getXray();
9
11
 
@@ -35,12 +37,19 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
35
37
 
36
38
  const focusableId = getFocusableId(prefixId, suffixId);
37
39
 
38
- const focused = focusableId === focusedButtonId;
40
+ const focused =
41
+ focusableId === focusedButtonId ||
42
+ toBooleanWithDefaultFalse(otherProps.focused);
39
43
 
40
44
  const additionalStyles = focused ? focusedStyles : normalStyles;
41
45
 
42
46
  const actionContext = useActions(pluginIdentifier);
43
47
 
48
+ const accessibilityManager = useAccessibilityManager({});
49
+
50
+ const accessibilityLabel =
51
+ actionContext?.initialEntryState(item)?.accessibilityLabel;
52
+
44
53
  const onPress = (event) => {
45
54
  event?.preventDefault?.();
46
55
 
@@ -85,6 +94,12 @@ export function FocusableView({ style, children, item, ...otherProps }: Props) {
85
94
  mouse: focusable.mouse,
86
95
  });
87
96
  });
97
+
98
+ if (accessibilityLabel) {
99
+ accessibilityManager.readText({
100
+ text: accessibilityLabel,
101
+ });
102
+ }
88
103
  };
89
104
 
90
105
  const handleBlur = (_focusable, _direction) => {
@@ -43,14 +43,16 @@ function Image({
43
43
  [source, error]
44
44
  );
45
45
 
46
+ const _source = shouldRenderImg
47
+ ? withDimensions(source)
48
+ : defaultPlaceHolderImage;
49
+
46
50
  return (
47
51
  // @ts-ignore
48
52
  <MemoizedImage
49
53
  style={style as ImageStyle}
50
54
  onError={() => setErrorState(true)}
51
- source={
52
- shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
53
- }
55
+ source={_source?.uri ? _source : undefined}
54
56
  {...R.omit(["source"], otherProps)}
55
57
  />
56
58
  );
@@ -0,0 +1,71 @@
1
+ import { ImageStyle } from "react-native";
2
+
3
+ import {
4
+ getImageContainerMarginStyles,
5
+ ifElseFocusedSelected,
6
+ } from "@applicaster/zapp-react-native-utils/cellUtils";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+
9
+ export const ImageBorderContainer = ({
10
+ value,
11
+ state,
12
+ imageStyles,
13
+ }: {
14
+ value: (key: string) => string;
15
+ state: CellState;
16
+ imageStyles: ImageStyle;
17
+ }) => {
18
+ const imageMargins = getImageContainerMarginStyles({ value });
19
+
20
+ return {
21
+ type: "BorderContainerView",
22
+ style: {
23
+ position: "absolute",
24
+ // Image border now separated from the image, so cell padding affects the border position
25
+ marginTop:
26
+ toNumberWithDefaultZero(imageMargins.marginTop) +
27
+ toNumberWithDefaultZero(value("cell_padding_top")),
28
+ marginLeft:
29
+ toNumberWithDefaultZero(imageMargins.marginLeft) +
30
+ toNumberWithDefaultZero(value("cell_padding_left")),
31
+ marginRight:
32
+ toNumberWithDefaultZero(imageMargins.marginRight) +
33
+ toNumberWithDefaultZero(value("cell_padding_right")),
34
+ marginBottom:
35
+ toNumberWithDefaultZero(imageMargins.marginBottom) +
36
+ toNumberWithDefaultZero(value("cell_padding_bottom")),
37
+ zIndex: 1,
38
+ borderWidth: value("image_border_size"),
39
+ borderColor: ifElseFocusedSelected(
40
+ state,
41
+ value("image_focused_border_color"),
42
+ value("image_selected_border_color"),
43
+ value("image_focused_selected_border_color"),
44
+ value("image_border_color")
45
+ ),
46
+ borderRadius: toNumberWithDefaultZero(value("image_corner_radius")),
47
+ },
48
+ additionalProps: {
49
+ pointerEvents: "none",
50
+ borderPosition: value("image_border_position"),
51
+ borderPaddingTop: toNumberWithDefaultZero(
52
+ value("image_border_padding_top")
53
+ ),
54
+ borderPaddingRight: toNumberWithDefaultZero(
55
+ value("image_border_padding_right")
56
+ ),
57
+ borderPaddingBottom: toNumberWithDefaultZero(
58
+ value("image_border_padding_bottom")
59
+ ),
60
+ borderPaddingLeft: toNumberWithDefaultZero(
61
+ value("image_border_padding_left")
62
+ ),
63
+ },
64
+ elements: [
65
+ {
66
+ type: "View",
67
+ style: imageStyles,
68
+ },
69
+ ],
70
+ };
71
+ };
@@ -2,6 +2,7 @@ import React from "react";
2
2
  import { isVideoPreviewEnabled } from "@applicaster/zapp-react-native-ui-components/Components/MasterCell/utils";
3
3
  import { LiveImage } from "../LiveImage";
4
4
  import PureImage from "../Image";
5
+ import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks";
5
6
 
6
7
  type Props = {
7
8
  enable_video_preview: boolean;
@@ -9,7 +10,10 @@ type Props = {
9
10
  };
10
11
 
11
12
  export const ImageContainer = (props: Props) => {
12
- const Component = isVideoPreviewEnabled(props) ? LiveImage : PureImage;
13
+ const isActive = useIsScreenActive();
14
+
15
+ const Component =
16
+ isVideoPreviewEnabled(props) && isActive ? LiveImage : PureImage;
13
17
 
14
18
  return <Component {...props} />;
15
19
  };
@@ -108,6 +108,26 @@ const prepareEntry = (entry) => {
108
108
  };
109
109
  }
110
110
 
111
+ const previewPlayback =
112
+ entry.extensions?.["brightcove"]?.["preview_playback"];
113
+
114
+ if (previewPlayback) {
115
+ return {
116
+ ...entry,
117
+ extensions: {
118
+ ...entry.extensions,
119
+ brightcove: {
120
+ ...entry?.extensions?.["brightcove"],
121
+ video_id: previewPlayback,
122
+ },
123
+ },
124
+ };
125
+ }
126
+
127
+ if (entry.extensions?.["brightcove"]?.["video_id"]) {
128
+ return entry;
129
+ }
130
+
111
131
  const src = entry.content?.src;
112
132
 
113
133
  if (!src) {
@@ -177,10 +197,23 @@ const getPlayerConfig = (player_screen_id, actionIdentifier) => {
177
197
  const positionAbsolute: ViewStyle = { position: "absolute" };
178
198
 
179
199
  const LiveImageComponent = (props: LiveImageProps) => {
180
- const { item, style, cellUUID, imageKey, actionIdentifier, state } = props;
200
+ const {
201
+ item,
202
+ style: componentStyle,
203
+ cellUUID,
204
+ imageKey,
205
+ actionIdentifier,
206
+ state,
207
+ } = props;
181
208
 
182
209
  const component = useUIComponentContext();
183
210
 
211
+ // Fix for blinking on state change
212
+ const style = React.useMemo(
213
+ () => ({ ...componentStyle, backgroundColor: "transparent" }),
214
+ [componentStyle]
215
+ );
216
+
184
217
  const componentType = React.useMemo(
185
218
  () => getPropComponentType(component),
186
219
  [component?.id]
@@ -1,7 +1,6 @@
1
1
  import * as React from "react";
2
2
  import { Text } from "react-native";
3
3
  import { omit } from "ramda";
4
- import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
5
4
  import {
6
5
  textTransform,
7
6
  dateFormat,
@@ -9,6 +8,9 @@ import {
9
8
 
10
9
  import { useTextLabel, withFocusedStyles } from "./hooks";
11
10
  import { withScaledLineHeight } from "./utils";
11
+ import { toNumber } from "@applicaster/zapp-react-native-utils/numberUtils";
12
+ import { MeasurementPortalContext } from "../../../MeasurmentsPortal";
13
+ import { isNilOrEmpty } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
12
14
 
13
15
  type Props = {
14
16
  style: any;
@@ -32,14 +34,24 @@ const _Text = ({
32
34
  ...otherProps
33
35
  }: Props) => {
34
36
  const _label = useTextLabel({ label, entry });
37
+ const isMeasurement = React.useContext(MeasurementPortalContext);
35
38
 
36
39
  if (isNilOrEmpty(_label)) {
37
40
  return null;
38
41
  }
39
42
 
43
+ // set maximum possible height for the text in case of measurement
44
+ const height =
45
+ isMeasurement && otherProps.numberOfLines && style.lineHeight
46
+ ? toNumber(otherProps.numberOfLines) * toNumber(style.lineHeight)
47
+ : undefined;
48
+
40
49
  return (
41
50
  <Text
42
- style={withScaledLineHeight(withFocusedStyles({ style, otherProps }))}
51
+ style={[
52
+ withScaledLineHeight(withFocusedStyles({ style, otherProps })),
53
+ { height },
54
+ ]}
43
55
  {...withoutLabel(otherProps)}
44
56
  >
45
57
  {dateTransformEnabled
@@ -39,6 +39,6 @@ describe("image with no source", () => {
39
39
  );
40
40
 
41
41
  const testInstance = testRenderer.root;
42
- expect(testInstance.findByType(Image).props.source).toEqual({ uri: "" });
42
+ expect(testInstance.findByType(Image).props.source).toBeUndefined();
43
43
  });
44
44
  });
@@ -1,11 +1,23 @@
1
1
  import React from "react";
2
- import { Text } from "react-native";
2
+ import RN from "react-native";
3
3
  import { Provider } from "react-redux";
4
4
  import TestRenderer from "react-test-renderer";
5
- import configureStore from "redux-mock-store";
6
- import CustomText from "../Text";
5
+ import configureStoreFn from "redux-mock-store";
7
6
 
8
- const mockStore = configureStore();
7
+ const mockUseIsRTL = jest.fn(() => true);
8
+ const mockGetIsRTL = jest.fn(() => true);
9
+ const mockUseLocalizedStrings = jest.fn(() => ({}));
10
+ const mockGetUILanguage = jest.fn(() => "he-HE");
11
+
12
+ jest.mock("@applicaster/zapp-react-native-utils/localizationUtils", () => ({
13
+ useIsRTL: mockUseIsRTL,
14
+ getIsRTL: mockGetIsRTL,
15
+ useLocalizedStrings: mockUseLocalizedStrings,
16
+ getUILanguage: mockGetUILanguage,
17
+ }));
18
+
19
+ const CustomText = require("../Text").default;
20
+ const mockStore = configureStoreFn();
9
21
 
10
22
  const defaultProps = {
11
23
  entry: {},
@@ -22,7 +34,7 @@ const getRenderedText = (label: string, store: any) => {
22
34
  </Provider>
23
35
  );
24
36
 
25
- return testRenderer.root.findByType(Text).props.children;
37
+ return testRenderer.root.findByType(RN.Text).props.children;
26
38
  };
27
39
 
28
40
  describe("RTL app: Hebrew text contains english word", () => {
@@ -34,7 +46,7 @@ describe("RTL app: Hebrew text contains english word", () => {
34
46
  remoteConfigurations: { localizations: { he: {} } },
35
47
  appData: {
36
48
  languageCode: "he",
37
- countryCode: "HE",
49
+ countryCode: "IL",
38
50
  },
39
51
  });
40
52
 
@@ -47,18 +59,22 @@ describe("RTL app: Hebrew text contains english word", () => {
47
59
 
48
60
  it("Hebrew text contains not first english word", () => {
49
61
  const renderedText = getRenderedText(textWithNotFirstEnglishWord, store);
50
-
51
62
  expect(renderedText).toEqual(textWithNotFirstEnglishWord);
52
63
  });
53
64
 
54
65
  it("Hebrew text doesn't contain english word", () => {
55
66
  const renderedText = getRenderedText(textWithoutEnglishWord, store);
56
-
57
67
  expect(renderedText).toEqual(textWithoutEnglishWord);
58
68
  });
59
69
  });
60
70
 
61
71
  describe("LTR app: English text", () => {
72
+ beforeAll(() => {
73
+ mockUseIsRTL.mockReturnValue(false);
74
+ mockGetIsRTL.mockReturnValue(false);
75
+ mockGetUILanguage.mockReturnValue("en-US");
76
+ });
77
+
62
78
  const englishText = "Test sentence";
63
79
 
64
80
  const store = mockStore({
@@ -71,7 +87,6 @@ describe("LTR app: English text", () => {
71
87
 
72
88
  it("English text", () => {
73
89
  const renderedText = getRenderedText(englishText, store);
74
-
75
90
  expect(renderedText).toEqual(englishText);
76
91
  });
77
92
  });
@@ -15,6 +15,7 @@ import { BorderContainerView } from "./BorderContainerView";
15
15
  import { CellBadge } from "./CellBadge";
16
16
  import { TvActionButtons } from "./tv/TvActionButtons";
17
17
  import { ButtonContainerView } from "./tv/ButtonContainerView";
18
+ import { ImageBorderContainer } from "./ImageBorderContainer";
18
19
 
19
20
  export const defaultComponents = {
20
21
  View,
@@ -28,6 +29,7 @@ export const defaultComponents = {
28
29
  ProgressBar,
29
30
  Toggle,
30
31
  LiveImage,
32
+ ImageBorderContainer,
31
33
  BorderContainerView,
32
34
  DynamicBadge,
33
35
  SecondaryImage,
@@ -61,7 +61,10 @@ export function ButtonContainerView({
61
61
  <View style={applyRTLStylesIfNeeded(style, isRTL)}>
62
62
  {insertBetween(
63
63
  (index) => (
64
- <HorizontalSeparator key={index} width={horizontalGutter} />
64
+ <HorizontalSeparator
65
+ key={`separator_${index}`}
66
+ width={horizontalGutter}
67
+ />
65
68
  ),
66
69
  cloneElementsWithIds(buttonIds, filteredChildren)
67
70
  )}