@applicaster/zapp-react-native-ui-components 15.0.0-alpha.4153840309 → 15.0.0-alpha.4200413542

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 (85) hide show
  1. package/Components/BaseFocusable/index.ios.ts +12 -2
  2. package/Components/Cell/Cell.tsx +6 -0
  3. package/Components/Cell/CellWithFocusable.tsx +9 -0
  4. package/Components/Cell/FocusableWrapper.tsx +3 -0
  5. package/Components/Cell/TvOSCellComponent.tsx +17 -5
  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 +32 -1
  10. package/Components/GeneralContentScreen/utils/__tests__/useCurationAPI.test.js +1 -1
  11. package/Components/GeneralContentScreen/utils/useCurationAPI.ts +22 -6
  12. package/Components/HandlePlayable/HandlePlayable.tsx +10 -7
  13. package/Components/Layout/TV/LayoutBackground.tsx +5 -2
  14. package/Components/Layout/TV/ScreenContainer.tsx +2 -6
  15. package/Components/Layout/TV/index.tsx +3 -4
  16. package/Components/Layout/TV/index.web.tsx +3 -4
  17. package/Components/LinkHandler/LinkHandler.tsx +2 -2
  18. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -10
  19. package/Components/MasterCell/DefaultComponents/Image/Image.android.tsx +5 -1
  20. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +11 -3
  21. package/Components/MasterCell/DefaultComponents/Image/Image.web.tsx +9 -1
  22. package/Components/MasterCell/DefaultComponents/Image/hooks/useImage.ts +15 -14
  23. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +40 -39
  24. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/Image.test.tsx +95 -0
  25. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +86 -0
  26. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/index.test.ts +141 -0
  27. package/Components/MasterCell/DefaultComponents/SecondaryImage/hooks/__tests__/useGetImageDimensions.test.ts +7 -6
  28. package/Components/MasterCell/DefaultComponents/SecondaryImage/index.ts +1 -1
  29. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/index.ts +6 -2
  30. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/getPluginIdentifier.test.ts +233 -11
  31. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +19 -15
  32. package/Components/MasterCell/hoc/__tests__/withAsyncRender.test.tsx +219 -0
  33. package/Components/MasterCell/hoc/withAsyncRender.tsx +9 -7
  34. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +82 -3
  35. package/Components/MasterCell/utils/index.ts +61 -31
  36. package/Components/MeasurmentsPortal/MeasurementsPortal.tsx +102 -87
  37. package/Components/MeasurmentsPortal/__tests__/MeasurementsPortal.test.tsx +355 -0
  38. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +17 -9
  39. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +16 -8
  40. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +2 -2
  41. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +17 -18
  42. package/Components/OfflineHandler/NotificationView/utils.ts +34 -0
  43. package/Components/OfflineHandler/__tests__/index.test.tsx +27 -18
  44. package/Components/PlayerContainer/PlayerContainer.tsx +51 -40
  45. package/Components/PlayerContainer/useRestrictMobilePlayback.tsx +101 -0
  46. package/Components/River/ComponentsMap/ComponentsMap.tsx +16 -0
  47. package/Components/River/ComponentsMap/hooks/__tests__/useLoadingState.test.ts +1 -1
  48. package/Components/River/TV/River.tsx +31 -14
  49. package/Components/River/TV/index.tsx +8 -4
  50. package/Components/River/TV/utils/__tests__/toStringOrEmpty.test.ts +30 -0
  51. package/Components/River/TV/utils/index.ts +4 -0
  52. package/Components/River/TV/withFocusableGroupForContent.tsx +71 -0
  53. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +2 -0
  54. package/Components/River/__tests__/componentsMap.test.js +38 -0
  55. package/Components/Screen/TV/index.web.tsx +4 -2
  56. package/Components/Screen/__tests__/Screen.test.tsx +65 -42
  57. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +68 -44
  58. package/Components/Screen/hooks.ts +2 -3
  59. package/Components/Screen/index.tsx +2 -3
  60. package/Components/Screen/navigationHandler.ts +49 -24
  61. package/Components/Screen/orientationHandler.ts +10 -13
  62. package/Components/ScreenResolver/index.tsx +13 -7
  63. package/Components/Tabs/TabContent.tsx +7 -4
  64. package/Components/Transitioner/index.js +3 -3
  65. package/Components/VideoModal/ModalAnimation/ModalAnimationContext.tsx +13 -9
  66. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +15 -7
  67. package/Components/VideoModal/utils.ts +12 -9
  68. package/Components/Viewport/ViewportEvents/__tests__/viewportEvents.test.js +1 -1
  69. package/Contexts/ScreenContext/index.tsx +25 -18
  70. package/Contexts/ScreenTrackedViewPositionsContext/__tests__/index.test.tsx +1 -1
  71. package/Contexts/ZappHookModalContext/index.tsx +37 -61
  72. package/Contexts/index.ts +0 -2
  73. package/Decorators/Analytics/index.tsx +6 -5
  74. package/Decorators/ConfigurationWrapper/__tests__/__snapshots__/withConfigurationProvider.test.tsx.snap +1 -0
  75. package/Decorators/ConfigurationWrapper/const.ts +1 -0
  76. package/Decorators/ZappPipesDataConnector/__tests__/zappPipesDataConnector.test.js +1 -1
  77. package/Decorators/ZappPipesDataConnector/index.tsx +2 -2
  78. package/Decorators/ZappPipesDataConnector/resolvers/StaticFeedResolver.tsx +1 -1
  79. package/Helpers/DataSourceHelper/__tests__/itemLimitForData.test.ts +80 -0
  80. package/Helpers/DataSourceHelper/index.ts +19 -0
  81. package/events/index.ts +3 -0
  82. package/events/scrollEndReached.ts +15 -0
  83. package/package.json +6 -5
  84. package/Components/River/TV/withTVEventHandler.tsx +0 -27
  85. package/Helpers/DataSourceHelper/index.js +0 -19
