@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1042942411 → 16.0.0-alpha.1054344748

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 (112) hide show
  1. package/Components/AudioPlayer/tv/__tests__/audioPlayer.test.js +19 -0
  2. package/Components/AudioPlayer/tv/__tests__/index.test.tsx +120 -0
  3. package/Components/AudioPlayer/tv/index.tsx +5 -1
  4. package/Components/Cell/Cell.tsx +3 -6
  5. package/Components/Cell/TvOSCellComponent.tsx +0 -1
  6. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
  7. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  8. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +64 -0
  9. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  10. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +18 -0
  11. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  12. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +64 -28
  13. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  14. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  15. package/Components/MasterCell/DefaultComponents/BorderContainerView/__tests__/index.test.tsx +46 -217
  16. package/Components/MasterCell/DefaultComponents/BorderContainerView/index.tsx +109 -46
  17. package/Components/MasterCell/DefaultComponents/Button.tsx +44 -50
  18. package/Components/MasterCell/DefaultComponents/CellBadge/index.tsx +14 -13
  19. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  20. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  21. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  22. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  23. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  24. package/Components/MasterCell/DefaultComponents/SecondaryImage/Image.tsx +50 -46
  25. package/Components/MasterCell/DefaultComponents/SecondaryImage/__tests__/__snapshots__/Image.test.tsx.snap +39 -47
  26. package/Components/MasterCell/DefaultComponents/SecondaryImage/utils.ts +6 -19
  27. package/Components/MasterCell/DefaultComponents/Text/index.tsx +15 -45
  28. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  29. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  30. package/Components/MasterCell/DefaultComponents/__tests__/text.test.tsx +67 -196
  31. package/Components/MasterCell/DefaultComponents/index.ts +0 -4
  32. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +6 -4
  33. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  34. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  35. package/Components/MasterCell/elementMapper.tsx +0 -2
  36. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  37. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  38. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  39. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  40. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  41. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +87 -97
  42. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +87 -126
  43. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +140 -83
  44. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +252 -123
  45. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  46. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  47. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  48. package/Components/ModalComponent/AudioPlayer/Components/index.ts +3 -3
  49. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  50. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  51. package/Components/ModalComponent/BottomSheetModalContent.tsx +498 -66
  52. package/Components/ModalComponent/Button/index.tsx +5 -3
  53. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  54. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  55. package/Components/ModalComponent/Header/index.tsx +20 -7
  56. package/Components/ModalComponent/Header/utils.ts +1 -1
  57. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  58. package/Components/ModalComponent/SortableList.tsx +156 -0
  59. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  60. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  61. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  62. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  63. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  64. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  65. package/Components/ModalComponent/index.tsx +18 -9
  66. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  67. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  68. package/Components/ModalComponent/presets/index.ts +27 -0
  69. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  70. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  71. package/Components/ModalComponent/presets/types.ts +35 -0
  72. package/Components/ModalComponent/showTextInput.ts +52 -0
  73. package/Components/ModalComponent/utils.ts +82 -21
  74. package/Components/OfflineHandler/__tests__/index.test.tsx +109 -0
  75. package/Components/OfflineHandler/hooks.ts +0 -12
  76. package/Components/OfflineHandler/index.tsx +42 -4
  77. package/Components/PlayerContainer/PlayerContainer.tsx +19 -8
  78. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  79. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +66 -13
  80. package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
  81. package/Components/River/River.tsx +4 -0
  82. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +138 -75
  83. package/Components/River/__tests__/componentsMap.test.js +8 -3
  84. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  85. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  86. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  87. package/Components/Screen/index.tsx +5 -0
  88. package/Components/Screen/navigationHandler.ts +8 -8
  89. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  90. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  91. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  92. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  93. package/Components/Transitioner/AnimationManager.js +1 -1
  94. package/Components/Transitioner/Scene.tsx +4 -3
  95. package/Components/Transitioner/Transitioner.tsx +71 -33
  96. package/Components/Transitioner/__tests__/transitioner.test.js +372 -3
  97. package/Components/VideoLive/LiveImageManager.ts +9 -3
  98. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  99. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  100. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  101. package/Hooks/useEntryActionsExpander.ts +63 -0
  102. package/assets/empty-Info.png +0 -0
  103. package/package.json +6 -5
  104. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/__snapshots__/index.test.tsx.snap +0 -18
  105. package/Components/MasterCell/DefaultComponents/CellBadge/__tests__/index.test.tsx +0 -90
  106. package/Components/MasterCell/DefaultComponents/UIKitAdapters/Badge.tsx +0 -49
  107. package/Components/MasterCell/DefaultComponents/__tests__/ActionButton.test.tsx +0 -122
  108. package/Components/MasterCell/DefaultComponents/__tests__/Button.test.tsx +0 -186
  109. package/Components/MasterCell/DefaultComponents/__tests__/DynamicBadge.test.tsx +0 -156
  110. package/Components/MasterCell/DefaultComponents/__tests__/__snapshots__/ActionButton.test.tsx.snap +0 -60
  111. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -332
  112. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -1,85 +1,26 @@
