@applicaster/zapp-react-native-ui-components 13.0.0-alpha.7222542422 → 13.0.0-alpha.7846284820

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 (49) hide show
  1. package/Components/BaseFocusable/index.ios.ts +1 -1
  2. package/Components/BaseFocusable/index.tsx +1 -1
  3. package/Components/Cell/Cell.tsx +7 -4
  4. package/Components/Cell/TvOSCellComponent.tsx +22 -20
  5. package/Components/Cell/index.js +1 -3
  6. package/Components/Focusable/FocusableTvOS.tsx +1 -5
  7. package/Components/Focusable/Touchable.tsx +19 -20
  8. package/Components/FocusableList/index.tsx +9 -25
  9. package/Components/FocusableScrollView/index.tsx +12 -39
  10. package/Components/Layout/TV/NavBarContainer.tsx +7 -8
  11. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +66 -0
  12. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +4 -1
  13. package/Components/MasterCell/DefaultComponents/Image/Image.ios.tsx +5 -11
  14. package/Components/MasterCell/DefaultComponents/ImageBorderContainer/__tests__/index.test.ts +93 -0
  15. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +1 -1
  16. package/Components/MasterCell/DefaultComponents/__tests__/image.test.js +1 -1
  17. package/Components/MasterCell/utils/index.ts +1 -1
  18. package/Components/River/TV/index.tsx +1 -6
  19. package/Components/River/__tests__/componentsMap.test.js +14 -1
  20. package/Components/Screen/index.tsx +1 -0
  21. package/Components/Tabs/Tab.tsx +6 -6
  22. package/Components/TextInputTv/index.tsx +2 -2
  23. package/Components/Transitioner/AnimationManager.js +8 -8
  24. package/Components/Transitioner/Scene.tsx +52 -23
  25. package/Components/Transitioner/__tests__/__snapshots__/Scene.test.js.snap +59 -43
  26. package/Components/Transitioner/__tests__/__snapshots__/transitioner.test.js.snap +2 -2
  27. package/Components/Transitioner/index.js +8 -4
  28. package/Components/VideoLive/LiveImageManager.ts +27 -1
  29. package/Components/VideoLive/PlayerLiveImageComponent.tsx +29 -21
  30. package/Components/VideoLive/__tests__/PlayerLiveImageComponent.test.tsx +51 -1
  31. package/Components/VideoLive/__tests__/__snapshots__/PlayerLiveImageComponent.test.tsx.snap +0 -5
  32. package/Components/VideoModal/ModalAnimation/AnimationComponent.tsx +7 -6
  33. package/Components/VideoModal/ModalAnimation/utils.ts +2 -2
  34. package/Components/VideoModal/OpaqueLayer.tsx +33 -0
  35. package/Components/VideoModal/PlayerWrapper.tsx +16 -35
  36. package/Components/VideoModal/VideoModal.tsx +14 -23
  37. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -1
  38. package/Components/VideoModal/__tests__/__snapshots__/PlayerWrapper.test.tsx.snap +0 -90
  39. package/Components/VideoModal/hooks/__tests__/useDelayedPlayerDetails.test.ts +89 -0
  40. package/Components/VideoModal/hooks/index.ts +7 -0
  41. package/Components/VideoModal/hooks/useDelayedPlayerDetails.ts +49 -0
  42. package/Components/VideoModal/hooks/utils/__tests__/showDetails.test.ts +91 -0
  43. package/Components/VideoModal/hooks/utils/index.ts +33 -0
  44. package/Components/VideoModal/utils.ts +1 -1
  45. package/Contexts/ScreenContext/index.tsx +3 -2
  46. package/Decorators/ZappPipesDataConnector/__tests__/Hero.js +1 -1
  47. package/Decorators/ZappPipesDataConnector/index.tsx +31 -1
  48. package/package.json +5 -5
  49. package/Contexts/ComponentsMapOffsetContext/index.tsx +0 -41
@@ -25,7 +25,7 @@ type Props = {
25
25
  };
26
26
 
27
27
  export class BaseFocusable<
