@applicaster/zapp-react-native-ui-components 16.0.0-rc.12 → 16.0.0-rc.120

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 (166) 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/CellRendererResolver/index.ts +1 -1
  7. package/Components/FocusableGroup/FocusableTvOS.tsx +11 -7
  8. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +54 -0
  9. package/Components/GeneralContentScreen/__tests__/GeneralContentScreenHookAdapter.test.tsx +64 -0
  10. package/Components/GeneralContentScreen/__tests__/HookContentFocusGroup.web.test.tsx +91 -0
  11. package/Components/GeneralContentScreen/hookAdapter/__tests__/networkService.test.ts +74 -0
  12. package/Components/GeneralContentScreen/hookAdapter/__tests__/runInBackground.test.ts +139 -0
  13. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  14. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +188 -0
  15. package/Components/GeneralContentScreen/hookAdapter/logger.ts +6 -0
  16. package/Components/GeneralContentScreen/hookAdapter/networkService.ts +53 -0
  17. package/Components/GeneralContentScreen/hookAdapter/runInBackground.ts +48 -0
  18. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  19. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +90 -0
  20. package/Components/GeneralContentScreen/hookFocus/index.tsx +13 -0
  21. package/Components/GeneralContentScreen/hookFocus/index.web.tsx +69 -0
  22. package/Components/GeneralContentScreen/index.ts +2 -0
  23. package/Components/Layout/FullWidthRow.tsx +38 -0
  24. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  25. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  26. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  27. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  28. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  29. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  30. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  31. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  32. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  33. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  34. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  35. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  36. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +14 -6
  37. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  38. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  39. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  40. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  41. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  42. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  43. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  44. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  45. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  46. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +34 -21
  47. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  48. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  49. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  50. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  51. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  52. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  53. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  54. package/Components/MasterCell/__tests__/masterCell.test.js +60 -0
  55. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  56. package/Components/MasterCell/dataAdapter.ts +15 -5
  57. package/Components/MasterCell/hooks/index.ts +2 -0
  58. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  59. package/Components/MasterCell/index.tsx +57 -8
  60. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  61. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  62. package/Components/MasterCell/utils/__tests__/withoutFocusedState.test.js +20 -0
  63. package/Components/MasterCell/utils/index.ts +103 -46
  64. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  65. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  66. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  67. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  68. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  69. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  70. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  71. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  72. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1028 -0
  73. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  74. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +164 -0
  75. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  76. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  77. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +433 -0
  78. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +563 -0
  79. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  80. package/Components/ModalComponent/BottomSheetModalContent.tsx +440 -50
  81. package/Components/ModalComponent/Button/index.tsx +5 -3
  82. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  83. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  84. package/Components/ModalComponent/Header/index.tsx +20 -7
  85. package/Components/ModalComponent/Header/utils.ts +1 -1
  86. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  87. package/Components/ModalComponent/SortableList.tsx +156 -0
  88. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  89. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  90. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  91. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  92. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  93. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  94. package/Components/ModalComponent/__tests__/showTextInput.test.ts +120 -0
  95. package/Components/ModalComponent/index.tsx +18 -9
  96. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  97. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  98. package/Components/ModalComponent/presets/index.ts +27 -0
  99. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  100. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  101. package/Components/ModalComponent/presets/types.ts +35 -0
  102. package/Components/ModalComponent/showTextInput.ts +54 -0
  103. package/Components/ModalComponent/utils.ts +82 -21
  104. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  105. package/Components/OfflineHandler/__tests__/index.test.tsx +186 -17
  106. package/Components/OfflineHandler/hooks.ts +111 -0
  107. package/Components/OfflineHandler/index.tsx +91 -75
  108. package/Components/OfflineHandler/utils/index.ts +1 -13
  109. package/Components/PlayerContainer/PlayerContainer.tsx +28 -11
  110. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  111. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +337 -0
  112. package/Components/PreloaderWrapper/index.tsx +1 -1
  113. package/Components/River/ComponentsMap/ComponentsMap.tsx +91 -62
  114. package/Components/River/River.tsx +4 -0
  115. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +142 -89
  116. package/Components/River/__tests__/componentsMap.test.js +8 -3
  117. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  118. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  119. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  120. package/Components/Screen/index.tsx +13 -4
  121. package/Components/Screen/navigationHandler.ts +8 -8
  122. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  123. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  124. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  125. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  126. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  127. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  128. package/Components/TopCutoffOverlay/index.tsx +2 -2
  129. package/Components/Transitioner/AnimationManager.js +1 -1
  130. package/Components/Transitioner/Scene.tsx +4 -3
  131. package/Components/Transitioner/Transitioner.tsx +98 -49
  132. package/Components/Transitioner/__tests__/transitioner.test.js +372 -3
  133. package/Components/VideoLive/LiveImageManager.ts +9 -3
  134. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  135. package/Components/VideoModal/PlayerWrapper.tsx +4 -1
  136. package/Components/VideoModal/__tests__/PlayerWrapper.test.tsx +1 -0
  137. package/Components/VideoModal/__tests__/playerWrapperStyle.test.ts +58 -0
  138. package/Components/VideoModal/__tests__/playerWrapperUtils.test.ts +16 -0
  139. package/Components/VideoModal/playerWrapperStyle.ts +21 -3
  140. package/Components/VideoModal/playerWrapperUtils.ts +7 -1
  141. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  142. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  143. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  144. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  145. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  146. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  147. package/Decorators/ZappPipesDataConnector/__tests__/screenToolbox.test.ts +347 -0
  148. package/Decorators/ZappPipesDataConnector/__tests__/screenToolboxUnavailable.test.ts +53 -0
  149. package/Decorators/ZappPipesDataConnector/index.tsx +19 -4
  150. package/Decorators/ZappPipesDataConnector/screenToolbox.ts +384 -0
  151. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  152. package/Helpers/index.js +0 -39
  153. package/Hooks/useEntryActionsExpander.ts +63 -0
  154. package/assets/empty-Info.png +0 -0
  155. package/package.json +6 -5
  156. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  157. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  158. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  159. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  160. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  161. package/Components/Screen/utils.ts +0 -16
  162. package/Helpers/Analytics/index.js +0 -95
  163. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  164. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  165. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  166. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -0,0 +1,433 @@