1
1
  import * as React from "react";
2
- import { render, waitFor } from "@testing-library/react-native";
3
- import { StyleSheet, View } from "react-native";
4
-
5
- import { BorderContainerView, getBorderPadding } from "../index";
6
- import { ImageBorderContainer } from "../../ImageBorderContainer";
7
- import { MeasurementPortalContext } from "../../../../MeasurmentsPortal/MeasurementsPortal";
8
-
9
- const mockAddHeading = jest.fn();
2
+ import {
3
+ BorderContainerView,
4
+ getBorderPadding, // Export for testing (using a double underscore prefix is a common convention)
5
+ } from "../index";
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
+ }));
10
13
 
11
14
  jest.mock(
12
15
  "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks",
13
16
  () => ({
14
17
  useAccessibilityManager: jest.fn(() => ({
15
- addHeading: mockAddHeading,
18
+ addHeading: jest.fn(),
16
19
  })),
17
20
  })
18
21
  );
19
22
 
20
- const createMockValue = () => {
21
- const values = {
22
- cell_padding_top: 1,
23
- cell_padding_left: 2,
24
- cell_padding_right: 3,
25
- cell_padding_bottom: 4,
26
- image_border_size: 5,
27
- image_focused_border_color: "red",
28
- image_selected_border_color: "blue",
29
- image_focused_selected_border_color: "green",
30
- image_border_color: "black",
31
- image_corner_radius: 10,
32
- image_border_position: "outside",
33
- image_border_padding_top: 6,
34
- image_border_padding_right: 7,
35
- image_border_padding_bottom: 8,
36
- image_border_padding_left: 9,
37
- image_margin_top: 11,
38
- image_margin_left: 12,
39
- image_margin_right: 13,
40
- image_margin_bottom: 14,
41
- };
42
-
43
- return (key) => values[key] || 0;
44
- };
45
-
46
- const renderBorderContainer = (
47
- overrideProps: Partial<any> = {},
48
- measurementValue: React.ContextType<typeof MeasurementPortalContext> = null
49
- ) => {
50
- const props = {
51
- style: {
52
- borderWidth: 4,
53
- borderColor: "red",
54
- borderRadius: 8,
55
- backgroundColor: "navy",
56
- },
57
- testID: "border-container",
58
- borderPosition: "outside" as const,
59
- borderPaddingTop: 1,
60
- borderPaddingRight: 2,
61
- borderPaddingBottom: 3,
62
- borderPaddingLeft: 4,
63
- hasFocusableInside: jest.fn(() => false),
64
- entry: { id: "test-entry", title: "Test title" } as ZappEntry,
65
- state: "focused" as CellState,
66
- hasTextLabels: false,
67
- children: <View testID="child" />,
68
- ...overrideProps,
69
- };
70
-
71
- return render(
72
- <MeasurementPortalContext.Provider value={measurementValue}>
73
- <BorderContainerView {...props} />
74
- </MeasurementPortalContext.Provider>
75
- );
76
- };
77
-
78
23
  describe("BorderContainerView", () => {
79
- beforeEach(() => {
80
- jest.clearAllMocks();
81
- });
82
-
83
24
  describe("getBorderPadding", () => {
84
25
  it("returns 0 for inside", () => {
85
26
  expect(getBorderPadding("inside", 10)).toBe(0);
@@ -94,159 +35,47 @@ describe("BorderContainerView", () => {
94
35
  });
95
36
 
96
37
  it("returns borderWidth for invalid position", () => {
97
- // @ts-ignore preserve current fallback semantics
38
+ // @ts-ignore
98
39
  expect(getBorderPadding("other_value", 10)).toBe(10);
99
40
  });
100
41
  });
101
42
 
102
- it("extracts explicit border props from flattened style while preserving root style", () => {
103
- const style = [
104
- { backgroundColor: "purple", shadowOpacity: 0.6 },
105
- { borderWidth: "6", borderColor: "orange", borderRadius: 12 },
106
- ];
107
-
108
- const { getByTestId } = renderBorderContainer({
109
- style,
110
- borderPaddingTop: 1,
111
- borderPaddingRight: 2,
112
- borderPaddingBottom: 3,
113
- borderPaddingLeft: 4,
114
- });
115
-
116
- const root = getByTestId("border-container");
117
- const overlay = root.children[0] as any;
118
- const overlayHost = overlay.children[0] as any;
119
- const child = root.children[1] as any;
120
-
121
- expect(child.props.testID).toBe("child");
122
-
123
- const flattenedRootStyle = StyleSheet.flatten(root.props.style);
124
- expect(flattenedRootStyle.backgroundColor).toBe("purple");
125
- expect(flattenedRootStyle.shadowOpacity).toBe(0.6);
126
- expect(flattenedRootStyle.borderRadius).toBe(12);
127
-
128
- expect(overlay.props.borderWidth).toBe(6);
129
- expect(overlay.props.borderColor).toBe("orange");
130
- expect(overlay.props.borderRadius).toBe(12);
131
-
132
- const flattenedOverlayStyle = StyleSheet.flatten(overlayHost.props.style);
133
- expect(flattenedOverlayStyle.borderWidth).toBe(6);
134
- expect(flattenedOverlayStyle.borderColor).toBe("orange");
135
- expect(flattenedOverlayStyle.top).toBe(-7);
136
- expect(flattenedOverlayStyle.right).toBe(-8);
137
- expect(flattenedOverlayStyle.bottom).toBe(-9);
138
- expect(flattenedOverlayStyle.left).toBe(-10);
139
- expect(flattenedOverlayStyle.borderTopLeftRadius).toBe(19);
140
- expect(flattenedOverlayStyle.borderTopRightRadius).toBe(19);
141
- expect(flattenedOverlayStyle.borderBottomLeftRadius).toBe(21);
142
- expect(flattenedOverlayStyle.borderBottomRightRadius).toBe(20);
143
- });
144
-
145
- it("renders no overlay when borderPosition is missing", () => {
146
- const { getByTestId } = renderBorderContainer({
147
- borderPosition: null,
148
- });
149
-
150
- const root = getByTestId("border-container");
151
-
152
- expect(root.children).toHaveLength(2);
153
- expect((root.children[0] as any).children).toHaveLength(0);
154
- expect((root.children[1] as any).props.testID).toBe("child");
155
- });
156
-
157
- it("preserves root pointerEvents defaults and caller overrides", () => {
158
- const defaultRender = renderBorderContainer();
159
-
160
- expect(
161
- defaultRender.getByTestId("border-container").props.pointerEvents
162
- ).toBe("box-none");
163
-
164
- const overrideRender = renderBorderContainer({
165
- pointerEvents: "none",
166
- testID: "override-border-container",
167
- });
168
-
169
- expect(
170
- overrideRender.getByTestId("override-border-container").props
171
- .pointerEvents
172
- ).toBe("none");
173
- });
174
-
175
- it("keeps the heading announcement logic in the QuickBrick wrapper", async () => {
176
- renderBorderContainer({
177
- entry: { id: "entry-1", title: "Hero title" } as ZappEntry,
178
- state: "focused",
179
- hasTextLabels: false,
180
- hasFocusableInside: jest.fn(() => false),
181
- });
182
-
183
- await waitFor(() => {
184
- expect(mockAddHeading).toHaveBeenCalledWith("Hero title");
185
- });
186
- });
187
-
188
- it("suppresses the heading announcement while measurement is in progress", async () => {
189
- renderBorderContainer(
190
- {
191
- entry: { id: "entry-1", title: "Hero title" } as ZappEntry,
192
- },
193
- { measuringInProgress: true }
43
+ it("Border component renders null if no borderPosition", () => {
44
+ const style = { borderWidth: 5, borderRadius: 10, borderColor: "red" };
45
+
46
+ const padding = {
47
+ paddingTop: 2,
48
+ paddingRight: 3,
49
+ paddingBottom: 4,
50
+ paddingLeft: 5,
51
+ };
52
+
53
+ const borderPosition = null;
54
+ const mockEntry = { id: "test-entry" } as ZappEntry;
55
+ const mockHasFocusableInside = jest.fn(() => false);
56
+
57
+ const { queryByTestId } = render(
58
+ <BorderContainerView
59
+ style={style}
60
+ testID="border-container"
61
+ borderPosition={borderPosition}
62
+ borderPaddingTop={toNumberWithDefaultZero(padding.paddingTop)}
63
+ borderPaddingRight={toNumberWithDefaultZero(padding.paddingRight)}
64
+ borderPaddingBottom={toNumberWithDefaultZero(padding.paddingBottom)}
65
+ borderPaddingLeft={toNumberWithDefaultZero(padding.paddingLeft)}
66
+ hasFocusableInside={mockHasFocusableInside}
67
+ entry={mockEntry}
68
+ state="focused"
69
+ hasTextLabels={false}
70
+ >
71
+ <View testID="child" />
72
+ </BorderContainerView>
194
73
  );
195
74
 
196
- await waitFor(() => {
197
- expect(mockAddHeading).not.toHaveBeenCalled();
198
- });
199
- });
200
-
201
- it("stays compatible with the ImageBorderContainer caller path", () => {
202
- const imageBorderConfig = ImageBorderContainer({
203
- value: createMockValue(),
204
- state: "default",
205
- imageStyles: {
206
- width: 100,
207
- height: 60,
208
- },
209
- });
210
-
211
- const { getByTestId } = renderBorderContainer({
212
- style: imageBorderConfig.style,
213
- pointerEvents: imageBorderConfig.additionalProps.pointerEvents,
214
- borderPosition: imageBorderConfig.additionalProps.borderPosition,
215
- borderPaddingTop: imageBorderConfig.additionalProps.borderPaddingTop,
216
- borderPaddingRight: imageBorderConfig.additionalProps.borderPaddingRight,
217
- borderPaddingBottom:
218
- imageBorderConfig.additionalProps.borderPaddingBottom,
219
- borderPaddingLeft: imageBorderConfig.additionalProps.borderPaddingLeft,
220
- children: <View testID="image-border-child" />,
221
- state: null,
222
- hasTextLabels: true,
223
- });
224
-
225
- const root = getByTestId("border-container");
226
- const overlay = root.children[0] as any;
227
- const overlayHost = overlay.children[0] as any;
228
- const child = root.children[1] as any;
229
-
230
- expect(root.props.pointerEvents).toBe("none");
231
- expect(child.props.testID).toBe("image-border-child");
232
-
233
- const flattenedRootStyle = StyleSheet.flatten(root.props.style);
234
- expect(flattenedRootStyle.borderRadius).toBe(10);
235
- expect(flattenedRootStyle.marginTop).toBe(12);
236
- expect(flattenedRootStyle.marginLeft).toBe(14);
237
- expect(flattenedRootStyle.marginRight).toBe(16);
238
- expect(flattenedRootStyle.marginBottom).toBe(18);
75
+ const children = queryByTestId("border-container").children;
239
76
 
240
- expect(overlay.props.borderWidth).toBe(5);
241
- expect(overlay.props.borderColor).toBe("black");
242
- expect(overlay.props.borderRadius).toBe(10);
77
+ expect(children[1].props.testID).toBe("child");
243
78
 
244
- const flattenedOverlayStyle = StyleSheet.flatten(overlayHost.props.style);
245
- expect(flattenedOverlayStyle.borderWidth).toBe(5);
246
- expect(flattenedOverlayStyle.borderColor).toBe("black");
247
- expect(flattenedOverlayStyle.borderTopLeftRadius).toBe(21);
248
- expect(flattenedOverlayStyle.borderTopRightRadius).toBe(21);
249
- expect(flattenedOverlayStyle.borderBottomLeftRadius).toBe(23);
250
- expect(flattenedOverlayStyle.borderBottomRightRadius).toBe(22);
79
+ expect(children[0].children.length).toBe(0);
251
80
  });
252
81
  });
@@ -1,27 +1,18 @@
1
1
  import React, { useMemo, useContext, useEffect } from "react";
2
- import {
3
- ImageStyle,
4
- StyleProp,
5
- StyleSheet,
6
- ViewProps,
7
- ViewStyle,
8
- } from "react-native";
9
- import {
10
- BorderContainer as UIKitBorderContainer,
11
- getBorderPadding,
12
- type BorderPosition,
13
- type UIKitBorderContainerProps,
14
- } from "@applicaster/uikit/lib/components/uikit/border-container";
2
+ import { ImageStyle, StyleSheet, View, ViewStyle } from "react-native";
15
3
  import { useAccessibilityManager } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
4
+ import { toNumberWithDefaultZero } from "@applicaster/zapp-react-native-utils/numberUtils";
16
5
  import { MeasurementPortalContext } from "../../../MeasurmentsPortal/MeasurementsPortal";
17
6
 
18
- interface Props extends ViewProps {
7
+ type BorderPosition = "inside" | "outside" | "center";
8
+
9
+ interface Props {
19
10
  hasFocusableInside: (entry: ZappEntry) => boolean;
20
11
  entry: ZappEntry;
21
12
  state: CellState;
22
13
  hasTextLabels: boolean;
23
- style: StyleProp<ImageStyle | ViewStyle>;
24
- borderPosition?: BorderPosition | null;
14
+ style: ImageStyle | ViewStyle;
15
+ borderPosition: BorderPosition;
25
16
  borderPaddingTop: number;
26
17
  borderPaddingRight: number;
27
18
  borderPaddingBottom: number;
@@ -29,23 +20,100 @@ interface Props extends ViewProps {
29
20
  children: React.ReactNode;
30
21
  }
31
22
 
32
- const toNumberOrZero = (value: unknown) => {
33
- const numeric = Number(value);
23
+ const COLOR_TRANSPARENT = "transparent";
24
+
25
+ const styles = StyleSheet.create({
26
+ container: {
27
+ overflow: "visible",
28
+ borderWidth: 0,
29
+ borderColor: COLOR_TRANSPARENT,
30
+ },
31
+ border: {
32
+ ...StyleSheet.absoluteFillObject,
33
+ zIndex: 1,
34
+ },
35
+ });
34
36
 
35
- return Number.isFinite(numeric) ? numeric : 0;
37
+ export const getBorderPadding = (
38
+ borderPosition: BorderPosition,
39
+ borderWidth: number
40
+ ) => {
41
+ switch (borderPosition) {
42
+ case "inside":
43
+ return 0;
44
+ case "center":
45
+ return borderWidth / 2;
46
+ case "outside":
47
+ return borderWidth;
48
+ default:
49
+ return borderWidth;
50
+ }
36
51
  };
37
52
 
38
- const getBorderPropsFromStyle = (style: StyleProp<ImageStyle | ViewStyle>) => {
39
- const flattenedStyle = StyleSheet.flatten(style) ?? {};
53
+ const Border = ({
54
+ style,
55
+ padding,
56
+ borderPosition,
57
+ }: {
58
+ style: ViewStyle;
59
+ padding: ViewStyle;
60
+ borderPosition: BorderPosition;
61
+ }) => {
62
+ if (!borderPosition) {
63
+ return null;
64
+ }
40
65
 
41
- return {
42
- borderWidth: toNumberOrZero(flattenedStyle.borderWidth),
43
- borderColor: flattenedStyle.borderColor,
44
- borderRadius: toNumberOrZero(flattenedStyle.borderRadius),
45
- };
46
- };
66
+ const borderWidth = toNumberWithDefaultZero(style.borderWidth);
67
+ const containerPadding = getBorderPadding(borderPosition, borderWidth);
68
+
69
+ const initialBorderRadius = toNumberWithDefaultZero(style.borderRadius);
70
+ const paddingTop = toNumberWithDefaultZero(padding.paddingTop);
71
+ const paddingRight = toNumberWithDefaultZero(padding.paddingRight);
72
+ const paddingBottom = toNumberWithDefaultZero(padding.paddingBottom);
73
+ const paddingLeft = toNumberWithDefaultZero(padding.paddingLeft);
74
+
75
+ const borderRadiusStyle = initialBorderRadius
76
+ ? {
77
+ borderTopLeftRadius:
78
+ initialBorderRadius +
79
+ containerPadding +
80
+ Math.min(paddingTop, paddingLeft),
81
+ borderTopRightRadius:
82
+ initialBorderRadius +
83
+ containerPadding +
84
+ Math.min(paddingTop, paddingRight),
85
+ borderBottomLeftRadius:
86
+ initialBorderRadius +
87
+ containerPadding +
88
+ Math.min(paddingBottom, paddingLeft),
89
+ borderBottomRightRadius:
90
+ initialBorderRadius +
91
+ containerPadding +
92
+ Math.min(paddingBottom, paddingRight),
93
+ }
94
+ : {
95
+ borderTopLeftRadius: 0,
96
+ borderTopRightRadius: 0,
97
+ borderBottomLeftRadius: 0,
98
+ borderBottomRightRadius: 0,
99
+ };
47
100
 
48
- export { getBorderPadding };
101
+ return (
102
+ <View
103
+ pointerEvents="none"
104
+ style={{
105
+ ...styles.border,
106
+ right: -containerPadding - paddingRight,
107
+ bottom: -containerPadding - paddingBottom,
108
+ left: -containerPadding - paddingLeft,
109
+ top: -containerPadding - paddingTop,
110
+ borderWidth,
111
+ borderColor: style.borderColor,
112
+ ...borderRadiusStyle,
113
+ }}
114
+ />
115
+ );
116
+ };
49
117
 
50
118
  export const BorderContainerView = (props: Props) => {
51
119
  const {
@@ -60,15 +128,11 @@ export const BorderContainerView = (props: Props) => {
60
128
  entry,
61
129
  state,
62
130
  hasTextLabels,
63
- ...viewProps
64
131
  } = props;
65
132
 
66
133
  const accessibilityManager = useAccessibilityManager();
67
134
  const isMeasurement = useContext(MeasurementPortalContext);
68
135
 
69
- const { borderWidth, borderColor, borderRadius } =
70
- getBorderPropsFromStyle(style);
71
-
72
136
  const isImageOnlyCell = useMemo(
73
137
  () =>
74
138
  state === "focused" &&
@@ -84,21 +148,20 @@ export const BorderContainerView = (props: Props) => {
84
148
  }
85
149
  }, [isImageOnlyCell, entry?.title]);
86
150
 
151
+ const padding =
152
+ borderPosition === "outside"
153
+ ? {
154
+ paddingTop: borderPaddingTop,
155
+ paddingRight: borderPaddingRight,
156
+ paddingBottom: borderPaddingBottom,
157
+ paddingLeft: borderPaddingLeft,
158
+ }
159
+ : {};
160
+
87
161
  return (
88
- <UIKitBorderContainer
89
- testID="border-container-view"
90
- {...viewProps}
91
- style={style as UIKitBorderContainerProps["style"]}
92
- borderPosition={borderPosition}
93
- borderWidth={borderWidth}
94
- borderColor={borderColor}
95
- borderRadius={borderRadius}
96
- borderPaddingTop={borderPaddingTop}
97
- borderPaddingRight={borderPaddingRight}
98
- borderPaddingBottom={borderPaddingBottom}
99
- borderPaddingLeft={borderPaddingLeft}
100
- >
162
+ <View pointerEvents="box-none" {...props} style={[style, styles.container]}>
163
+ <Border style={style} padding={padding} borderPosition={borderPosition} />
101
164
  {children}
102
- </UIKitBorderContainer>
165
+ </View>
103
166
  );
104
167
  };
@@ -1,9 +1,10 @@
1
1
  import * as React from "react";
2
2
  import * as R from "ramda";
3
3
 
4
- import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
4
+ import { TouchableOpacity } from "react-native";
5
5
 
6
- import { Button as UIKitButton } from "@applicaster/uikit/lib/components/uikit/button";
6
+ import { connectToStore } from "@applicaster/zapp-react-native-redux/utils/connectToStore";
7
+ import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
7
8
 
8
9
  import Image from "./Image";
9
10
  import { masterCellLogger } from "../logger";
@@ -21,43 +22,50 @@ type Props = {
21
22
  style: {};
22
23
  type: "Image" | "Svg" | "Lottie";
23
24
  };
25
+ initiallyActive: boolean;
24
26
  action: {
25
- src?: Record<string, string | undefined>;
26
27
  identifier: string;
27
28
  type: string;
28
29
  };
29
30
  style: {};
31
+ plugins: [];
30
32
  testID?: string;
31
33
  accessibilityLabel?: string;
34
+ cellUUID?: string;
32
35
  };
33
36
 
34
37
  const components = {
35
38
  Image,
36
39
  };
37
40
 
38
- export function Button(props: Props) {
39
- const { asset, item, action, style, testID, accessibilityLabel } = props;
41
+ function ButtonComponent(props: Props) {
42
+ const asset = props?.asset;
43
+ const item = props?.item;
44
+ const action = props?.action;
40
45
 
41
46
  const assetType = asset?.type || "Image";
42
47
 
43
48
  const ButtonAsset = components[assetType];
44
49
 
45
- type ActionContext =
46
- | {
47
- masterCell?: {
48
- isSelected?: (entry: ZappEntry) => boolean;
49
- };
50
- state?: ZappEntry[];
51
- removeFavourite?: (item: ZappEntry) => Promise<void>;
52
- addFavourite?: (item: ZappEntry) => Promise<void>;
53
- invokeAction?: (item: ZappEntry) => Promise<void>;
54
- initialEntryState?: (...args: unknown[]) => unknown;
55
- }
56
- | undefined;
50
+ const actionContext = useActions(action?.identifier);
51
+
52
+ // TODO: better handling of missing plugin
53
+ if (!actionContext) {
54
+ // eslint-disable-next-line no-console
55
+ masterCellLogger.warning(
56
+ `You're missing an action plugin(${action?.identifier}) required by your button. \n Button can't be displayed`
57
+ );
57
58
 
58
- const actionContext: ActionContext = useActions(action?.identifier);
59
+ return null;
60
+ }
61
+
62
+ // if the action context implements the initialEntryState function, it
63
+ // means it uses the new Action Button API.
64
+ if (typeof actionContext?.initialEntryState === "function") {
65
+ return <ActionButton {...props} />;
66
+ }
59
67
 
60
- const defaultIsSelected = R.includes(item, actionContext?.state || []);
68
+ const defaultIsSelected = R.includes(item, actionContext.state || []);
61
69
 
62
70
  const isSelected = actionContext?.masterCell?.isSelected
63
71
  ? actionContext?.masterCell?.isSelected(item)
@@ -67,45 +75,31 @@ export function Button(props: Props) {
67
75
  const assetSrc = isSelected ? asset?.src?.active : asset?.src?.inactive;
68
76
 
69
77
  const favouritesAction = isSelected
70
- ? actionContext?.removeFavourite
71
- : actionContext?.addFavourite;
78
+ ? actionContext.removeFavourite
79
+ : actionContext.addFavourite;
72
80
 
73
81
  const toggleAction = actionContext?.invokeAction ?? favouritesAction;
74
82
 
75
- const onPress = React.useCallback(async () => {
76
- try {
77
- await toggleAction(item);
78
- } catch (error) {
79
- masterCellLogger.error(
80
- `Error invoking action for button with item ${item?.id}`,
81
- error
82
- );
83
- }
84
- }, [item, toggleAction]);
85
-
86
- if (!actionContext) {
87
- masterCellLogger.warning(
88
- `You're missing an action plugin(${action?.identifier}) required by your button. \n Button can't be displayed`
89
- );
90
-
91
- return null;
92
- }
93
-
94
- // Action context is the preferred API when the plugin exposes initialEntryState (new Action Button API).
95
- if (typeof actionContext?.initialEntryState === "function") {
96
- return <ActionButton {...props} />;
83
+ async function onPress() {
84
+ // TODO: loading state
85
+ await toggleAction(item);
97
86
  }
98
87
 
99
- // Legacy button rendering with image inside is the default path for action contexts without initialEntryState.
100
88
  return (
101
- <UIKitButton
102
- id={`${item?.id}`}
103
- style={style}
104
- testID={testID}
105
- accessibilityLabel={accessibilityLabel}
89
+ <TouchableOpacity
90
+ activeOpacity={1}
106
91
  onPress={onPress}
92
+ testID={props?.testID || `${item?.id}`}
93
+ accessibilityLabel={props?.accessibilityLabel || `${item?.id}`}
94
+ accessibilityRole="button"
95
+ accessible={!!(props?.testID || props?.accessibilityLabel)}
96
+ style={props?.style}
107
97
  >
108
98
  <ButtonAsset style={asset?.style} uri={assetSrc} {...asset?.props} />
109
- </UIKitButton>
99
+ </TouchableOpacity>
110
100
  );
111
101
  }
102
+
103
+ export const Button = R.compose(connectToStore(R.pick(["plugins"])))(
104
+ ButtonComponent
105
+ );
@@ -4,7 +4,10 @@ import {
4
4
  ifElseSelected,
5
5
  } from "@applicaster/zapp-react-native-utils/cellUtils";
6
6
  import { platformSelect } from "@applicaster/zapp-react-native-utils/reactUtils";
7
- const UIKitBadge = "UIKitBadge";
7
+
8
+ import { getPositionStyles } from "./utils";
9
+
10
+ const View = "View";
8
11
  const Image = "Image";
9
12
 
10
13
  type Props = {
@@ -27,24 +30,22 @@ export const CellBadge = ({
27
30
  const position = value("cell_badge_position");
28
31
 
29
32
  return {
30
- type: UIKitBadge, // Cell badge view
33
+ type: View, // Cell badge view
31
34
  style: {
32
35
  flex: platformSelect({
33
36
  tvos: 0,
34
37
  android_tv: 0,
35
38
  default: 1,
36
39
  }),
37
- },
38
- additionalProps: {
39
- position,
40
- width: value("cell_badge_width"),
41
- height: value("cell_badge_height"),
42
- margins: {
43
- marginTop: value("cell_badge_margin_top"),
44
- marginRight: value("cell_badge_margin_right"),
45
- marginBottom: value("cell_badge_margin_bottom"),
46
- marginLeft: value("cell_badge_margin_left"),
47
- },
40
+ position: "absolute",
41
+ ...getPositionStyles(position, {
42
+ width: value("cell_badge_width"),
43
+ height: value("cell_badge_height"),
44
+ }),
45
+ marginTop: value("cell_badge_margin_top"),
46
+ marginRight: value("cell_badge_margin_right"),
47
+ marginBottom: value("cell_badge_margin_bottom"),
48
+ marginLeft: value("cell_badge_margin_left"),
48
49
  },
49
50
  elements: compact([
50
51
  {