@applicaster/zapp-react-native-utils 16.0.0-alpha.1593888345 → 16.0.0-alpha.1733304298
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.
- package/actionUtils/index.ts +3 -1
- package/actionsExecutor/ActionExecutor.ts +10 -1
- package/actionsExecutor/ActionExecutorContext.tsx +3 -0
- package/actionsExecutor/__tests__/ActionExecutor.test.ts +71 -0
- package/actionsExecutor/__tests__/StorageActions.test.ts +98 -0
- package/actionsExecutor/__tests__/feedDecorator.test.ts +30 -0
- package/actionsExecutor/__tests__/screenToggleFlag.test.ts +90 -0
- package/actionsExecutor/actions/__tests__/navigateToScreen.test.ts +139 -5
- package/actionsExecutor/actions/__tests__/openBottomSheet.customContent.test.ts +24 -0
- package/actionsExecutor/actions/__tests__/openBottomSheet.inlineItems.test.ts +160 -0
- package/actionsExecutor/actions/__tests__/showAlert.test.ts +49 -0
- package/actionsExecutor/actions/__tests__/showToast.test.ts +2 -2
- package/actionsExecutor/actions/index.ts +2 -0
- package/actionsExecutor/actions/navigateToScreen.ts +29 -45
- package/actionsExecutor/actions/openBottomSheet.ts +21 -6
- package/actionsExecutor/actions/screenToggleFlag.ts +1 -3
- package/actionsExecutor/actions/showAlert.ts +51 -0
- package/actionsExecutor/actions/showToast.ts +2 -2
- package/actionsExecutor/consts.ts +1 -0
- package/actionsExecutor/feedDecorator.ts +6 -0
- package/analyticsUtils/__tests__/playerAnalyticsSafeguard.test.ts +62 -0
- package/analyticsUtils/playerAnalyticsTracker.ts +1 -0
- package/appUtils/contextKeysManager/__tests__/getKey/failure.test.ts +1 -22
- package/appUtils/contextKeysManager/__tests__/getKey/success/fullSearch.test.ts +1 -25
- package/appUtils/contextKeysManager/__tests__/getKey/success/searchInSecure.test.ts +1 -7
- package/appUtils/contextKeysManager/__tests__/removeKey/failure.test.ts +0 -25
- package/appUtils/contextKeysManager/__tests__/removeKey/success.test.ts +0 -6
- package/appUtils/contextKeysManager/__tests__/setKey/failure/failedToSaveInStorage.test.ts +0 -40
- package/appUtils/contextKeysManager/__tests__/setKey/{success/saveItemAndReference.test.ts → success.test.ts} +2 -42
- package/appUtils/contextKeysManager/index.ts +28 -129
- package/appUtils/playerManager/OverlayObserver/OverlaysObserver.ts +4 -11
- package/appUtils/playerManager/OverlayObserver/__tests__/getUpcomingQueue.test.ts +67 -0
- package/appUtils/playerManager/OverlayObserver/__tests__/utils.test.ts +9 -2
- package/appUtils/playerManager/OverlayObserver/getUpcomingQueue.ts +15 -2
- package/appUtils/playerManager/OverlayObserver/utils.ts +2 -5
- package/appUtils/playerManager/__tests__/playerDiagnostics.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerNative.test.ts +76 -0
- package/appUtils/playerManager/__tests__/playerNativeCommand.test.ts +62 -0
- package/appUtils/playerManager/__tests__/playerToolbox.test.ts +281 -0
- package/appUtils/playerManager/__tests__/usePlayerControllerSetup.test.tsx +62 -0
- package/appUtils/playerManager/const.ts +13 -0
- package/appUtils/playerManager/index.ts +7 -0
- package/appUtils/playerManager/player.ts +25 -0
- package/appUtils/playerManager/playerNative.ts +22 -0
- package/appUtils/playerManager/playerNativeCommand.ts +3 -6
- package/appUtils/playerManager/playerToolbox.ts +196 -0
- package/appUtils/playerManager/usePlayerControllerSetup.tsx +6 -0
- package/arrayUtils/__tests__/reorderByIds.test.ts +13 -0
- package/arrayUtils/index.ts +26 -43
- package/cellUtils/__tests__/getButtonsEnabled.test.ts +28 -0
- package/cellUtils/__tests__/getCellNavigationEnabled.test.ts +36 -0
- package/cellUtils/__tests__/getIsCellNavigationEnabled.test.ts +35 -0
- package/cellUtils/__tests__/isCellNavigationEnabled.test.ts +21 -0
- package/cellUtils/index.ts +40 -0
- package/configurationUtils/__tests__/modalBlocksParser.test.ts +189 -46
- package/configurationUtils/modalBlocksParser.ts +57 -101
- package/entryActions/__tests__/buildEntryActions.test.ts +97 -18
- package/entryActions/aliasPresentation.ts +26 -42
- package/entryActions/buildEntryActions.ts +1 -0
- package/entryActions/index.ts +0 -2
- package/focusManager/FocusManager.ts +26 -0
- package/focusManager/__tests__/FocusManager.test.ts +9 -0
- package/localizationUtils/index.ts +12 -6
- package/logger/__tests__/xrayToolbox.test.ts +69 -0
- package/logger/xrayToolbox.ts +67 -0
- package/manifestUtils/defaultManifestConfigurations/player.js +8 -0
- package/manifestUtils/keys.js +7 -2
- package/manifestUtils/mobileAction/container/__tests__/mobileActionButtonsContainer.test.js +21 -0
- package/manifestUtils/mobileAction/groups/__tests__/buildMobileActionButtonGroups.test.js +1 -1
- package/manifestUtils/mobileAction/groups/defaults.js +2 -1
- package/modalState/ModalOrchestrator.ts +2 -0
- package/modalState/RemoteEditableCollection.ts +9 -1
- package/modalState/__tests__/ModalOrchestrator.phase3.test.ts +39 -0
- package/modalState/__tests__/RemoteEditableCollection.test.ts +90 -1
- package/modalState/__tests__/useModalStoreState.test.ts +142 -0
- package/modalState/index.ts +2 -0
- package/modalState/store.ts +9 -2
- package/modalState/types.ts +14 -0
- package/modalState/useBottomSheetContent.ts +17 -12
- package/navigationUtils/xrayNavigator.ts +12 -0
- package/package.json +3 -2
- package/pluginUtils/__tests__/pluginUtils.test.js +32 -0
- package/pluginUtils/index.ts +29 -0
- package/quickBrickAssets/__tests__/index.test.ts +18 -0
- package/quickBrickAssets/index.ts +35 -0
- package/reactHooks/__tests__/getPluginConfiguration.test.ts +82 -0
- package/reactHooks/actions/index.ts +29 -23
- package/reactHooks/cell-click/__tests__/index.test.js +64 -0
- package/reactHooks/cell-click/index.ts +21 -14
- package/reactHooks/connection/index.tsx +8 -5
- package/reactHooks/feed/__tests__/feedInvalidation.test.tsx +338 -0
- package/reactHooks/feed/__tests__/useBatchLoading.test.tsx +8 -2
- package/reactHooks/feed/__tests__/useMarkPipesDataStale.test.tsx +243 -0
- package/reactHooks/feed/useFeedLoader.tsx +28 -1
- package/reactHooks/feed/useLoadPipesDataDispatch.ts +12 -11
- package/reactHooks/feed/useMarkPipesDataStale.ts +68 -23
- package/reactHooks/navigation/__tests__/index.test.tsx +128 -1
- package/reactHooks/navigation/useRoute.ts +8 -4
- package/reactHooks/usePluginConfiguration.ts +84 -46
- package/riverComponetsMeasurementProvider/index.tsx +3 -1
- package/uiActionsRegistrator/__tests__/subscribe.test.ts +26 -0
- package/uiActionsRegistrator/registry.ts +16 -0
- package/appUtils/contextKeysManager/__tests__/getKey/success/byReference.test.ts +0 -56
- package/appUtils/contextKeysManager/__tests__/getKeyByReference/failure.test.ts +0 -265
- package/appUtils/contextKeysManager/__tests__/getKeyByReference/success.test.ts +0 -303
- package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/failure.test.ts +0 -82
- package/appUtils/contextKeysManager/__tests__/removeReferenceForKey/success.test.ts +0 -41
- package/appUtils/contextKeysManager/__tests__/setKey/success/saveItemButNotReference.test.ts +0 -208
- package/appUtils/contextKeysManager/__tests__/setReferenceForKey/failure.test.ts +0 -93
- package/appUtils/contextKeysManager/__tests__/setReferenceForKey/success.test.ts +0 -45
package/cellUtils/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
|
12
12
|
import { isNotNil } from "@applicaster/zapp-react-native-utils/reactUtils/helpers";
|
|
13
13
|
|
|
14
14
|
import { toNumberWithDefault, toNumberWithDefaultZero } from "../numberUtils";
|
|
15
|
+
import { isTrue } from "../booleanUtils";
|
|
15
16
|
|
|
16
17
|
const CUSTOM_KEY = "other";
|
|
17
18
|
|
|
@@ -572,3 +573,42 @@ export const hasTextLabelsEnabled = (
|
|
|
572
573
|
return Boolean(value);
|
|
573
574
|
});
|
|
574
575
|
};
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Reads the mobile buttons container "buttons enabled" switch from cell styles.
|
|
579
|
+
*/
|
|
580
|
+
export const getButtonsEnabled = (styles?: Record<string, any>): boolean => {
|
|
581
|
+
return isTrue(styles?.mobile_buttons_container_buttons_enabled);
|
|
582
|
+
};
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Reads the mobile buttons container "cell navigation enabled" switch from cell styles.
|
|
586
|
+
*/
|
|
587
|
+
export const getCellNavigationEnabled = (
|
|
588
|
+
styles?: Record<string, any>
|
|
589
|
+
): boolean => {
|
|
590
|
+
return isTrue(styles?.mobile_buttons_container_cell_navigation_enabled);
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Whether tapping a cell outside its action buttons triggers the cell's navigation.
|
|
595
|
+
* Always true when the buttons are disabled.
|
|
596
|
+
* @param buttonsEnabled the cell's "buttons enabled" switch value
|
|
597
|
+
* @param cellNavigationEnabled the cell's "cell navigation enabled" switch value
|
|
598
|
+
*/
|
|
599
|
+
export const isCellNavigationEnabled = (
|
|
600
|
+
buttonsEnabled: unknown,
|
|
601
|
+
cellNavigationEnabled: unknown
|
|
602
|
+
): boolean => !isTrue(buttonsEnabled) || isTrue(cellNavigationEnabled);
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Whether tapping a mobile cell outside its action buttons triggers the cell's navigation,
|
|
606
|
+
* read from the cell styles.
|
|
607
|
+
*/
|
|
608
|
+
export const getIsCellNavigationEnabled = (
|
|
609
|
+
styles?: Record<string, any>
|
|
610
|
+
): boolean =>
|
|
611
|
+
isCellNavigationEnabled(
|
|
612
|
+
getButtonsEnabled(styles),
|
|
613
|
+
getCellNavigationEnabled(styles)
|
|
614
|
+
);
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
getEmptyModalBlocksStructure,
|
|
3
|
+
parseModalBlocksConfiguration,
|
|
4
|
+
} from "../modalBlocksParser";
|
|
2
5
|
|
|
3
6
|
// Mock the platform selection logic so it's consistent
|
|
4
7
|
jest.mock("../../reactUtils", () => ({
|
|
@@ -151,110 +154,250 @@ describe("parseModalBlocksConfiguration", () => {
|
|
|
151
154
|
);
|
|
152
155
|
});
|
|
153
156
|
|
|
154
|
-
it("should map
|
|
157
|
+
it("should ignore keys that do not map to a known block sub-component", () => {
|
|
155
158
|
const result = parseModalBlocksConfiguration(
|
|
156
159
|
{
|
|
157
160
|
"button.create_new_playlist_asset":
|
|
158
161
|
"https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN",
|
|
162
|
+
"item.checkbox_asset":
|
|
163
|
+
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=CB",
|
|
164
|
+
"reorder_playlist_item.reorder_handle_asset":
|
|
165
|
+
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
|
|
159
166
|
},
|
|
160
167
|
"playlist-experience"
|
|
161
168
|
);
|
|
162
169
|
|
|
163
|
-
expect(result.button.leadingIcon.default).
|
|
170
|
+
expect(result.button.leadingIcon.default?.asset).toBeUndefined();
|
|
171
|
+
expect(result.item.checkbox.default).toBeUndefined();
|
|
172
|
+
expect(result.item.actionButtons.default).toBeUndefined();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("parses the generic button leading icon asset", () => {
|
|
176
|
+
const result = parseModalBlocksConfiguration(
|
|
177
|
+
{ "button.leading_icon.asset": "https://example.com/add.png" },
|
|
178
|
+
"playlist-experience"
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
expect(result.button.leadingIcon.default?.asset).toBe(
|
|
182
|
+
"https://example.com/add.png"
|
|
183
|
+
);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("getEmptyModalBlocksStructure", () => {
|
|
188
|
+
it("should return every block with empty containers and no state entries", () => {
|
|
189
|
+
const result = getEmptyModalBlocksStructure();
|
|
190
|
+
|
|
191
|
+
expect(result.header.container).toEqual({});
|
|
192
|
+
expect(result.item.background).toEqual({});
|
|
193
|
+
expect(result.button.background).toEqual({});
|
|
194
|
+
expect(result.action.background).toEqual({});
|
|
195
|
+
|
|
196
|
+
expect(result.button.leadingIcon.default).toBeUndefined();
|
|
197
|
+
expect(result.item.checkbox.default).toBeUndefined();
|
|
198
|
+
expect(result.item.actionButtons.default).toBeUndefined();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("should return a fresh object on every call", () => {
|
|
202
|
+
const first = getEmptyModalBlocksStructure();
|
|
203
|
+
const second = getEmptyModalBlocksStructure();
|
|
204
|
+
|
|
205
|
+
expect(first).not.toBe(second);
|
|
206
|
+
expect(first.header).not.toBe(second.header);
|
|
207
|
+
expect(first).toEqual(second);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("should be what the parser returns when config or componentName is missing", () => {
|
|
211
|
+
expect(
|
|
212
|
+
parseModalBlocksConfiguration(undefined as any, "queue_action")
|
|
213
|
+
).toEqual(getEmptyModalBlocksStructure());
|
|
214
|
+
|
|
215
|
+
expect(parseModalBlocksConfiguration({ foo: "bar" }, "")).toEqual(
|
|
216
|
+
getEmptyModalBlocksStructure()
|
|
217
|
+
);
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("should parse section_header title and action_buttons independently of header", () => {
|
|
221
|
+
const result = parseModalBlocksConfiguration(
|
|
222
|
+
{
|
|
223
|
+
"section_header.title.font_color": "#ABCDEF",
|
|
224
|
+
"section_header.title.font_size": 13,
|
|
225
|
+
"section_header.title.ios.font_family": "SFProText-Bold",
|
|
226
|
+
"section_header.action_buttons.default.background_color":
|
|
227
|
+
"rgba(62, 62, 62, 1)",
|
|
228
|
+
"section_header.action_buttons.focus.background_color":
|
|
229
|
+
"rgba(78, 78, 78, 1)",
|
|
230
|
+
"section_header.action_buttons.default.font_color": "#FFFFFF",
|
|
231
|
+
"section_header.action_buttons.font_size": 14,
|
|
232
|
+
"section_header.action_buttons.border_radius": 16,
|
|
233
|
+
// Theme modal header — must not leak into sectionHeader
|
|
234
|
+
queue_action_style_header_title_default_fontColor: "#111111",
|
|
235
|
+
queue_action_style_header_title_default_fontSize: 22,
|
|
236
|
+
},
|
|
237
|
+
"queue_action"
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
expect(result.sectionHeader.title.default).toEqual(
|
|
241
|
+
expect.objectContaining({
|
|
242
|
+
fontColor: "#ABCDEF",
|
|
243
|
+
fontSize: 13,
|
|
244
|
+
fontFamily: "SFProText-Bold",
|
|
245
|
+
})
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
expect(result.sectionHeader.actionButtons.default).toEqual(
|
|
249
|
+
expect.objectContaining({
|
|
250
|
+
backgroundColor: "rgba(62, 62, 62, 1)",
|
|
251
|
+
fontColor: "#FFFFFF",
|
|
252
|
+
fontSize: 14,
|
|
253
|
+
borderRadius: 16,
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
expect(result.sectionHeader.actionButtons.focused).toEqual(
|
|
164
258
|
expect.objectContaining({
|
|
165
|
-
|
|
259
|
+
backgroundColor: "rgba(78, 78, 78, 1)",
|
|
260
|
+
})
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
expect(result.header.title.default).toEqual(
|
|
264
|
+
expect.objectContaining({
|
|
265
|
+
fontColor: "#111111",
|
|
266
|
+
fontSize: 22,
|
|
166
267
|
})
|
|
167
268
|
);
|
|
168
269
|
});
|
|
169
270
|
|
|
170
|
-
it("
|
|
271
|
+
it("maps text label and now playing font keys onto the label style", () => {
|
|
171
272
|
const result = parseModalBlocksConfiguration(
|
|
172
273
|
{
|
|
173
|
-
"item.
|
|
174
|
-
|
|
175
|
-
"item.
|
|
176
|
-
|
|
274
|
+
"item.text_label1.ios.font_family": "SFProText-Bold",
|
|
275
|
+
"item.text_label1.letter_spacing": -0.4,
|
|
276
|
+
"item.text_label1.text_transform": "uppercase",
|
|
277
|
+
"now_playing_item.now_playing_label.ios.font_family":
|
|
278
|
+
"SFProText-Semibold",
|
|
279
|
+
"now_playing_item.now_playing_label.font_size": 18,
|
|
177
280
|
},
|
|
178
|
-
"
|
|
281
|
+
"queue-action"
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
expect(result.item.textLabel1.default).toEqual(
|
|
285
|
+
expect.objectContaining({
|
|
286
|
+
fontFamily: "SFProText-Bold",
|
|
287
|
+
letterSpacing: -0.4,
|
|
288
|
+
textTransform: "uppercase",
|
|
289
|
+
})
|
|
179
290
|
);
|
|
180
291
|
|
|
181
|
-
expect(result.
|
|
292
|
+
expect(result.nowPlayingItem?.nowPlayingLabel.default).toEqual(
|
|
182
293
|
expect.objectContaining({
|
|
183
|
-
|
|
184
|
-
|
|
294
|
+
fontFamily: "SFProText-Semibold",
|
|
295
|
+
fontSize: 18,
|
|
185
296
|
})
|
|
186
297
|
);
|
|
187
298
|
});
|
|
188
299
|
|
|
189
|
-
it("
|
|
300
|
+
it("maps queue remove and reorder assets onto item action buttons", () => {
|
|
190
301
|
const result = parseModalBlocksConfiguration(
|
|
191
302
|
{
|
|
192
|
-
"
|
|
193
|
-
|
|
194
|
-
"reorder_playlist_item.reorder_handle_asset":
|
|
195
|
-
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
|
|
303
|
+
"item.remove_asset": "https://example.com/remove.png",
|
|
304
|
+
"item.reorder_handle_asset": "https://example.com/reorder.png",
|
|
196
305
|
},
|
|
197
|
-
"
|
|
306
|
+
"queue-action"
|
|
198
307
|
);
|
|
199
308
|
|
|
200
309
|
expect(result.item.actionButtons.default).toEqual(
|
|
201
310
|
expect.objectContaining({
|
|
202
|
-
removeAsset: "https://
|
|
203
|
-
dragAsset: "https://
|
|
311
|
+
removeAsset: "https://example.com/remove.png",
|
|
312
|
+
dragAsset: "https://example.com/reorder.png",
|
|
204
313
|
})
|
|
205
314
|
);
|
|
206
315
|
});
|
|
207
316
|
|
|
208
|
-
it("
|
|
317
|
+
it("maps now playing play and pause assets onto action buttons", () => {
|
|
209
318
|
const result = parseModalBlocksConfiguration(
|
|
210
319
|
{
|
|
211
|
-
"
|
|
212
|
-
|
|
213
|
-
"playlist_experience_assets_flavor1.reorder_handle_asset":
|
|
214
|
-
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
|
|
320
|
+
"now_playing_item.play_asset": "https://example.com/play.png",
|
|
321
|
+
"now_playing_item.pause_asset": "https://example.com/pause.png",
|
|
215
322
|
},
|
|
216
|
-
"
|
|
323
|
+
"queue-action"
|
|
217
324
|
);
|
|
218
325
|
|
|
219
|
-
expect(result.
|
|
326
|
+
expect(result.nowPlayingItem?.actionButtons.default).toEqual(
|
|
220
327
|
expect.objectContaining({
|
|
221
|
-
|
|
222
|
-
|
|
328
|
+
playAsset: "https://example.com/play.png",
|
|
329
|
+
pauseAsset: "https://example.com/pause.png",
|
|
223
330
|
})
|
|
224
331
|
);
|
|
225
332
|
});
|
|
226
333
|
|
|
227
|
-
it("should
|
|
334
|
+
it("should leave sectionHeader empty when keys are absent", () => {
|
|
335
|
+
const result = parseModalBlocksConfiguration(
|
|
336
|
+
{ "item.text_label1.font_color": "#FFFFFF" },
|
|
337
|
+
"playlist-experience"
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
expect(result.sectionHeader.title).toEqual({ default: {} });
|
|
341
|
+
expect(result.sectionHeader.actionButtons).toEqual({ default: {} });
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it("maps playlist button font family and the new green style keys", () => {
|
|
228
345
|
const result = parseModalBlocksConfiguration(
|
|
229
346
|
{
|
|
230
|
-
"
|
|
231
|
-
|
|
232
|
-
"
|
|
233
|
-
|
|
347
|
+
"button.title.ios.font_family": "GreycliffCF-Bold",
|
|
348
|
+
"button.title.inactive_font_color": "#111111",
|
|
349
|
+
"button.title.focused_font_color": "#222222",
|
|
350
|
+
"button.title.text_transform": "uppercase",
|
|
351
|
+
"button.background.inactive_color": "#999999",
|
|
352
|
+
"button.background.horizontal_gutter": 6,
|
|
353
|
+
"button.icons.width": 24,
|
|
354
|
+
"button.icons.height": 24,
|
|
234
355
|
},
|
|
235
356
|
"playlist-experience"
|
|
236
357
|
);
|
|
237
358
|
|
|
238
|
-
expect(result.
|
|
359
|
+
expect(result.button.title.default).toEqual(
|
|
360
|
+
expect.objectContaining({
|
|
361
|
+
fontFamily: "GreycliffCF-Bold",
|
|
362
|
+
inactiveFontColor: "#111111",
|
|
363
|
+
textTransform: "uppercase",
|
|
364
|
+
})
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
// "focused_" is a state prefix, so this key lands on the focused bucket.
|
|
368
|
+
expect(result.button.title.focused).toEqual(
|
|
369
|
+
expect.objectContaining({
|
|
370
|
+
fontColor: "#222222",
|
|
371
|
+
})
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
expect(result.button.background.default).toEqual(
|
|
375
|
+
expect.objectContaining({
|
|
376
|
+
inactiveColor: "#999999",
|
|
377
|
+
horizontalGutter: 6,
|
|
378
|
+
})
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
expect(result.button.icons.default).toEqual(
|
|
239
382
|
expect.objectContaining({
|
|
240
|
-
|
|
241
|
-
|
|
383
|
+
width: 24,
|
|
384
|
+
height: 24,
|
|
242
385
|
})
|
|
243
386
|
);
|
|
244
387
|
});
|
|
245
388
|
|
|
246
|
-
it("
|
|
389
|
+
it("keeps a font-selector object on the button title", () => {
|
|
247
390
|
const result = parseModalBlocksConfiguration(
|
|
248
391
|
{
|
|
249
|
-
"button.
|
|
250
|
-
"https://placehold.co/24x24/FF00FF/FFFFFF/png?text=CN",
|
|
251
|
-
"playlist_experience_assets_flavor1.reorder_handle_asset":
|
|
252
|
-
"https://placehold.co/24x24/FF3B30/FFFFFF/png?text=RH",
|
|
392
|
+
"button.title.ios.font_family": { fontFamily: "GreycliffCF-Bold" },
|
|
253
393
|
},
|
|
254
|
-
"
|
|
394
|
+
"playlist-experience"
|
|
255
395
|
);
|
|
256
396
|
|
|
257
|
-
expect(result.button.
|
|
258
|
-
|
|
397
|
+
expect(result.button.title.default).toEqual(
|
|
398
|
+
expect.objectContaining({
|
|
399
|
+
fontFamily: { fontFamily: "GreycliffCF-Bold" },
|
|
400
|
+
})
|
|
401
|
+
);
|
|
259
402
|
});
|
|
260
403
|
});
|
|
@@ -7,6 +7,14 @@ export type ModalBlocksConfig = {
|
|
|
7
7
|
closeButton: Record<string, any>;
|
|
8
8
|
actionButtons: Record<string, any>;
|
|
9
9
|
};
|
|
10
|
+
/**
|
|
11
|
+
* Queue Section Header (BB Variant 3) — shared by Next in Queue / Next Up.
|
|
12
|
+
* Separate from Theme modal `header` so top-title styles do not collide.
|
|
13
|
+
*/
|
|
14
|
+
sectionHeader: {
|
|
15
|
+
title: Record<string, any>;
|
|
16
|
+
actionButtons: Record<string, any>;
|
|
17
|
+
};
|
|
10
18
|
action: {
|
|
11
19
|
background: Record<string, any>;
|
|
12
20
|
title: Record<string, any>;
|
|
@@ -52,10 +60,7 @@ export type ModalBlocksConfig = {
|
|
|
52
60
|
};
|
|
53
61
|
};
|
|
54
62
|
|
|
55
|
-
export const
|
|
56
|
-
configuration: Record<string, any>,
|
|
57
|
-
componentName: string
|
|
58
|
-
): ModalBlocksConfig => {
|
|
63
|
+
export const getEmptyModalBlocksStructure = (): ModalBlocksConfig => {
|
|
59
64
|
const result: ModalBlocksConfig = {
|
|
60
65
|
header: {
|
|
61
66
|
container: {},
|
|
@@ -63,6 +68,10 @@ export const parseModalBlocksConfiguration = (
|
|
|
63
68
|
closeButton: {},
|
|
64
69
|
actionButtons: {},
|
|
65
70
|
},
|
|
71
|
+
sectionHeader: {
|
|
72
|
+
title: {},
|
|
73
|
+
actionButtons: {},
|
|
74
|
+
},
|
|
66
75
|
action: {
|
|
67
76
|
background: {},
|
|
68
77
|
title: {},
|
|
@@ -106,6 +115,15 @@ export const parseModalBlocksConfiguration = (
|
|
|
106
115
|
},
|
|
107
116
|
};
|
|
108
117
|
|
|
118
|
+
return result;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const parseModalBlocksConfiguration = (
|
|
122
|
+
configuration: Record<string, any>,
|
|
123
|
+
componentName: string
|
|
124
|
+
): ModalBlocksConfig => {
|
|
125
|
+
const result = getEmptyModalBlocksStructure();
|
|
126
|
+
|
|
109
127
|
if (!configuration || !componentName) {
|
|
110
128
|
return result;
|
|
111
129
|
}
|
|
@@ -148,6 +166,8 @@ export const parseModalBlocksConfiguration = (
|
|
|
148
166
|
"action_buttons",
|
|
149
167
|
]);
|
|
150
168
|
|
|
169
|
+
parseBlock("section_header", ["title", "action_buttons"]);
|
|
170
|
+
|
|
151
171
|
parseBlock("action", [
|
|
152
172
|
"background",
|
|
153
173
|
"title",
|
|
@@ -190,107 +210,43 @@ export const parseModalBlocksConfiguration = (
|
|
|
190
210
|
|
|
191
211
|
parseBlock("button", ["background", "title", "icons", "leading_icon"]);
|
|
192
212
|
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
result.button.leadingIcon.default.asset = createNewPlaylistAsset;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Studio checkbox assets are top-level item.* keys (not a parseBlock
|
|
218
|
-
// sub-component). Map onto item.checkbox so multiSelect can render them.
|
|
219
|
-
const checkboxAsset =
|
|
220
|
-
configuration["item.checkbox_asset"] ||
|
|
221
|
-
configuration[`${normalizedComponentName}_item_checkbox_asset`];
|
|
222
|
-
|
|
223
|
-
const checkedCheckboxAsset =
|
|
224
|
-
configuration["item.checked_checkbox_asset"] ||
|
|
225
|
-
configuration[`${normalizedComponentName}_item_checked_checkbox_asset`];
|
|
226
|
-
|
|
227
|
-
if (checkboxAsset || checkedCheckboxAsset) {
|
|
228
|
-
if (!result.item.checkbox.default) {
|
|
229
|
-
result.item.checkbox.default = {};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
if (checkboxAsset) {
|
|
233
|
-
result.item.checkbox.default.asset = checkboxAsset;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (checkedCheckboxAsset) {
|
|
237
|
-
result.item.checkbox.default.checkedAsset = checkedCheckboxAsset;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Edit Order row icons: prefer reorder_playlist_item.*, then flavor1, then
|
|
242
|
-
// flavor2 playlist_experience_assets_* (playlist-experience theme only).
|
|
243
|
-
const removeAsset = isPlaylistExperience
|
|
244
|
-
? configuration["reorder_playlist_item.remove_from_playlist_asset"] ||
|
|
245
|
-
configuration[
|
|
246
|
-
`${normalizedComponentName}_reorder_playlist_item_remove_from_playlist_asset`
|
|
247
|
-
] ||
|
|
248
|
-
configuration[
|
|
249
|
-
"playlist_experience_assets_flavor1.remove_from_playlist_asset"
|
|
250
|
-
] ||
|
|
251
|
-
configuration[
|
|
252
|
-
`${normalizedComponentName}_assets_flavor1_remove_from_playlist_asset`
|
|
253
|
-
] ||
|
|
254
|
-
configuration[
|
|
255
|
-
"playlist_experience_assets_flavor2.remove_from_playlist_asset"
|
|
256
|
-
] ||
|
|
257
|
-
configuration[
|
|
258
|
-
`${normalizedComponentName}_assets_flavor2_remove_from_playlist_asset`
|
|
259
|
-
]
|
|
260
|
-
: undefined;
|
|
213
|
+
// Leaf uploader keys. They are not `<block>_<sub>_<prop>` styles, so
|
|
214
|
+
// getAllSpecificStyles never sees them.
|
|
215
|
+
const assignLeafAsset = (
|
|
216
|
+
block: ModalBlocksConfig["item"] | undefined,
|
|
217
|
+
key: string,
|
|
218
|
+
prop: "playAsset" | "pauseAsset" | "removeAsset" | "dragAsset"
|
|
219
|
+
) => {
|
|
220
|
+
if (!block) return;
|
|
221
|
+
|
|
222
|
+
const value = configuration[key];
|
|
223
|
+
|
|
224
|
+
if (typeof value !== "string" || !value.trim()) return;
|
|
225
|
+
|
|
226
|
+
block.actionButtons = {
|
|
227
|
+
...block.actionButtons,
|
|
228
|
+
default: {
|
|
229
|
+
...(block.actionButtons?.default || {}),
|
|
230
|
+
[prop]: value,
|
|
231
|
+
},
|
|
232
|
+
};
|
|
233
|
+
};
|
|
261
234
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
configuration[
|
|
268
|
-
"playlist_experience_assets_flavor1.reorder_handle_asset"
|
|
269
|
-
] ||
|
|
270
|
-
configuration[
|
|
271
|
-
`${normalizedComponentName}_assets_flavor1_reorder_handle_asset`
|
|
272
|
-
] ||
|
|
273
|
-
configuration[
|
|
274
|
-
"playlist_experience_assets_flavor2.reorder_handle_asset"
|
|
275
|
-
] ||
|
|
276
|
-
configuration[
|
|
277
|
-
`${normalizedComponentName}_assets_flavor2_reorder_handle_asset`
|
|
278
|
-
]
|
|
279
|
-
: undefined;
|
|
235
|
+
assignLeafAsset(
|
|
236
|
+
result.nowPlayingItem,
|
|
237
|
+
"now_playing_item.play_asset",
|
|
238
|
+
"playAsset"
|
|
239
|
+
);
|
|
280
240
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
241
|
+
assignLeafAsset(
|
|
242
|
+
result.nowPlayingItem,
|
|
243
|
+
"now_playing_item.pause_asset",
|
|
244
|
+
"pauseAsset"
|
|
245
|
+
);
|
|
285
246
|
|
|
286
|
-
|
|
287
|
-
result.item.actionButtons.default.removeAsset = removeAsset;
|
|
288
|
-
}
|
|
247
|
+
assignLeafAsset(result.item, "item.remove_asset", "removeAsset");
|
|
289
248
|
|
|
290
|
-
|
|
291
|
-
result.item.actionButtons.default.dragAsset = dragAsset;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
249
|
+
assignLeafAsset(result.item, "item.reorder_handle_asset", "dragAsset");
|
|
294
250
|
|
|
295
251
|
return result;
|
|
296
252
|
};
|