@@ -1,19 +1,21 @@
1
1
  import * as React from "react";
2
- import { path } from "ramda";
3
2
 
4
3
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
5
4
  import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
6
5
  import { extractAsset } from "./utils";
7
6
 
8
7
  type Return = { uri: string } | null;
8
+ type Source = { context?: string; uri?: string } | null | undefined;
9
9
 
10
- const getSourceContext = path(["source", "context"]);
11
- const getSourceUri = path(["source", "uri"]);
12
- const getState = path(["state"]);
10
+ const getSourceContext = (source: Source) => source?.context;
11
+ const getSourceUri = (source: Source) => source?.uri;
13
12
 
14
- export const useImageSource = ({ uri, entry, otherProps }): Return => {
15
- const uriContext = getSourceContext(otherProps);
16
- const uriState = getState(otherProps);
13
+ export const useImageSource = ({
14
+ uri,
15
+ entry,
16
+ otherProps: { source, state: uriState },
17
+ }): Return => {
18
+ const uriContext = getSourceContext(source);
17
19
 
18
20
  const action = useActions(uriContext);
19
21
 
@@ -38,7 +40,7 @@ export const useImageSource = ({ uri, entry, otherProps }): Return => {
38
40
  return { uri };
39
41
  }
40
42
 
41
- const uriFromSource = getSourceUri(otherProps);
43
+ const uriFromSource = getSourceUri(source);
42
44
 
43
45
  if (uriFromSource) {
44
46
  return { uri: uriFromSource };
@@ -47,7 +49,7 @@ export const useImageSource = ({ uri, entry, otherProps }): Return => {
47
49
  return null;
48
50
  };
49
51
 
50
- const getSource = (uri, showDefault, placeholderImage, otherProps) => {
52
+ const getSource = (uri, showDefault, placeholderImage, source) => {
51
53
  const placeholderName = placeholderImage || "";
52
54
 
53
55
  const defaultPath = {
@@ -60,7 +62,7 @@ const getSource = (uri, showDefault, placeholderImage, otherProps) => {
60
62
  return { uri };
61
63
  }
62
64
 
63
- const uriFromSource = getSourceUri(otherProps);
65
+ const uriFromSource = getSourceUri(source);
64
66
 
65
67
  if (uriFromSource) {
66
68
  return { uri: uriFromSource };
@@ -74,10 +76,9 @@ export const useImageSourceWithDefault = ({
74
76
  entry,
75
77
  showDefault,
76
78
  placeholderImage,
77
- otherProps,
79
+ otherProps: { state: uriState, source },
78
80
  }): Return => {
79
- const uriContext = getSourceContext(otherProps);
80
- const uriState = getState(otherProps);
81
+ const uriContext = getSourceContext(source);
81
82
 
82
83
  const action = useActions(uriContext);
83
84
 
@@ -98,5 +99,5 @@ export const useImageSourceWithDefault = ({
98
99
  return extractAsset(!isTV(), entryStateLocal.asset, uriState);
99
100
  }
100
101
 
101
- return getSource(uri, showDefault, placeholderImage, otherProps);
102
+ return getSource(uri, showDefault, placeholderImage, source);
102
103
  };
@@ -3,8 +3,8 @@ import { isNil } from "ramda";
3
3
  import { ImageStyle, View } from "react-native";
4
4
  import {
5
5
  FIT_POSITION,
6
- IMAGE_SIZING_FIT,
7
6
  IMAGE_SIZING_FILL,
7
+ IMAGE_SIZING_FIT,
8
8
  } from "@applicaster/zapp-react-native-utils/manifestUtils/secondaryImage";
9
9
  import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
10
10
 
@@ -25,49 +25,51 @@ interface Props {
25
25
  fitPosition: typeof FIT_POSITION;
26
26
  fixedWidth: number;
27
27
  fixedHeight: number;
28
- onAsyncRender: () => void;
28
+ onAsyncRender?: () => void;
29
29
  }
30
30
 
31
31
  /** Secondary Image Dynamic does not render until the image is loaded */
32
- const SecondaryImageDynamic = (props: Props) => {
33
- const { uri, style, displayMode, imageSizing, fitPosition, onAsyncRender } =
34
- props;
32
+ const SecondaryImageDynamic = withAsyncRenderHOC(
33
+ (props: Props & { onAsyncRender: () => void }) => {
34
+ const { uri, style, displayMode, imageSizing, fitPosition, onAsyncRender } =
35
+ props;
35
36
 
36
- const imageDimension = useGetImageDimensions(
37
- uri,
38
- style.width as number,
39
- isImageSizingFit(imageSizing) ? undefined : (style.height as number)
40
- );
37
+ const imageDimension = useGetImageDimensions(
38
+ uri,
39
+ style.width as number,
40
+ isImageSizingFit(imageSizing) ? undefined : (style.height as number)
41
+ );
41
42
 
42
- const containerHeight = imageDimension?.height;
43
+ const containerHeight = imageDimension?.height;
43
44
 
44
- const containerWidth = style?.width;
45
+ const containerWidth = style?.width;
45
46
 
46
- if (isNil(imageDimension?.aspectRatio)) {
47
- return null;
48
- }
47
+ if (isNil(imageDimension?.aspectRatio)) {
48
+ return null;
49
+ }
49
50
 
50
- return (
51
- <View style={style} onLayout={onAsyncRender}>
52
- <Image
53
- {...props}
54
- source={{ uri }}
55
- style={{
56
- ...getStyle({
57
- imageSizing,
58
- fitPosition,
59
- displayMode,
60
- imageDimension,
61
- containerHeight,
62
- containerWidth,
63
- }),
64
- borderRadius: style.borderRadius,
65
- aspectRatio: imageDimension.aspectRatio,
66
- }}
67
- />
68
- </View>
69
- );
70
- };
51
+ return (
52
+ <View style={style} onLayout={onAsyncRender}>
53
+ <Image
54
+ {...props}
55
+ source={{ uri }}
56
+ style={{
57
+ ...getStyle({
58
+ imageSizing,
59
+ fitPosition,
60
+ displayMode,
61
+ imageDimension,
62
+ containerHeight,
63
+ containerWidth,
64
+ }),
65
+ borderRadius: style.borderRadius,
66
+ aspectRatio: imageDimension.aspectRatio,
67
+ }}
68
+ />
69
+ </View>
70
+ );
71
+ }
72
+ );
71
73
 
72
74
  /** Secondary Image Fixed does not render the image until the image is loaded, but keep container rendered */
73
75
  const SecondaryImageFixed = (props: Props) => {
@@ -79,7 +81,6 @@ const SecondaryImageFixed = (props: Props) => {
79
81
  fitPosition,
80
82
  fixedHeight,
81
83
  fixedWidth,
82
- onAsyncRender,
83
84
  } = props;
84
85
 
85
86
  const imageDimension = useGetImageDimensions(
@@ -89,7 +90,7 @@ const SecondaryImageFixed = (props: Props) => {
89
90
  );
90
91
 
91
92
  return (
92
- <View style={style} onLayout={onAsyncRender}>
93
+ <View style={style}>
93
94
  {isNil(imageDimension?.aspectRatio) ? null : (
94
95
  <Image
95
96
  {...props}
@@ -128,4 +129,4 @@ const SecondaryImageComponent = (props: Props) => {
128
129
  );
129
130
  };
130
131
 
131
- export const SecondaryImage = withAsyncRenderHOC(SecondaryImageComponent);
132
+ export const SecondaryImage = SecondaryImageComponent;
@@ -10,6 +10,10 @@ describe("SecondaryImage - Image", () => {
10
10
  displayMode="dynamic"
11
11
  uri={undefined}
12
12
  style={{ width: 100 }}
13
+ imageSizing="fit"
14
+ fitPosition="center"
15
+ fixedWidth={0}
16
+ fixedHeight={0}
13
17
  />
14
18
  );
15
19
 
@@ -23,6 +27,10 @@ describe("SecondaryImage - Image", () => {
23
27
  displayMode="dynamic"
24
28
  uri="someurl"
25
29
  style={{ width: 100 }}
30
+ imageSizing="fit"
31
+ fitPosition="center"
32
+ fixedWidth={0}
33
+ fixedHeight={0}
26
34
  />
27
35
  );
28
36
 
@@ -36,6 +44,93 @@ describe("SecondaryImage - Image", () => {
36
44
  uri="someUrl"
37
45
  displayMode="dynamic"
38
46
  style={{ width: 100, height: 100, borderRadius: 10 }}
47
+ imageSizing="fill"
48
+ fitPosition="center"
49
+ fixedWidth={0}
50
+ fixedHeight={0}
51
+ />
52
+ );
53
+
54
+ expect(wrapper.toJSON()).not.toEqual(null);
55
+ expect(wrapper.toJSON()).toMatchSnapshot();
56
+ });
57
+
58
+ it("SecondaryImage should render in fixed mode without image until loaded", async () => {
59
+ const wrapper = await render(
60
+ <SecondaryImage
61
+ displayMode="fixed"
62
+ uri="someurl"
63
+ style={{ width: 100, height: 100, borderRadius: 5 }}
64
+ imageSizing="fit"
65
+ fitPosition="center"
66
+ fixedWidth={100}
67
+ fixedHeight={100}
68
+ />
69
+ );
70
+
71
+ expect(wrapper.toJSON()).toMatchSnapshot();
72
+ });
73
+
74
+ it("SecondaryImage should not render in dynamic mode without image", async () => {
75
+ const wrapper = await render(
76
+ <SecondaryImage
77
+ displayMode="dynamic"
78
+ uri={null}
79
+ style={{ width: 100, height: 100 }}
80
+ imageSizing="fit"
81
+ fitPosition="center"
82
+ fixedWidth={0}
83
+ fixedHeight={0}
84
+ />
85
+ );
86
+
87
+ expect(wrapper.toJSON()).toEqual(null);
88
+ expect(wrapper.toJSON()).toMatchSnapshot();
89
+ });
90
+
91
+ it("SecondaryImage should render in fixed mode with known dimensions", async () => {
92
+ const wrapper = await render(
93
+ <SecondaryImage
94
+ uri="someUrl"
95
+ displayMode="fixed"
96
+ style={{ width: 100, height: 100, borderRadius: 10 }}
97
+ imageSizing="fill"
98
+ fitPosition="center"
99
+ fixedWidth={100}
100
+ fixedHeight={100}
101
+ />
102
+ );
103
+
104
+ expect(wrapper.toJSON()).not.toEqual(null);
105
+ expect(wrapper.toJSON()).toMatchSnapshot();
106
+ });
107
+
108
+ it("SecondaryImage dynamic mode should call onAsyncRender when provided", async () => {
109
+ const wrapper = await render(
110
+ <SecondaryImage
111
+ uri="someUrl"
112
+ displayMode="dynamic"
113
+ style={{ width: 100, height: 100, borderRadius: 10 }}
114
+ imageSizing="fill"
115
+ fitPosition="center"
116
+ fixedWidth={0}
117
+ fixedHeight={0}
118
+ />
119
+ );
120
+
121
+ expect(wrapper.toJSON()).not.toEqual(null);
122
+ });
123
+
124
+ it("SecondaryImage fixed mode should not use async render props", async () => {
125
+ const wrapper = await render(
126
+ <SecondaryImage
127
+ uri="someUrl"
128
+ displayMode="fixed"
129
+ style={{ width: 100, height: 100, borderRadius: 10 }}
130
+ imageSizing="fill"
131
+ fitPosition="center"
132
+ fixedWidth={100}
133
+ fixedHeight={100}
39
134
  />
40
135
  );
41
136
 
@@ -1,9 +1,45 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
+ exports[`SecondaryImage - Image SecondaryImage fixed mode should not use async render props 1`] = `
4
+ <View
5
+ style={
6
+ {
7
+ "borderRadius": 10,
8
+ "height": 100,
9
+ "width": 100,
10
+ }
11
+ }
12
+ >
13
+ <Image
14
+ displayMode="fixed"
15
+ fitPosition="center"
16
+ fixedHeight={100}
17
+ fixedWidth={100}
18
+ imageSizing="fill"
19
+ source={
20
+ {
21
+ "uri": "someUrl",
22
+ }
23
+ }
24
+ style={
25
+ {
26
+ "aspectRatio": 1,
27
+ "borderRadius": 10,
28
+ "height": 100,
29
+ "width": 100,
30
+ }
31
+ }
32
+ uri="someUrl"
33
+ />
34
+ </View>
35
+ `;
36
+
3
37
  exports[`SecondaryImage - Image SecondaryImage should not render if no aspect ratio (dynamic) 1`] = `null`;
4
38
 
5
39
  exports[`SecondaryImage - Image SecondaryImage should not render if no uri 1`] = `null`;
6
40
 
41
+ exports[`SecondaryImage - Image SecondaryImage should not render in dynamic mode without image 1`] = `null`;
42
+
7
43
  exports[`SecondaryImage - Image SecondaryImage should render if known dimensions 1`] = `
8
44
  <View
9
45
  onLayout={[Function]}
@@ -17,6 +53,10 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
17
53
  >
18
54
  <Image
19
55
  displayMode="dynamic"
56
+ fitPosition="center"
57
+ fixedHeight={0}
58
+ fixedWidth={0}
59
+ imageSizing="fill"
20
60
  onAsyncRender={[Function]}
21
61
  source={
22
62
  {
@@ -35,3 +75,49 @@ exports[`SecondaryImage - Image SecondaryImage should render if known dimensions
35
75
  />
36
76
  </View>
37
77
  `;
78
+
79
+ exports[`SecondaryImage - Image SecondaryImage should render in fixed mode with known dimensions 1`] = `
80
+ <View
81
+ style={
82
+ {
83
+ "borderRadius": 10,
84
+ "height": 100,
85
+ "width": 100,
86
+ }
87
+ }
88
+ >
89
+ <Image
90
+ displayMode="fixed"
91
+ fitPosition="center"
92
+ fixedHeight={100}
93
+ fixedWidth={100}
94
+ imageSizing="fill"
95
+ source={
96
+ {
97
+ "uri": "someUrl",
98
+ }
99
+ }
100
+ style={
101
+ {
102
+ "aspectRatio": 1,
103
+ "borderRadius": 10,
104
+ "height": 100,
105
+ "width": 100,
106
+ }
107
+ }
108
+ uri="someUrl"
109
+ />
110
+ </View>
111
+ `;
112
+
113
+ exports[`SecondaryImage - Image SecondaryImage should render in fixed mode without image until loaded 1`] = `
114
+ <View
115
+ style={
116
+ {
117
+ "borderRadius": 5,
118
+ "height": 100,
119
+ "width": 100,
120
+ }
121
+ }
122
+ />
123
+ `;
@@ -0,0 +1,141 @@
1
+ import { SecondaryImage } from "../index";
2
+
3
+ describe("SecondaryImage - Configuration Builder", () => {
4
+ const mockValue = (config: any) => (key: string, transformer?: Function) => {
5
+ const value = config[key];
6
+
7
+ return transformer ? transformer(value) : value;
8
+ };
9
+
10
+ it("should pass false as second argument to image_src_from_media_item", () => {
11
+ const config = {
12
+ secondary_image_switch: true,
13
+ secondary_image_position: "over_image",
14
+ secondary_image_visibility: "always",
15
+ secondary_image_image_key: "custom_image",
16
+ secondary_image_display_mode: "dynamic",
17
+ secondary_image_dynamic_width: 100,
18
+ };
19
+
20
+ const result = SecondaryImage({
21
+ value: mockValue(config),
22
+ currentPosition: "over_image",
23
+ state: "default",
24
+ });
25
+
26
+ expect(result).not.toBeNull();
27
+ expect(result?.elements).toBeDefined();
28
+ expect(result?.elements[0].data).toBeDefined();
29
+
30
+ const imageData = result?.elements[0].data[0];
31
+ expect(imageData.func).toBe("image_src_from_media_item");
32
+ expect(imageData.args).toEqual(["custom_image", false]);
33
+ expect(imageData.propName).toBe("uri");
34
+ });
35
+
36
+ it("should return null when secondary image is disabled", () => {
37
+ const config = {
38
+ secondary_image_switch: false,
39
+ };
40
+
41
+ const result = SecondaryImage({
42
+ value: mockValue(config),
43
+ currentPosition: "over_image",
44
+ state: "default",
45
+ });
46
+
47
+ expect(result).toBeNull();
48
+ });
49
+
50
+ it("should return null when position does not match currentPosition", () => {
51
+ const config = {
52
+ secondary_image_switch: true,
53
+ secondary_image_position: "above_text_label_1",
54
+ secondary_image_visibility: "always",
55
+ };
56
+
57
+ const result = SecondaryImage({
58
+ value: mockValue(config),
59
+ currentPosition: "over_image",
60
+ state: "default",
61
+ });
62
+
63
+ expect(result).toBeNull();
64
+ });
65
+
66
+ it("should return null when visibility does not match state", () => {
67
+ const config = {
68
+ secondary_image_switch: true,
69
+ secondary_image_position: "over_image",
70
+ secondary_image_visibility: "focused",
71
+ };
72
+
73
+ const result = SecondaryImage({
74
+ value: mockValue(config),
75
+ currentPosition: "over_image",
76
+ state: "default",
77
+ });
78
+
79
+ expect(result).toBeNull();
80
+ });
81
+
82
+ it("should include display mode and image sizing in additionalProps", () => {
83
+ const config = {
84
+ secondary_image_switch: true,
85
+ secondary_image_position: "over_image",
86
+ secondary_image_visibility: "always",
87
+ secondary_image_image_key: "logo",
88
+ secondary_image_display_mode: "fixed",
89
+ secondary_image_image_sizing: "fill",
90
+ secondary_image_fit_position: "center",
91
+ secondary_image_fixed_width: 200,
92
+ secondary_image_fixed_height: 150,
93
+ };
94
+
95
+ const result = SecondaryImage({
96
+ value: mockValue(config),
97
+ currentPosition: "over_image",
98
+ state: "default",
99
+ });
100
+
101
+ expect(result?.elements[0].additionalProps).toMatchObject({
102
+ displayMode: "fixed",
103
+ imageSizing: "fill",
104
+ fitPosition: "center",
105
+ fixedWidth: 200,
106
+ fixedHeight: 150,
107
+ });
108
+ });
109
+
110
+ it("should apply correct styles including margins and border radius", () => {
111
+ const config = {
112
+ secondary_image_switch: true,
113
+ secondary_image_position: "over_image",
114
+ secondary_image_visibility: "always",
115
+ secondary_image_display_mode: "fixed",
116
+ secondary_image_fixed_width: 100,
117
+ secondary_image_fixed_height: 100,
118
+ secondary_image_corner_radius: 12,
119
+ secondary_image_margin_top: 10,
120
+ secondary_image_margin_left: 5,
121
+ secondary_image_margin_right: 5,
122
+ secondary_image_margin_bottom: 10,
123
+ };
124
+
125
+ const result = SecondaryImage({
126
+ value: mockValue(config),
127
+ currentPosition: "over_image",
128
+ state: "default",
129
+ });
130
+
131
+ expect(result?.elements[0].style).toMatchObject({
132
+ width: 100,
133
+ height: 100,
134
+ borderRadius: 12,
135
+ marginTop: 10,
136
+ marginLeft: 5,
137
+ marginRight: 5,
138
+ marginBottom: 10,
139
+ });
140
+ });
141
+ });
@@ -1,4 +1,4 @@
1
- import { renderHook } from "@testing-library/react-hooks";
1
+ import { renderHook, waitFor } from "@testing-library/react-native";
2
2
  import { Image } from "react-native";
3
3
 
4
4
  import { useGetImageDimensions } from "../useGetImageDimensions";
@@ -13,15 +13,16 @@ jest.spyOn(Image, "getSize").mockImplementation((_uri, success) => {
13
13
 
14
14
  describe("useGetImageDimensions", () => {
15
15
  it("should return aspect ration initially when known dimensions", async () => {
16
- const { result, waitForNextUpdate } = renderHook(() =>
16
+ const { result } = renderHook(() =>
17
17
  useGetImageDimensions("https://some_url.com", WIDTH, undefined)
18
18
  );
19
19
 
20
20
  expect(result.current).toBeUndefined();
21
- await waitForNextUpdate();
22
21
 
23
- expect(result.current).toEqual(
24
- getDimension({ width: WIDTH, height: HEIGTH })
25
- );
22
+ await waitFor(() => {
23
+ expect(result.current).toEqual(
24
+ getDimension({ width: WIDTH, height: HEIGTH })
25
+ );
26
+ });
26
27
  });
27
28
  });
@@ -103,7 +103,7 @@ export const SecondaryImage = ({ value, currentPosition, state }: Props) => {
103
103
  data: [
104
104
  {
105
105
  func: "image_src_from_media_item",
106
- args: [imageKey],
106
+ args: [imageKey, false],
107
107
  propName: "uri",
108
108
  },
109
109
  ],
@@ -4,7 +4,7 @@ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/nu
4
4
  import { Button } from "./Button";
5
5
  import {
6
6
  getButtonsCount,
7
- getPluginIdentifier,
7
+ memoizedGetPluginIdentifier,
8
8
  mapSelfAlignment,
9
9
  } from "./utils";
10
10
 
@@ -80,7 +80,11 @@ export const TvActionButtons = ({
80
80
  prefix: independentStyles ? prefixSpecificButton : buttonId(1),
81
81
  value,
82
82
  platformValue,
83
- pluginIdentifier: getPluginIdentifier(configuration, PREFIX, index),
83
+ pluginIdentifier: memoizedGetPluginIdentifier(
84
+ configuration,
85
+ PREFIX,
86
+ index
87
+ ),
84
88
  suffixId: prefixSpecificButton,
85
89
  preferredFocus: index === 0,
86
90
  });