@applicaster/zapp-react-native-ui-components 13.0.0-alpha.1045252438 → 13.0.0-alpha.1177234712

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.
@@ -0,0 +1,60 @@
1
+ import {
2
+ BorderContainerView,
3
+ getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
4
+ } from "../index";
5
+ import * as React from "react";
6
+ import { render } from "@testing-library/react-native";
7
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
8
+
9
+ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
10
+ toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
11
+ }));
12
+
13
+ describe("BorderContainerView", () => {
14
+ describe("getBorderPadding", () => {
15
+ it("returns 0 for inside", () => {
16
+ expect(getBorderPadding("inside", 10)).toBe(0);
17
+ });
18
+
19
+ it("returns borderWidth / 2 for center", () => {
20
+ expect(getBorderPadding("center", 10)).toBe(5);
21
+ });
22
+
23
+ it("returns borderWidth for outside", () => {
24
+ expect(getBorderPadding("outside", 10)).toBe(10);
25
+ });
26
+
27
+ it("returns borderWidth for invalid position", () => {
28
+ // @ts-ignore
29
+ expect(getBorderPadding("other_value", 10)).toBe(10);
30
+ });
31
+ });
32
+
33
+ it("Border component renders null if no borderPosition", () => {
34
+ const style = { borderWidth: 5, borderRadius: 10, borderColor: "red" };
35
+
36
+ const padding = {
37
+ paddingTop: "2",
38
+ paddingRight: "3",
39
+ paddingBottom: "4",
40
+ paddingLeft: "5",
41
+ };
42
+
43
+ const borderPosition = null;
44
+
45
+ const { container } = render(
46
+ <BorderContainerView
47
+ style={style}
48
+ borderPosition={borderPosition}
49
+ borderPaddingTop={toNumberWithDefaultZero(padding.paddingTop)}
50
+ borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
51
+ borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
52
+ borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
53
+ >
54
+ <div data-testid="child" />
55
+ </BorderContainerView>
56
+ );
57
+
58
+ expect(container.firstChild).toBeUndefined();
59
+ });
60
+ });
@@ -28,7 +28,10 @@ const styles = StyleSheet.create({
28
28
  },
29
29
  });
30
30
 