1
+ import {
2
+ mergeItemConfiguration,
3
+ mergeButtonConfiguration,
4
+ mergeHeaderConfiguration,
5
+ } from "../mergeStyles";
6
+ import {
7
+ VARIANT_SECTION_HEADER,
8
+ VARIANT_SINGLE_LINE_HEADER,
9
+ } from "../../Components/Header";
10
+
11
+ const base = {
12
+ background: {
13
+ defaultColor: "transparent",
14
+ focusedColor: "rgba(62, 62, 62, 1)",
15
+ cornerRadius: 12,
16
+ },
17
+ image: {
18
+ width: 48,
19
+ height: 48,
20
+ borderWidth: 1,
21
+ borderColor: "rgba(255, 255, 255, 0.1)",
22
+ cornerRadius: 6,
23
+ },
24
+ nowPlayingAsset: {},
25
+ nowPlayingLabel: { fontColor: "#fff", fontSize: 12 },
26
+ textLabel1: { fontColor: "#fff", fontSize: 15 },
27
+ textLabel2: { fontColor: "#999", fontSize: 13 },
28
+ };
29
+
30
+ describe("mergeItemConfiguration background", () => {
31
+ it("applies Studio focus background to focusedColor", () => {
32
+ const merged = mergeItemConfiguration(base as any, {
33
+ background: {
34
+ default: { backgroundColor: "transparent" },
35
+ focused: { backgroundColor: "#FF3B30" },
36
+ },
37
+ });
38
+
39
+ expect(merged.background.defaultColor).toBe("transparent");
40
+ expect(merged.background.focusedColor).toBe("#FF3B30");
41
+ });
42
+
43
+ it("accepts focus alias bucket from parser output", () => {
44
+ const merged = mergeItemConfiguration(base as any, {
45
+ background: {
46
+ default: { backgroundColor: "transparent" },
47
+ focus: { backgroundColor: "#00FF66" },
48
+ },
49
+ });
50
+
51
+ expect(merged.background.focusedColor).toBe("#00FF66");
52
+ });
53
+
54
+ it("merges text label lineHeight from Studio keys", () => {
55
+ const merged = mergeItemConfiguration(base as any, {
56
+ textLabel1: {
57
+ default: { fontSize: 28, lineHeight: 40 },
58
+ },
59
+ });
60
+
61
+ expect(merged.textLabel1.fontSize).toBe(28);
62
+ expect(merged.textLabel1.lineHeight).toBe(40);
63
+ });
64
+
65
+ it("does not merge Studio item background padding (white spec field)", () => {
66
+ const merged = mergeItemConfiguration(base as any, {
67
+ background: {
68
+ default: {
69
+ backgroundColor: "#2E7D32",
70
+ paddingTop: 32,
71
+ paddingBottom: 32,
72
+ paddingLeft: 40,
73
+ paddingRight: 40,
74
+ },
75
+ },
76
+ });
77
+
78
+ expect(merged.background.defaultColor).toBe("#2E7D32");
79
+ expect(merged.background.paddingTop).toBeUndefined();
80
+ expect(merged.background.paddingBottom).toBeUndefined();
81
+ expect(merged.background.paddingLeft).toBeUndefined();
82
+ expect(merged.background.paddingRight).toBeUndefined();
83
+ expect(merged.background.focusedColor).toBe("rgba(62, 62, 62, 1)");
84
+ });
85
+
86
+ it("preserves base focusedColor when only default background is set", () => {
87
+ const merged = mergeItemConfiguration(base as any, {
88
+ background: {
89
+ default: { backgroundColor: "#2E7D32" },
90
+ },
91
+ });
92
+
93
+ expect(merged.background.defaultColor).toBe("#2E7D32");
94
+ expect(merged.background.focusedColor).toBe("rgba(62, 62, 62, 1)");
95
+ });
96
+
97
+ it("applies Studio checkbox assets onto checkboxAssets", () => {
98
+ const merged = mergeItemConfiguration(base as any, {
99
+ checkbox: {
100
+ default: {
101
+ asset: "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=CB",
102
+ checkedAsset: "https://placehold.co/24x24/FFD60A/000000/png?text=CK",
103
+ },
104
+ },
105
+ });
106
+
107
+ expect(merged.checkboxAssets?.asset).toBe(
108
+ "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=CB"
109
+ );
110
+
111
+ expect(merged.checkboxAssets?.checkedAsset).toBe(
112
+ "https://placehold.co/24x24/FFD60A/000000/png?text=CK"
113
+ );
114
+ });
115
+
116
+ it("applies Studio reorder/remove assets onto actionButtonAssets", () => {
117
+ const merged = mergeItemConfiguration(base as any, {
118
+ actionButtons: {
119
+ default: {
120
+ removeAsset: "https://placehold.co/24x24/00FF00/000000/png?text=RM",
121
+ dragAsset: "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
122
+ },
123
+ },
124
+ });
125
+
126
+ expect(merged.actionButtonAssets?.removeAsset).toBe(
127
+ "https://placehold.co/24x24/00FF00/000000/png?text=RM"
128
+ );
129
+
130
+ expect(merged.actionButtonAssets?.dragAsset).toBe(
131
+ "https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH"
132
+ );
133
+ });
134
+
135
+ it("applies a text label font family and ignores a non-string font family", () => {
136
+ const withFont = mergeItemConfiguration(base as any, {
137
+ textLabel1: { default: { fontFamily: "Roboto-Bold" } },
138
+ });
139
+
140
+ expect(withFont.textLabel1.iosFontFamily).toBe("Roboto-Bold");
141
+ expect(withFont.textLabel1.androidFontFamily).toBe("Roboto-Bold");
142
+
143
+ const withEmpty = mergeItemConfiguration(base as any, {
144
+ textLabel1: {
145
+ default: { fontFamily: {}, iosFontFamily: "SFProText-Semibold" },
146
+ },
147
+ });
148
+
149
+ expect(withEmpty.textLabel1.iosFontFamily).toBe("SFProText-Semibold");
150
+ expect(withEmpty.textLabel1.androidFontFamily).toBeUndefined();
151
+ });
152
+
153
+ it("applies play and pause assets onto actionButtonAssets", () => {
154
+ const merged = mergeItemConfiguration(base as any, {
155
+ actionButtons: {
156
+ default: {
157
+ playAsset: "https://example.com/play.png",
158
+ pauseAsset: "https://example.com/pause.png",
159
+ },
160
+ },
161
+ });
162
+
163
+ expect(merged.actionButtonAssets?.playAsset).toBe(
164
+ "https://example.com/play.png"
165
+ );
166
+
167
+ expect(merged.actionButtonAssets?.pauseAsset).toBe(
168
+ "https://example.com/pause.png"
169
+ );
170
+ });
171
+ });
172
+
173
+ describe("mergeButtonConfiguration background", () => {
174
+ const buttonBase = {
175
+ background: {
176
+ defaultColor: "#FFFFFF",
177
+ focusedColor: "#E0E0E0",
178
+ inactiveColor: "#CCCCCC",
179
+ cornerRadius: 12,
180
+ paddingTop: 12,
181
+ paddingRight: 12,
182
+ paddingBottom: 12,
183
+ paddingLeft: 12,
184
+ horizontalGutter: 8,
185
+ },
186
+ title: {
187
+ fontColor: "#000000",
188
+ focusedFontColor: "#000000",
189
+ inactiveFontColor: "#666666",
190
+ iosFontFamily: "SFProText-Semibold",
191
+ androidFontFamily: "Roboto-Medium",
192
+ fontSize: 15,
193
+ lineHeight: 24,
194
+ iosLetterSpacing: 0,
195
+ androidLetterSpacing: 0,
196
+ textTransform: "default" as const,
197
+ },
198
+ icons: {
199
+ width: 24,
200
+ height: 24,
201
+ leadingIcon: { enabled: true },
202
+ },
203
+ };
204
+
205
+ it("applies Studio button focus background to focusedColor", () => {
206
+ const merged = mergeButtonConfiguration(buttonBase as any, {
207
+ background: {
208
+ default: { backgroundColor: "#FFFFFF" },
209
+ focused: { backgroundColor: "#00FF00" },
210
+ },
211
+ });
212
+
213
+ expect(merged.background.defaultColor).toBe("#FFFFFF");
214
+ expect(merged.background.focusedColor).toBe("#00FF00");
215
+ });
216
+
217
+ it("applies Studio button title lineHeight", () => {
218
+ const merged = mergeButtonConfiguration(buttonBase as any, {
219
+ title: {
220
+ default: { fontColor: "#000000", fontSize: 15, lineHeight: 40 },
221
+ },
222
+ });
223
+
224
+ expect(merged.title.lineHeight).toBe(40);
225
+ expect(merged.title.fontSize).toBe(15);
226
+ });
227
+
228
+ it("applies Studio button background padding", () => {
229
+ const merged = mergeButtonConfiguration(buttonBase as any, {
230
+ background: {
231
+ default: {
232
+ backgroundColor: "#FFFFFF",
233
+ paddingTop: 32,
234
+ paddingRight: 20,
235
+ paddingBottom: 8,
236
+ paddingLeft: 4,
237
+ },
238
+ },
239
+ });
240
+
241
+ expect(merged.background.paddingTop).toBe(32);
242
+ expect(merged.background.paddingRight).toBe(20);
243
+ expect(merged.background.paddingBottom).toBe(8);
244
+ expect(merged.background.paddingLeft).toBe(4);
245
+ expect(merged.background.focusedColor).toBe("#E0E0E0");
246
+ });
247
+
248
+ it("preserves base button focusedColor when only default background is set", () => {
249
+ const merged = mergeButtonConfiguration(buttonBase as any, {
250
+ background: {
251
+ default: { backgroundColor: "#ABCDEF" },
252
+ },
253
+ });
254
+
255
+ expect(merged.background.defaultColor).toBe("#ABCDEF");
256
+ expect(merged.background.focusedColor).toBe("#E0E0E0");
257
+ });
258
+
259
+ it("applies Studio create-new leading icon asset", () => {
260
+ const merged = mergeButtonConfiguration(buttonBase as any, {
261
+ leadingIcon: {
262
+ default: {
263
+ asset: "https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN",
264
+ },
265
+ },
266
+ });
267
+
268
+ expect(merged.icons.leadingIcon.asset).toBe(
269
+ "https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN"
270
+ );
271
+ });
272
+
273
+ it("applies Studio button title typography (fontFamily, letterSpacing, textTransform)", () => {
274
+ const merged = mergeButtonConfiguration(buttonBase as any, {
275
+ title: {
276
+ default: {
277
+ fontFamily: "CustomFont",
278
+ letterSpacing: -0.5,
279
+ textTransform: "uppercase",
280
+ },
281
+ },
282
+ });
283
+
284
+ expect(merged.title.iosFontFamily).toBe("CustomFont");
285
+ expect(merged.title.androidFontFamily).toBe("CustomFont");
286
+ expect(merged.title.iosLetterSpacing).toBe(-0.5);
287
+ expect(merged.title.androidLetterSpacing).toBe(-0.5);
288
+ expect(merged.title.textTransform).toBe("uppercase");
289
+ });
290
+
291
+ it("uses the family string inside a font-selector object and ignores {}", () => {
292
+ const fromObject = mergeButtonConfiguration(buttonBase as any, {
293
+ title: {
294
+ default: { fontFamily: { fontFamily: "GreycliffCF-Bold" } },
295
+ },
296
+ });
297
+
298
+ expect(fromObject.title.iosFontFamily).toBe("GreycliffCF-Bold");
299
+ expect(fromObject.title.androidFontFamily).toBe("GreycliffCF-Bold");
300
+
301
+ const fromEmpty = mergeButtonConfiguration(buttonBase as any, {
302
+ title: {
303
+ default: { fontFamily: {} },
304
+ },
305
+ });
306
+
307
+ expect(fromEmpty.title.iosFontFamily).toBe(buttonBase.title.iosFontFamily);
308
+
309
+ expect(fromEmpty.title.androidFontFamily).toBe(
310
+ buttonBase.title.androidFontFamily
311
+ );
312
+ });
313
+
314
+ it("maps inactive color, gutter, and focused and inactive font colors", () => {
315
+ const merged = mergeButtonConfiguration(buttonBase as any, {
316
+ background: {
317
+ default: { inactiveColor: "#999999", horizontalGutter: 6 },
318
+ },
319
+ title: {
320
+ default: { inactiveFontColor: "#111111" },
321
+ focused: { fontColor: "#222222" },
322
+ },
323
+ });
324
+
325
+ expect(merged.background.inactiveColor).toBe("#999999");
326
+ expect(merged.background.horizontalGutter).toBe(6);
327
+ expect(merged.title.inactiveFontColor).toBe("#111111");
328
+ expect(merged.title.focusedFontColor).toBe("#222222");
329
+ });
330
+ });
331
+
332
+ describe("mergeHeaderConfiguration section header (Variant 3)", () => {
333
+ it("merges section title + Clear action button styles", () => {
334
+ const merged = mergeHeaderConfiguration(VARIANT_SECTION_HEADER, {
335
+ title: {
336
+ default: {
337
+ fontColor: "#ABCDEF",
338
+ fontSize: 15,
339
+ lineHeight: 36,
340
+ fontFamily: "CustomSection",
341
+ letterSpacing: -0.2,
342
+ textTransform: "uppercase",
343
+ alignment: "left",
344
+ },
345
+ },
346
+ actionButtons: {
347
+ default: {
348
+ backgroundColor: "rgba(10, 10, 10, 1)",
349
+ fontColor: "#EEEEEE",
350
+ fontSize: 12,
351
+ borderRadius: 20,
352
+ paddingTop: 8,
353
+ paddingRight: 12,
354
+ paddingBottom: 8,
355
+ paddingLeft: 12,
356
+ },
357
+ focused: {
358
+ backgroundColor: "rgba(20, 20, 20, 1)",
359
+ fontColor: "#FFDD00",
360
+ },
361
+ },
362
+ });
363
+
364
+ expect(merged.title.fontColor).toBe("#ABCDEF");
365
+ expect(merged.title.fontSize).toBe(15);
366
+ expect(merged.title.lineHeight).toBe(36);
367
+ expect(merged.title.iosFontFamily).toBe("CustomSection");
368
+ expect(merged.title.androidFontFamily).toBe("CustomSection");
369
+ expect(merged.title.iosLetterSpacing).toBe(-0.2);
370
+ expect(merged.title.textTransform).toBe("uppercase");
371
+
372
+ expect(merged.actionButtons.defaultBackgroundColor).toBe(
373
+ "rgba(10, 10, 10, 1)"
374
+ );
375
+
376
+ expect(merged.actionButtons.focusedBackgroundColor).toBe(
377
+ "rgba(20, 20, 20, 1)"
378
+ );
379
+
380
+ expect(merged.actionButtons.fontColor).toBe("#EEEEEE");
381
+ expect(merged.actionButtons.focusedFontColor).toBe("#FFDD00");
382
+ expect(merged.actionButtons.fontSize).toBe(12);
383
+ expect(merged.actionButtons.cornerRadius).toBe(20);
384
+ expect(merged.actionButtons.paddingTop).toBe(8);
385
+ expect(merged.actionButtons.paddingLeft).toBe(12);
386
+ });
387
+
388
+ it("uses title.default when only platform font and letter spacing are set", () => {
389
+ const merged = mergeHeaderConfiguration(VARIANT_SECTION_HEADER, {
390
+ title: {
391
+ default: {
392
+ iosFontFamily: "SFProText-Semibold",
393
+ androidFontFamily: "Roboto-Medium",
394
+ iosLetterSpacing: -0.4,
395
+ androidLetterSpacing: 0,
396
+ },
397
+ },
398
+ });
399
+
400
+ expect(merged.title.iosFontFamily).toBe("SFProText-Semibold");
401
+ expect(merged.title.androidFontFamily).toBe("Roboto-Medium");
402
+ expect(merged.title.iosLetterSpacing).toBe(-0.4);
403
+ expect(merged.title.androidLetterSpacing).toBe(0);
404
+ expect(merged.title.fontSize).toBe(VARIANT_SECTION_HEADER.title.fontSize);
405
+ });
406
+
407
+ it("does not change section defaults when block is empty", () => {
408
+ const merged = mergeHeaderConfiguration(VARIANT_SECTION_HEADER, {
409
+ title: { default: {} },
410
+ actionButtons: { default: {} },
411
+ });
412
+
413
+ expect(merged.title.fontSize).toBe(VARIANT_SECTION_HEADER.title.fontSize);
414
+
415
+ expect(merged.actionButtons.defaultBackgroundColor).toBe(
416
+ VARIANT_SECTION_HEADER.actionButtons.defaultBackgroundColor
417
+ );
418
+ });
419
+
420
+ it("leaves single-line header title size untouched when only actionButtons set", () => {
421
+ const merged = mergeHeaderConfiguration(VARIANT_SINGLE_LINE_HEADER, {
422
+ actionButtons: {
423
+ default: { backgroundColor: "#123456" },
424
+ },
425
+ });
426
+
427
+ expect(merged.title.fontSize).toBe(
428
+ VARIANT_SINGLE_LINE_HEADER.title.fontSize
429
+ );
430
+
431
+ expect(merged.actionButtons.defaultBackgroundColor).toBe("#123456");
432
+ });
433
+ });