28
- T extends Props = Props
28
+ T extends Props = Props,
29
29
  > extends BaseFocusableInterface<T> {
30
30
  constructor(props) {
31
31
  super(props);
@@ -35,7 +35,7 @@ type Props = {
35
35
  };
36
36
 
37
37
  export class BaseFocusable<
38
- T extends Props = Props
38
+ T extends Props = Props,
39
39
  > extends BaseFocusableInterface<T> {
40
40
  constructor(props) {
41
41
  super(props);
@@ -61,7 +61,6 @@ type Props = {
61
61
  skipFocusManagerRegistration?: boolean;
62
62
  shouldUpdate: boolean;
63
63
  behavior: Behavior;
64
- componentsMapOffset: number;
65
64
  };
66
65
 
67
66
  type State = {
@@ -274,10 +273,14 @@ export class CellComponent extends React.Component<Props, State> {
274
273
  if (isFocused) {
275
274
  const accessibilityManager = AccessibilityManager.getInstance();
276
275
 
276
+ const accessibilityTitle =
277
+ item?.extensions?.accessibility?.label || item?.title || "";
278
+
279
+ const accessibilityHint =
280
+ item?.extensions?.accessibility?.hint || "";
281
+
277
282
  accessibilityManager.readText({
278
- text: String(
279
- item.extensions?.accessibility?.label || item.title
280
- ),
283
+ text: `${accessibilityTitle} ${accessibilityHint}`,
281
284
  });
282
285
  }
283
286
 
@@ -2,7 +2,7 @@ import * as React from "react";
2
2
  import * as R from "ramda";
3
3
  import { View, StyleSheet } from "react-native";
4
4
 
5
- import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable/FocusableTvOS";
5
+ import { Focusable } from "@applicaster/zapp-react-native-ui-components/Components/Focusable";
6
6
  import { FocusableCell } from "@applicaster/zapp-react-native-ui-components/Components/FocusableCell";
7
7
  import { getItemType } from "@applicaster/zapp-react-native-utils/navigationUtils";
8
8
  import { SCREEN_TYPES } from "@applicaster/zapp-react-native-utils/navigationUtils/itemTypes";
@@ -65,7 +65,6 @@ type Props = {
65
65
  isFocusable: boolean;
66
66
  shouldUpdate: boolean;
67
67
  behavior: Behavior;
68
- componentsMapOffset: number;
69
68
  };
70
69
 
71
70
  type State = {
@@ -180,7 +179,6 @@ export class TvOSCellComponent extends React.Component<Props, State> {
180
179
  groupId,
181
180
  component,
182
181
  index,
183
- componentsMapOffset,
184
182
  } = this.props;
185
183
 
186
184
  this.setScreenLayout(componentAnchorPointY, screenLayout);
@@ -195,11 +193,13 @@ export class TvOSCellComponent extends React.Component<Props, State> {
195
193
  const extraAnchorPointYOffset =
196
194
  screenLayout?.extraAnchorPointYOffset || 0;
197
195
 
196
+ const marginTop = screenLayout?.screenMarginTop || 0;
197
+
198
198
  const totalOffset =
199
199
  headerOffset +
200
- (componentAnchorPointY || 0) +
201
- extraAnchorPointYOffset -
202
- componentsMapOffset || 0;
200
+ (componentAnchorPointY || 0) +
201
+ extraAnchorPointYOffset -
202
+ marginTop;
203
203
 
204
204
  mainOffsetUpdater?.(
205
205
  { tag: this.target },
@@ -247,7 +247,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
247
247
 
248
248
  const focusableId = R.join("-", [component?.id, id, index]);
249
249
 
250
- const handleFocus = (arg1: any, index?: number) => {
250
+ const handleFocus = (arg1: any, index: number) => {
251
251
  const focusFn = onFocus || noop;
252
252
  focusFn(arg1, index);
253
253
 
@@ -280,7 +280,7 @@ export class TvOSCellComponent extends React.Component<Props, State> {
280
280
  <View onLayout={this.onLayout} style={styles.container}>
281
281
  <Focusable
282
282
  id={focusableId}
283
- groupId={String(groupId || component?.id)}
283
+ groupId={groupId || component?.id}
284
284
  onFocus={handleFocus}
285
285
  onBlur={onBlur || this.onBlur}
286
286
  isParallaxDisabled={this.layout?.width > 1740}
@@ -292,18 +292,20 @@ export class TvOSCellComponent extends React.Component<Props, State> {
292
292
  style={baseCellStyles}
293
293
  isFocusable={isFocusable}
294
294
  >
295
- {(focused) => (
296
- <FocusableCell
297
- {...{
298
- index,
299
- CellRenderer,
300
- item,
301
- focused: !!(this.props.focused || selectedCell || focused),
302
- scrollTo: this.scrollTo,
303
- behavior,
304
- }}
305
- />
306
- )}
295
+ {(focused) => {
296
+ return (
297
+ <FocusableCell
298
+ {...{
299
+ index,
300
+ CellRenderer,
301
+ item,
302
+ focused: this.props.focused || selectedCell || focused,
303
+ scrollTo: this.scrollTo,
304
+ behavior,
305
+ }}
306
+ />
307
+ );
308
+ }}
307
309
  </Focusable>
308
310
  </View>
309
311
  );
@@ -11,7 +11,6 @@ import { ScreenScrollingContext } from "../../Contexts/ScreenScrollingContext";
11
11
 
12
12
  import { ScreenLayoutContextConsumer } from "../../Contexts/ScreenLayoutContext";
13
13
  import { createContext } from "@applicaster/zapp-react-native-utils/reactUtils/createContext";
14
- import { withComponentsMapOffsetContextConsumer } from "../../Contexts/ComponentsMapOffsetContext";
15
14
 
16
15
  // TODO | Gallery QB | Extract this
17
16
  export const ScrollInterceptorContext = createContext(
@@ -31,6 +30,5 @@ export const Cell = R.compose(
31
30
  RiverOffsetContext.withConsumer,
32
31
  HorizontalScrollContext.withConsumer,
33
32
  withConsumer,
34
- ScreenLayoutContextConsumer,
35
- withComponentsMapOffsetContextConsumer
33
+ ScreenLayoutContextConsumer
36
34
  )(Component);
@@ -9,7 +9,7 @@ import {
9
9
  focusManager,
10
10
  forceFocusableFocus,
11
11
  } from "@applicaster/zapp-react-native-utils/appUtils/focusManager/index.ios";
12
- import { findNodeHandle, ViewStyle } from "react-native";
12
+ import { findNodeHandle } from "react-native";
13
13
 
14
14
  function noop() {}
15
15
 
@@ -35,10 +35,6 @@ type Props = {
35
35
  forceFocus?: boolean;
36
36
  initialFocus?: boolean;
37
37
  onLayout?: (e: any) => void;
38
- willReceiveFocus: () => void;
39
- hasReceivedFocus: () => void;
40
- offsetUpdater: (arg1: string, arg2: number) => number;
41
- style: ViewStyle;
42
38
  };
43
39
 
44
40
  export class Focusable extends BaseFocusable<Props> {
@@ -1,25 +1,24 @@
1
1
  import * as React from "react";
2
2
 
3
- type Props =
4
- | {
5
- id: string;
6
- children: React.ReactNode;
7
- onPress?: (ref: FocusManager.TouchableRef) => void;
8
- onPressIn?: (ref: FocusManager.TouchableRef) => void;
9
- onPressOut?: (ref: FocusManager.TouchableRef) => void;
10
- onLongPress?: (ref: FocusManager.TouchableRef) => void;
11
- onFocus?: (
12
- ref: FocusManager.TouchableRef,
13
- options: FocusManager.Android.CallbackOptions
14
- ) => void;
15
- onBlur?: (
16
- ref: FocusManager.TouchableRef,
17
- options: FocusManager.Android.CallbackOptions
18
- ) => void;
19
-
20
- disableFocus?: boolean;
21
- blockFocus?: boolean;
22
- } & Partial<ParentFocus>;
3
+ type Props = {
4
+ id: string;
5
+ children: React.ReactNode;
6
+ onPress?: (ref: FocusManager.TouchableRef) => void;
7
+ onPressIn?: (ref: FocusManager.TouchableRef) => void;
8
+ onPressOut?: (ref: FocusManager.TouchableRef) => void;
9
+ onLongPress?: (ref: FocusManager.TouchableRef) => void;
10
+ onFocus?: (
11
+ ref: FocusManager.TouchableRef,
12
+ options: FocusManager.Android.CallbackOptions
13
+ ) => void;
14
+ onBlur?: (
15
+ ref: FocusManager.TouchableRef,
16
+ options: FocusManager.Android.CallbackOptions
17
+ ) => void;
18
+
19
+ disableFocus?: boolean;
20
+ blockFocus?: boolean;
21
+ } & Partial<ParentFocus>;
23
22
 
24
23
  export class Touchable extends React.Component<Props> {
25
24
  onPress(focusableRef: FocusManager.TouchableRef): void {
@@ -15,7 +15,6 @@ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
15
15
 
16
16
  import { useParentFocus, useCheckItemIdsForUnique } from "./hooks";
17
17
  import { FocusableListItemWrapper } from "./FocusableListItemWrapper";
18
- import { FocusableScrollView } from "../FocusableScrollView";
19
18
 
20
19
  const mapIndexed = R.addIndex(R.map);
21
20
 
@@ -55,7 +54,6 @@ export type Props<ItemT> = FlatListProps<ItemT> & {
55
54
  disableNextFocusUp?: boolean;
56
55
  disableNextFocusLeft?: boolean;
57
56
  disableNextFocusRight?: boolean;
58
- useScrollView?: boolean;
59
57
  } & ParentFocus;
60
58
 
61
59
  function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
@@ -90,7 +88,6 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
90
88
  data = [],
91
89
  // eslint-disable-next-line unused-imports/no-unused-vars
92
90
  omitPropsPropagation = [],
93
- useScrollView = false,
94
91
  } = props;
95
92
 
96
93
  useCheckItemIdsForUnique({ componentId: props.id, items: data });
@@ -276,7 +273,6 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
276
273
  "initialFocusDirection",
277
274
  "withStateMemory",
278
275
  "useSequentialLoading",
279
- "useScrollView",
280
276
  ...omitPropsPropagation,
281
277
  ],
282
278
  R.__
@@ -299,27 +295,15 @@ function FocusableListComponent<ItemT>(props: Props<ItemT>, ref) {
299
295
  return (
300
296
  // @ts-ignore
301
297
  <ChildrenFocusDeactivatorView flex={1}>
302
- {useScrollView ? (
303
- <FocusableScrollView
304
- ref={ref}
305
- {...getFlatListProps(props)}
306
- onEndReached={onEndReached}
307
- initialNumToRender={initialNumToRender}
308
- renderItem={renderItem}
309
- focused={focused}
310
- data={data}
311
- />
312
- ) : (
313
- <FlatList
314
- focusable={false}
315
- scrollEnabled={false}
316
- ref={ref}
317
- {...getFlatListProps(props)}
318
- renderItem={renderItem}
319
- onEndReached={onEndReached}
320
- initialNumToRender={initialNumToRender}
321
- />
322
- )}
298
+ <FlatList
299
+ focusable={false}
300
+ scrollEnabled={false}
301
+ ref={ref}
302
+ {...getFlatListProps(props)}
303
+ renderItem={renderItem}
304
+ onEndReached={onEndReached}
305
+ initialNumToRender={initialNumToRender}
306
+ />
323
307
  </ChildrenFocusDeactivatorView>
324
308
  );
325
309
  }
@@ -11,13 +11,6 @@ import {
11
11
  useInitialFocus,
12
12
  } from "@applicaster/zapp-react-native-utils/focusManager";
13
13
 
14
- export type FocusableScrollViewRenderItem = (info: {
15
- item: ZappUIComponent;
16
- index: number;
17
- focused: boolean;
18
- parentFocus: ParentFocus;
19
- }) => React.ReactElement;
20
-
21
14
  export type Props = ScrollViewProps & {
22
15
  id?: number | string;
23
16
  horizontal?: boolean;
@@ -39,27 +32,17 @@ export type Props = ScrollViewProps & {
39
32
  initialFocusDirection?: FocusManager.Android.FocusNavigationDirections;
40
33
  onAllComponentsLoaded?: () => void;
41
34
  omitPropsPropagation?: Array<keyof FlatListProps<any>>;
42
- renderItem: FocusableScrollViewRenderItem;
35
+ renderItem: (info: {
36
+ item: ZappUIComponent;
37
+ index: number;
38
+ focused: boolean;
39
+ parentFocus: ParentFocus;
40
+ }) => React.ReactElement;
43
41
  } & ParentFocus;
44
42
 
45
43
  type State = boolean[];
46
44
  type Action = { index?: number; isFocusable: boolean; type?: string };
47
45
 
48
- const unwrapArray = (maybeArray) =>
49
- Array.isArray(maybeArray) ? maybeArray[0] : maybeArray;
50
-
51
- function getEntry<T>(data: T | T[], index: unknown): T {
52
- let entry;
53
-
54
- if (R.isNil(index)) {
55
- entry = unwrapArray(data);
56
- } else if (typeof index === "number") {
57
- entry = unwrapArray(data[index]);
58
- }
59
-
60
- return entry;
61
- }
62
-
63
46
  const focusableStateReducer = (
64
47
  state: State,
65
48
  { index = null, isFocusable, type = null }: Action
@@ -90,7 +73,7 @@ const extendScrollViewRef = (_ref, childCompsMeasurementsMap) => {
90
73
  };
91
74
 
92
75
  _ref.scrollToIndex = (params) => {
93
- const { index, viewOffset = 0 } = params;
76
+ const { index, viewOffset } = params;
94
77
 
95
78
  const itemByIndex = R.find(R.propEq("index", index))(
96
79
  R.values(childCompsMeasurementsMap)
@@ -146,11 +129,8 @@ function FocusableScrollViewComponent(props: Props, ref) {
146
129
  );
147
130
 
148
131
  const getFocusableEntryId = React.useCallback(
149
- (_entry: ZappEntry | null, index?: number) => {
150
- const entry: { id?: string } = getEntry(
151
- R.isNil(index) ? _entry : data,
152
- index
153
- );
132
+ (_entry: ZappEntry, index?: number) => {
133
+ const entry = R.isNil(index) ? _entry : data[index];
154
134
 
155
135
  return entry ? `${props.id}--${entry?.id}` : undefined;
156
136
  },
@@ -293,18 +273,11 @@ function FocusableScrollViewComponent(props: Props, ref) {
293
273
  const renderItem = React.useCallback(
294
274
  (item, index) => {
295
275
  const renderArgs = { item, index };
296
-
297
- const id = getFocusableEntryId(
298
- Array.isArray(item) ? item[0] : item,
299
- index
300
- );
301
-
302
- const nextFocus = getNextFocus(index);
276
+ const id = `${props.id}--${renderArgs?.item?.id}`;
277
+ const nextFocus = getNextFocus(renderArgs?.index);
303
278
 
304
279
  const onItemLayout = (event) => {
305
- childCompsMeasurementsMap.current[
306
- Array.isArray(item) ? index : item.id
307
- ] = {
280
+ childCompsMeasurementsMap.current[item.id] = {
308
281
  index,
309
282
  layout: event.nativeEvent.layout,
310
283
  };
@@ -1,5 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { View, StyleSheet } from "react-native";
3
+ import { ScreenLayoutContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenLayoutContext";
4
+ import { ifEmptyUseFallback } from "@applicaster/zapp-react-native-utils/cellUtils";
3
5
  import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
4
6
  import { useScreenConfiguration } from "../../River/useScreenConfiguration";
5
7
 
@@ -10,15 +12,12 @@ type Props = {
10
12
  };
11
13
 
12
14
  const styles = StyleSheet.create({
13
- container: {
14
- position: "absolute",
15
- top: 0,
16
- zIndex: 10,
17
- width: 1920,
18
- },
15
+ container: { position: "absolute", top: 0, zIndex: 10, width: 1920 },
19
16
  });
20
17
 
21
18
  export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
19
+ const screenLayout = React.useContext(ScreenLayoutContext);
20
+ const screenMarginTop = screenLayout?.screenMarginTop || 0;
22
21
  const screen = useCurrentScreenData();
23
22
  const screenConfig = useScreenConfiguration(screen?.id);
24
23
 
@@ -27,9 +26,9 @@ export const NavBarContainer = ({ children, isVisible, onReady }: Props) => {
27
26
  // limits the height of the focusable container of the TopMenuBarTV component
28
27
  // to prevent it from being overlapped by the screen content,
29
28
  // as it makes TopMenuBarTV unfocusable on tvOS
30
- maxHeight: 1,
29
+ maxHeight: ifEmptyUseFallback(screenConfig?.marginTop, screenMarginTop),
31
30
  }),
32
- [screenConfig]
31
+ [screenLayout, screenConfig]
33
32
  );
34
33
 
35
34
  React.useEffect(() => {
@@ -0,0 +1,66 @@
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
+ import { View } from "react-native";
9
+
10
+ jest.mock("@applicaster/zapp-react-native-utils/numberUtils", () => ({
11
+ toNumberWithDefaultZero: jest.fn((value) => Number(value) || 0),
12
+ }));
13
+
14
+ describe("BorderContainerView", () => {
15
+ describe("getBorderPadding", () => {
16
+ it("returns 0 for inside", () => {
17
+ expect(getBorderPadding("inside", 10)).toBe(0);
18
+ });
19
+
20
+ it("returns borderWidth / 2 for center", () => {
21
+ expect(getBorderPadding("center", 10)).toBe(5);
22
+ });
23
+
24
+ it("returns borderWidth for outside", () => {
25
+ expect(getBorderPadding("outside", 10)).toBe(10);
26
+ });
27
+
28
+ it("returns borderWidth for invalid position", () => {
29
+ // @ts-ignore
30
+ expect(getBorderPadding("other_value", 10)).toBe(10);
31
+ });
32
+ });
33
+
34
+ it("Border component renders null if no borderPosition", () => {
35
+ const style = { borderWidth: 5, borderRadius: 10, borderColor: "red" };
36
+
37
+ const padding = {
38
+ paddingTop: 2,
39
+ paddingRight: 3,
40
+ paddingBottom: 4,
41
+ paddingLeft: 5,
42
+ };
43
+
44
+ const borderPosition = null;
45
+
46
+ const { queryByTestId } = render(
47
+ <BorderContainerView
48
+ style={style}
49
+ testID="border-container"
50
+ borderPosition={borderPosition}
51
+ borderPaddingTop={toNumberWithDefaultZero(padding.paddingTop)}
52
+ borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
53
+ borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
54
+ borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
55
+ >
56
+ <View testID="child" />
57
+ </BorderContainerView>
58
+ );
59
+
60
+ const children = queryByTestId("border-container").children;
61
+
62
+ expect(children[1].props.testID).toBe("child");
63
+
64
+ expect(children[0].children.length).toBe(0);
65
+ });
66
+ });
@@ -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,93 @@
1
+ import { ImageBorderContainer } from "../index";
2
+ import { getImageContainerMarginStyles } from "@applicaster/zapp-react-native-utils/cellUtils";
3
+
4
+ const mockValue = (key) => {
5
+ const values = {
6
+ cell_padding_top: 1,
7
+ cell_padding_left: 2,
8
+ cell_padding_right: 3,
9
+ cell_padding_bottom: 4,
10
+ image_border_size: 5,
11
+ image_focused_border_color: "red",
12
+ image_selected_border_color: "blue",
13
+ image_focused_selected_border_color: "green",
14
+ image_border_color: "black",
15
+ image_corner_radius: 10,
16
+ image_border_position: "outside",
17
+ image_border_padding_top: 6,
18
+ image_border_padding_right: 7,
19
+ image_border_padding_bottom: 8,
20
+ image_border_padding_left: 9,
21
+ image_margin_top: 11,
22
+ image_margin_left: 12,
23
+ image_margin_right: 13,
24
+ image_margin_bottom: 14,
25
+ };
26
+
27
+ return values[key] || 0;
28
+ };
29
+
30
+ describe("ImageBorderContainer", () => {
31
+ it("calculates style properties correctly", () => {
32
+ const marginStyles = getImageContainerMarginStyles({ value: mockValue });
33
+
34
+ expect(marginStyles.marginTop).toBe(11);
35
+ expect(marginStyles.marginLeft).toBe(12);
36
+ expect(marginStyles.marginRight).toBe(13);
37
+ expect(marginStyles.marginBottom).toBe(14);
38
+
39
+ const props = {
40
+ value: mockValue,
41
+ state: "default",
42
+ imageStyles: {},
43
+ };
44
+
45
+ const result = ImageBorderContainer(props);
46
+
47
+ expect(result.style.marginTop).toBe(12); // 11 + 1
48
+ expect(result.style.marginLeft).toBe(14); // 12 + 2
49
+ expect(result.style.marginRight).toBe(16); // 13 + 3
50
+ expect(result.style.marginBottom).toBe(18); // 14 + 4
51
+ expect(result.style.borderWidth).toBe(5);
52
+ expect(result.style.borderColor).toBe("black");
53
+ expect(result.style.borderRadius).toBe(10);
54
+ expect(result.additionalProps.borderPosition).toBe("outside");
55
+ expect(result.additionalProps.borderPaddingTop).toBe(6);
56
+ expect(result.additionalProps.borderPaddingRight).toBe(7);
57
+ expect(result.additionalProps.borderPaddingBottom).toBe(8);
58
+ expect(result.additionalProps.borderPaddingLeft).toBe(9);
59
+ });
60
+
61
+ it("handles focused state correctly", () => {
62
+ const props = {
63
+ value: mockValue,
64
+ state: "focused",
65
+ imageStyles: {},
66
+ };
67
+
68
+ const result = ImageBorderContainer(props);
69
+ expect(result.style.borderColor).toBe("red");
70
+ });
71
+
72
+ it("handles selected state correctly", () => {
73
+ const props = {
74
+ value: mockValue,
75
+ state: "selected",
76
+ imageStyles: {},
77
+ };
78
+
79
+ const result = ImageBorderContainer(props);
80
+ expect(result.style.borderColor).toBe("blue");
81
+ });
82
+
83
+ it("handles focused and selected state correctly", () => {
84
+ const props = {
85
+ value: mockValue,
86
+ state: "focused_selected",
87
+ imageStyles: {},
88
+ };
89
+
90
+ const result = ImageBorderContainer(props);
91
+ expect(result.style.borderColor).toBe("green");
92
+ });
93
+ });
@@ -17,7 +17,7 @@ const SECONDARY_IMAGE_PREFIX = "secondary_image";
17
17
 
18
18
  type ImageSizing = typeof IMAGE_SIZING_FIT | typeof IMAGE_SIZING_FILL;
19
19
 
20
- type ImagePosition = typeof IMAGE_POSITION[keyof typeof IMAGE_POSITION];
20
+ type ImagePosition = (typeof IMAGE_POSITION)[keyof typeof IMAGE_POSITION];
21
21
 
22
22
  export type DisplayMode =
23
23
  | typeof DISPLAY_MODE_FIXED
@@ -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
  });
@@ -103,7 +103,7 @@ export const useFilterChildren = <
103
103
  item: any;
104
104
  pluginIdentifier: string;
105
105
  };
106
- }
106
+ },
107
107
  >(
108
108
  children: T[]
109
109
  ): T[] => {
@@ -1,9 +1,4 @@
1
- import { compose } from "ramda";
2
1
  import { River as RiverComponent } from "./River";
3
2
  import { withTvEventHandler } from "./withTVEventHandler";
4
- import { withComponentsMapOffsetContext } from "../../../Contexts/ComponentsMapOffsetContext";
5
3
 
6
- export const River = compose(
7
- withTvEventHandler,
8
- withComponentsMapOffsetContext
9
- )(RiverComponent);
4
+ export const River = withTvEventHandler(RiverComponent);