31
- const getBorderPadding = (borderPosition: BorderPosition, borderWidth) => {
31
+ export const getBorderPadding = (
32
+ borderPosition: BorderPosition,
33
+ borderWidth: number
34
+ ) => {
32
35
  switch (borderPosition) {
33
36
  case "inside":
34
37
  return 0;
@@ -38,22 +38,16 @@ function Image({
38
38
 
39
39
  const shouldRenderImg = source && !error;
40
40
 
41
- // Default placeholder image fallback is empty string because it won't rerender
42
- // in case of undefined or {uri: undefined} source
43
- // https://github.com/facebook/react-native/issues/9195
44
- const defaultPlaceHolderImage = React.useMemo(
45
- () => ({ uri: placeholderImage || "" }),
46
- [source, error]
47
- );
41
+ const _source = shouldRenderImg
42
+ ? withDimensions(source)
43
+ : { uri: placeholderImage };
48
44
 
49
45
  return (
50
- // @ts-ignore
51
46
  <MemoizedImage
52
47
  style={style as ImageStyle}
53
48
  onError={React.useCallback(() => setErrorState(true), [])}
54
- source={
55
- shouldRenderImg ? withDimensions(source) : defaultPlaceHolderImage
56
- }
49
+ // as we have defaults as "" for placeholder image, we need to pass undefined to source to not throw warnings
50
+ source={_source?.uri ? _source : undefined}
57
51
  {...R.omit(["source"], otherProps)}
58
52
  />
59
53
  );
@@ -0,0 +1,110 @@
1
+ import { ImageBorderContainer } from "../index";
2
+ import { getImageContainerMarginStyles } from "@applicaster/zapp-react-native-utils/cellUtils";
3
+
4
+ jest.mock("@applicaster/zapp-react-native-utils/cellUtils", () => ({
5
+ getImageContainerMarginStyles: jest.fn(),
6
+ ifElseFocusedSelected: jest.fn(
7
+ (state, focused, selected, focusedSelected, def) => {
8
+ if (state === "focused") return focused;
9
+ if (state === "selected") return selected;
10
+ if (state === "focusedSelected") return focusedSelected;
11
+
12
+ return def;
13
+ }
14
+ ),
15
+ }));
16
+
17
+ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
18
+ toNumberWithDefaultZero: jest.fn((v) => Number(v) || 0),
19
+ }));
20
+
21
+ const mockValue = (key) => {
22
+ const values = {
23
+ cell_padding_top: 1,
24
+ cell_padding_left: 2,
25
+ cell_padding_right: 3,
26
+ cell_padding_bottom: 4,
27
+ image_border_size: 5,
28
+ image_focused_border_color: "red",
29
+ image_selected_border_color: "blue",
30
+ image_focused_selected_border_color: "green",
31
+ image_border_color: "black",
32
+ image_corner_radius: 10,
33
+ image_border_position: "outside",
34
+ image_border_padding_top: 6,
35
+ image_border_padding_right: 7,
36
+ image_border_padding_bottom: 8,
37
+ image_border_padding_left: 9,
38
+ image_margin_top: 11,
39
+ image_margin_left: 12,
40
+ image_margin_right: 13,
41
+ image_margin_bottom: 14,
42
+ };
43
+
44
+ return values[key] || 0;
45
+ };
46
+
47
+ describe("ImageBorderContainer", () => {
48
+ it("calculates style properties correctly", () => {
49
+ const marginStyles = getImageContainerMarginStyles({ value: mockValue });
50
+
51
+ expect(marginStyles.marginTop).toBe(11);
52
+ expect(marginStyles.marginLeft).toBe(12);
53
+ expect(marginStyles.marginRight).toBe(13);
54
+ expect(marginStyles.marginBottom).toBe(14);
55
+
56
+ const props = {
57
+ value: mockValue,
58
+ state: "default",
59
+ imageStyles: {},
60
+ };
61
+
62
+ const result = ImageBorderContainer(props);
63
+
64
+ expect(result.style.marginTop).toBe(12); // 11 + 1
65
+ expect(result.style.marginLeft).toBe(14); // 12 + 2
66
+ expect(result.style.marginRight).toBe(16); // 13 + 3
67
+ expect(result.style.marginBottom).toBe(18); // 14 + 4
68
+ expect(result.style.borderWidth).toBe(5);
69
+ expect(result.style.borderColor).toBe("black");
70
+ expect(result.style.borderRadius).toBe(10);
71
+ expect(result.additionalProps.borderPosition).toBe("outside");
72
+ expect(result.additionalProps.borderPaddingTop).toBe(6);
73
+ expect(result.additionalProps.borderPaddingRight).toBe(7);
74
+ expect(result.additionalProps.borderPaddingBottom).toBe(8);
75
+ expect(result.additionalProps.borderPaddingLeft).toBe(9);
76
+ });
77
+
78
+ it("handles focused state correctly", () => {
79
+ const props = {
80
+ value: mockValue,
81
+ state: "focused",
82
+ imageStyles: {},
83
+ };
84
+
85
+ const result = ImageBorderContainer(props);
86
+ expect(result.style.borderColor).toBe("red");
87
+ });
88
+
89
+ it("handles selected state correctly", () => {
90
+ const props = {
91
+ value: mockValue,
92
+ state: "selected",
93
+ imageStyles: {},
94
+ };
95
+
96
+ const result = ImageBorderContainer(props);
97
+ expect(result.style.borderColor).toBe("blue");
98
+ });
99
+
100
+ it("handles focused and selected state correctly", () => {
101
+ const props = {
102
+ value: mockValue,
103
+ state: "focused_selected",
104
+ imageStyles: {},
105
+ };
106
+
107
+ const result = ImageBorderContainer(props);
108
+ expect(result.style.borderColor).toBe("green");
109
+ });
110
+ });
@@ -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
  });
@@ -8,6 +8,8 @@ import { loggerLiveImageManager } from "./loggerHelper";
8
8
  import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils";
9
9
  import { Component } from "react";
10
10
 
11
+ const TIMEOUT_FOR_DELAY_CHECK_PLAYER_POSITION = 500; // ms
12
+
11
13
  const { log_debug, log_info, log_error } = loggerLiveImageManager;
12
14
 
13
15
  export type LiveImageManagerEvent =
@@ -62,6 +64,9 @@ export class LiveImageManager implements PlayerLifecycleListener {
62
64
  protected currentlyPlaying: LiveImage | null = null;
63
65
  protected primaryPlayer: Player | null = null;
64
66
 
67
+ private checkPlayerPositionTimeout: ReturnType<typeof setTimeout> | null =
68
+ null;
69
+
65
70
  protected listeners: Record<
66
71
  string,
67
72
  Record<LiveImageManagerEvent, (event?: Record<string, any>) => void>
@@ -118,6 +123,11 @@ export class LiveImageManager implements PlayerLifecycleListener {
118
123
  });
119
124
  };
120
125
 
126
+ private cancelCheckPlayerPositionTimeout = () => {
127
+ clearTimeout(this.checkPlayerPositionTimeout);
128
+ this.checkPlayerPositionTimeout = null;
129
+ };
130
+
121
131
  public onViewportEnter = (item: LiveImage) => {
122
132
  log_debug(
123
133
  `onViewportEnter: live-image ${playerInfo(
@@ -125,7 +135,21 @@ export class LiveImageManager implements PlayerLifecycleListener {
125
135
  )}, primary ${playerInfo(this.primaryPlayer)}`
126
136
  );
127
137
 
128
- this.checkPlayerPosition(item);
138
+ if (!isTV()) {
139
+ // mobile only
140
+ // we have to delay running checkPlayerPosition, because sometimes on fast scrolling we get wrong order onEnter, then onLeave.
141
+ // which could cause select wrong item to play
142
+
143
+ this.cancelCheckPlayerPositionTimeout();
144
+
145
+ this.checkPlayerPositionTimeout = setTimeout(() => {
146
+ this.cancelCheckPlayerPositionTimeout();
147
+
148
+ this.checkPlayerPosition(item);
149
+ }, TIMEOUT_FOR_DELAY_CHECK_PLAYER_POSITION);
150
+ } else {
151
+ this.checkPlayerPosition(item);
152
+ }
129
153
  };
130
154
 
131
155
  public onViewportLeave = (item: LiveImage) => {
@@ -246,6 +270,8 @@ export class LiveImageManager implements PlayerLifecycleListener {
246
270
  };
247
271
 
248
272
  public checkPlayerPosition = (item: LiveImage) => {
273
+ this.cancelCheckPlayerPositionTimeout();
274
+
249
275
  log_debug(
250
276
  `checkPlayerPosition: live-image playerId: ${playerInfo(
251
277
  item.player
@@ -31,11 +31,6 @@ exports[`PlayerLiveImageComponent should render correctly with default props 1`]
31
31
  playerId="player1"
32
32
  resizeMode="cover"
33
33
  screenConfig={{}}
34
- source={
35
- {
36
- "uri": "",
37
- }
38
- }
39
34
  style={{}}
40
35
  />
41
36
  </View>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-ui-components",
3
- "version": "13.0.0-alpha.1045252438",
3
+ "version": "13.0.0-alpha.1177234712",
4
4
  "description": "Applicaster Zapp React Native ui components for the Quick Brick App",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -34,10 +34,10 @@
34
34
  "redux-mock-store": "^1.5.3"
35
35
  },
36
36
  "dependencies": {
37
- "@applicaster/applicaster-types": "13.0.0-alpha.1045252438",
38
- "@applicaster/zapp-react-native-bridge": "13.0.0-alpha.1045252438",
39
- "@applicaster/zapp-react-native-redux": "13.0.0-alpha.1045252438",
40
- "@applicaster/zapp-react-native-utils": "13.0.0-alpha.1045252438",
37
+ "@applicaster/applicaster-types": "13.0.0-alpha.1177234712",
38
+ "@applicaster/zapp-react-native-bridge": "13.0.0-alpha.1177234712",
39
+ "@applicaster/zapp-react-native-redux": "13.0.0-alpha.1177234712",
40
+ "@applicaster/zapp-react-native-utils": "13.0.0-alpha.1177234712",
41
41
  "promise": "^8.3.0",
42
42
  "react-router-native": "^5.1.2",
43
43
  "url": "^0.11.0",