@applicaster/zapp-react-native-ui-components 16.0.0-rc.92 → 16.0.0-rc.93
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/Components/LinkHandler/LinkHandler.tsx +2 -7
- package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
- package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
- package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +64 -20
- package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +3 -1
- package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +108 -0
- package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +148 -1
- package/Components/ModalComponent/BottomSheetModalContent.tsx +85 -9
- package/Components/Screen/__tests__/Screen.test.tsx +24 -0
- package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
- package/Components/Screen/index.tsx +5 -0
- package/package.json +5 -5
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import * as R from "ramda";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { usePlugins } from "@applicaster/zapp-react-native-redux/hooks";
|
|
4
|
+
import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
6
5
|
import {
|
|
7
6
|
inflateString,
|
|
8
7
|
objectToReadableString,
|
|
@@ -40,12 +39,8 @@ export async function inflateUrl(url) {
|
|
|
40
39
|
|
|
41
40
|
export function LinkHandler(props: Props) {
|
|
42
41
|
const screenData = props?.screenData;
|
|
43
|
-
const plugins = usePlugins();
|
|
44
42
|
|
|
45
|
-
const ScreenPlugin =
|
|
46
|
-
WEBVIEW_SCREEN_IDENTIFIER,
|
|
47
|
-
plugins
|
|
48
|
-
);
|
|
43
|
+
const ScreenPlugin = getPluginByIdentifier(WEBVIEW_SCREEN_IDENTIFIER);
|
|
49
44
|
|
|
50
45
|
const Component =
|
|
51
46
|
R.path(["module", "Component"], ScreenPlugin) ||
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
1
|
+
import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
3
2
|
import { loggerLiveImageManager } from "../../../VideoLive/loggerHelper";
|
|
4
3
|
|
|
5
4
|
const { log_debug, log_error } = loggerLiveImageManager;
|
|
@@ -74,10 +73,9 @@ export async function executePreloadHooks({
|
|
|
74
73
|
let payload: any = entry;
|
|
75
74
|
|
|
76
75
|
let needsAbort = false;
|
|
77
|
-
const plugins = appStore.get("plugins");
|
|
78
76
|
|
|
79
77
|
for (const hookConfig of sortedHooks) {
|
|
80
|
-
const plugin =
|
|
78
|
+
const plugin = getPluginByIdentifier(hookConfig.identifier, true);
|
|
81
79
|
|
|
82
80
|
if (!plugin?.module) {
|
|
83
81
|
log_debug(
|
|
@@ -8,7 +8,7 @@ import Image from "../Image";
|
|
|
8
8
|
import { useTrackCurrentAutoScrollingElement } from "@applicaster/zapp-react-native-utils/reactHooks/autoscrolling/useTrackCurrentAutoScrollingElement";
|
|
9
9
|
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
10
10
|
import { getPropComponentType } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
11
|
-
import {
|
|
11
|
+
import { getPluginByIdentifier } from "@applicaster/zapp-react-native-utils/pluginUtils";
|
|
12
12
|
import { useAccessibilityState } from "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager/hooks";
|
|
13
13
|
|
|
14
14
|
type LiveImageProps = {
|
|
@@ -249,9 +249,8 @@ const LiveImageComponent = (props: LiveImageProps) => {
|
|
|
249
249
|
getPlayerConfig(player_screen_id, actionIdentifier) ?? {};
|
|
250
250
|
|
|
251
251
|
const playableEntry = playerPluginId ? getPlayableEntry(item) : null;
|
|
252
|
-
const plugins = appStore.get("plugins");
|
|
253
252
|
|
|
254
|
-
const playerPlugin =
|
|
253
|
+
const playerPlugin = getPluginByIdentifier(playerPluginId);
|
|
255
254
|
|
|
256
255
|
const isShouldRender =
|
|
257
256
|
playerPlugin &&
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Modal Building Block — Header
|
|
3
3
|
*
|
|
4
|
-
* Single-file Expo Snack demo for the Header building block spec.
|
|
5
|
-
*
|
|
6
4
|
* Green (configurable): title, closeButton (enable + asset), actionButtons styling
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* (incl. Clear padding/radius for Variant 3 / section_header Studio keys)
|
|
6
|
+
* White (hardcoded): container gutters, action button slot enablement/actions,
|
|
7
|
+
* close dimensions, default action labels (overridable via HeaderData.clearLabel)
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
import React, { useCallback, useState } from "react";
|
|
@@ -94,6 +93,12 @@ export type ActionButtonsStyleConfiguration = {
|
|
|
94
93
|
iosLetterSpacing: number;
|
|
95
94
|
androidLetterSpacing: number;
|
|
96
95
|
textTransform: TextTransform;
|
|
96
|
+
/** Green Variant 3 fields — defaults match ACTION_BUTTON_LAYOUT_SPEC. */
|
|
97
|
+
paddingTop?: number;
|
|
98
|
+
paddingRight?: number;
|
|
99
|
+
paddingBottom?: number;
|
|
100
|
+
paddingLeft?: number;
|
|
101
|
+
cornerRadius?: number;
|
|
97
102
|
};
|
|
98
103
|
|
|
99
104
|
export type HeaderConfiguration = {
|
|
@@ -133,6 +138,11 @@ export const DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION: ActionButtonsStyleConfi
|
|
|
133
138
|
iosLetterSpacing: 0,
|
|
134
139
|
androidLetterSpacing: 0,
|
|
135
140
|
textTransform: "default",
|
|
141
|
+
paddingTop: 6,
|
|
142
|
+
paddingRight: 10,
|
|
143
|
+
paddingBottom: 6,
|
|
144
|
+
paddingLeft: 10,
|
|
145
|
+
cornerRadius: 16,
|
|
136
146
|
};
|
|
137
147
|
|
|
138
148
|
// ---------------------------------------------------------------------------
|
|
@@ -141,6 +151,8 @@ export const DEFAULT_ACTION_BUTTONS_STYLE_CONFIGURATION: ActionButtonsStyleConfi
|
|
|
141
151
|
|
|
142
152
|
export type HeaderData = {
|
|
143
153
|
title: string;
|
|
154
|
+
/** Localization override for Clear; falls back to ACTION_LABELS.clear. */
|
|
155
|
+
clearLabel?: string;
|
|
144
156
|
};
|
|
145
157
|
|
|
146
158
|
export type AudioPlayerHeaderProps = {
|
|
@@ -437,6 +449,7 @@ type HeaderActionButtonProps = {
|
|
|
437
449
|
actionType: HeaderActionType;
|
|
438
450
|
configuration: ActionButtonsStyleConfiguration;
|
|
439
451
|
focused?: boolean;
|
|
452
|
+
label?: string;
|
|
440
453
|
onPress?: () => void;
|
|
441
454
|
};
|
|
442
455
|
|
|
@@ -444,8 +457,24 @@ function HeaderActionButton({
|
|
|
444
457
|
actionType,
|
|
445
458
|
configuration,
|
|
446
459
|
focused = false,
|
|
460
|
+
label,
|
|
447
461
|
onPress,
|
|
448
462
|
}: HeaderActionButtonProps) {
|
|
463
|
+
const paddingTop =
|
|
464
|
+
configuration.paddingTop ?? ACTION_BUTTON_LAYOUT_SPEC.paddingTop;
|
|
465
|
+
|
|
466
|
+
const paddingRight =
|
|
467
|
+
configuration.paddingRight ?? ACTION_BUTTON_LAYOUT_SPEC.paddingRight;
|
|
468
|
+
|
|
469
|
+
const paddingBottom =
|
|
470
|
+
configuration.paddingBottom ?? ACTION_BUTTON_LAYOUT_SPEC.paddingBottom;
|
|
471
|
+
|
|
472
|
+
const paddingLeft =
|
|
473
|
+
configuration.paddingLeft ?? ACTION_BUTTON_LAYOUT_SPEC.paddingLeft;
|
|
474
|
+
|
|
475
|
+
const cornerRadius =
|
|
476
|
+
configuration.cornerRadius ?? ACTION_BUTTON_LAYOUT_SPEC.cornerRadius;
|
|
477
|
+
|
|
449
478
|
return (
|
|
450
479
|
<Pressable
|
|
451
480
|
onPress={onPress}
|
|
@@ -454,11 +483,11 @@ function HeaderActionButton({
|
|
|
454
483
|
pressed || focused
|
|
455
484
|
? configuration.focusedBackgroundColor
|
|
456
485
|
: configuration.defaultBackgroundColor,
|
|
457
|
-
borderRadius:
|
|
458
|
-
paddingTop
|
|
459
|
-
paddingRight
|
|
460
|
-
paddingBottom
|
|
461
|
-
paddingLeft
|
|
486
|
+
borderRadius: cornerRadius,
|
|
487
|
+
paddingTop,
|
|
488
|
+
paddingRight,
|
|
489
|
+
paddingBottom,
|
|
490
|
+
paddingLeft,
|
|
462
491
|
})}
|
|
463
492
|
>
|
|
464
493
|
<Text
|
|
@@ -481,7 +510,7 @@ function HeaderActionButton({
|
|
|
481
510
|
}}
|
|
482
511
|
numberOfLines={1}
|
|
483
512
|
>
|
|
484
|
-
{ACTION_LABELS[actionType]}
|
|
513
|
+
{label || ACTION_LABELS[actionType]}
|
|
485
514
|
</Text>
|
|
486
515
|
</Pressable>
|
|
487
516
|
);
|
|
@@ -594,14 +623,20 @@ export function AudioPlayerHeader({
|
|
|
594
623
|
onClear,
|
|
595
624
|
}: AudioPlayerHeaderProps) {
|
|
596
625
|
const modalBlocksStyle = useModalBlocksStyle();
|
|
597
|
-
|
|
626
|
+
|
|
627
|
+
// Next Up section header uses sectionHeader — never Theme modal `header`,
|
|
628
|
+
// so the Queue title styles do not collide with the section title.
|
|
629
|
+
const styleBlock =
|
|
630
|
+
configuration.variant === "section"
|
|
631
|
+
? modalBlocksStyle?.sectionHeader
|
|
632
|
+
: modalBlocksStyle?.header;
|
|
598
633
|
|
|
599
634
|
const effectiveConfig = React.useMemo(() => {
|
|
600
|
-
return mergeHeaderConfiguration(configuration,
|
|
601
|
-
}, [configuration,
|
|
635
|
+
return mergeHeaderConfiguration(configuration, styleBlock);
|
|
636
|
+
}, [configuration, styleBlock]);
|
|
602
637
|
|
|
603
638
|
const { variant, title, closeButton, actionButtons } = effectiveConfig;
|
|
604
|
-
const { title: titleText } = data;
|
|
639
|
+
const { title: titleText, clearLabel } = data;
|
|
605
640
|
|
|
606
641
|
const container = CONTAINER_SPEC[variant];
|
|
607
642
|
const behavior = ACTION_BUTTONS_BEHAVIOR[variant];
|
|
@@ -612,16 +647,24 @@ export function AudioPlayerHeader({
|
|
|
612
647
|
behavior.sectionEnabled
|
|
613
648
|
);
|
|
614
649
|
|
|
615
|
-
const showTrailingAction =
|
|
616
|
-
behavior.trailing,
|
|
617
|
-
behavior.
|
|
618
|
-
|
|
650
|
+
const showTrailingAction =
|
|
651
|
+
shouldShowActionSlot(behavior.trailing, behavior.sectionEnabled) &&
|
|
652
|
+
(behavior.trailing.actionType !== "clear" || !!onClear);
|
|
653
|
+
|
|
654
|
+
const actionLabelFor = (actionType?: HeaderActionType) => {
|
|
655
|
+
if (actionType === "clear" && clearLabel) {
|
|
656
|
+
return clearLabel;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return actionType ? ACTION_LABELS[actionType] : undefined;
|
|
660
|
+
};
|
|
619
661
|
|
|
620
662
|
const leadingContent = showLeadingAction ? (
|
|
621
663
|
<HeaderActionButton
|
|
622
|
-
actionType={behavior.leading.actionType}
|
|
664
|
+
actionType={behavior.leading.actionType as HeaderActionType}
|
|
623
665
|
configuration={actionButtons}
|
|
624
666
|
focused={focusedLeadingAction}
|
|
667
|
+
label={actionLabelFor(behavior.leading.actionType)}
|
|
625
668
|
onPress={onCancel}
|
|
626
669
|
/>
|
|
627
670
|
) : onBack ? (
|
|
@@ -630,9 +673,10 @@ export function AudioPlayerHeader({
|
|
|
630
673
|
|
|
631
674
|
const trailingContent = showTrailingAction ? (
|
|
632
675
|
<HeaderActionButton
|
|
633
|
-
actionType={behavior.trailing.actionType}
|
|
676
|
+
actionType={behavior.trailing.actionType as HeaderActionType}
|
|
634
677
|
configuration={actionButtons}
|
|
635
678
|
focused={focusedTrailingAction}
|
|
679
|
+
label={actionLabelFor(behavior.trailing.actionType)}
|
|
636
680
|
onPress={behavior.trailing.actionType === "done" ? onDone : onClear}
|
|
637
681
|
/>
|
|
638
682
|
) : closeButton.enabled && !isCenterAligned ? (
|
|
@@ -14,12 +14,14 @@ type NowPlayingHeaderSectionProps = {
|
|
|
14
14
|
onClear?: () => void;
|
|
15
15
|
nextUpTitle?: string;
|
|
16
16
|
nowPlayingTitle?: string;
|
|
17
|
+
clearLabel?: string;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export function NowPlayingHeaderSection({
|
|
20
21
|
onClear,
|
|
21
22
|
nextUpTitle = "Next Up",
|
|
22
23
|
nowPlayingTitle = "Now Playing",
|
|
24
|
+
clearLabel,
|
|
23
25
|
}: NowPlayingHeaderSectionProps) {
|
|
24
26
|
const player = usePlayer();
|
|
25
27
|
const playerState = usePlayerState("queue-now-playing-section");
|
|
@@ -74,7 +76,7 @@ export function NowPlayingHeaderSection({
|
|
|
74
76
|
{renderNowPlayingTrack()}
|
|
75
77
|
<AudioPlayerHeader
|
|
76
78
|
configuration={VARIANT_SECTION_HEADER}
|
|
77
|
-
data={{ title: nextUpTitle }}
|
|
79
|
+
data={{ title: nextUpTitle, clearLabel }}
|
|
78
80
|
onClear={onClear}
|
|
79
81
|
/>
|
|
80
82
|
</View>
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
mergeItemConfiguration,
|
|
3
3
|
mergeButtonConfiguration,
|
|
4
|
+
mergeHeaderConfiguration,
|
|
4
5
|
} from "../mergeStyles";
|
|
6
|
+
import {
|
|
7
|
+
VARIANT_SECTION_HEADER,
|
|
8
|
+
VARIANT_SINGLE_LINE_HEADER,
|
|
9
|
+
} from "../../Components/Header";
|
|
5
10
|
|
|
6
11
|
const base = {
|
|
7
12
|
background: {
|
|
@@ -246,3 +251,106 @@ describe("mergeButtonConfiguration background", () => {
|
|
|
246
251
|
expect(merged.title.textTransform).toBe("uppercase");
|
|
247
252
|
});
|
|
248
253
|
});
|
|
254
|
+
|
|
255
|
+
describe("mergeHeaderConfiguration section header (Variant 3)", () => {
|
|
256
|
+
it("merges section title + Clear action button styles", () => {
|
|
257
|
+
const merged = mergeHeaderConfiguration(VARIANT_SECTION_HEADER, {
|
|
258
|
+
title: {
|
|
259
|
+
default: {
|
|
260
|
+
fontColor: "#ABCDEF",
|
|
261
|
+
fontSize: 15,
|
|
262
|
+
lineHeight: 36,
|
|
263
|
+
fontFamily: "CustomSection",
|
|
264
|
+
letterSpacing: -0.2,
|
|
265
|
+
textTransform: "uppercase",
|
|
266
|
+
alignment: "left",
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
actionButtons: {
|
|
270
|
+
default: {
|
|
271
|
+
backgroundColor: "rgba(10, 10, 10, 1)",
|
|
272
|
+
fontColor: "#EEEEEE",
|
|
273
|
+
fontSize: 12,
|
|
274
|
+
borderRadius: 20,
|
|
275
|
+
paddingTop: 8,
|
|
276
|
+
paddingRight: 12,
|
|
277
|
+
paddingBottom: 8,
|
|
278
|
+
paddingLeft: 12,
|
|
279
|
+
},
|
|
280
|
+
focused: {
|
|
281
|
+
backgroundColor: "rgba(20, 20, 20, 1)",
|
|
282
|
+
fontColor: "#FFDD00",
|
|
283
|
+
},
|
|
284
|
+
},
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
expect(merged.title.fontColor).toBe("#ABCDEF");
|
|
288
|
+
expect(merged.title.fontSize).toBe(15);
|
|
289
|
+
expect(merged.title.lineHeight).toBe(36);
|
|
290
|
+
expect(merged.title.iosFontFamily).toBe("CustomSection");
|
|
291
|
+
expect(merged.title.androidFontFamily).toBe("CustomSection");
|
|
292
|
+
expect(merged.title.iosLetterSpacing).toBe(-0.2);
|
|
293
|
+
expect(merged.title.textTransform).toBe("uppercase");
|
|
294
|
+
|
|
295
|
+
expect(merged.actionButtons.defaultBackgroundColor).toBe(
|
|
296
|
+
"rgba(10, 10, 10, 1)"
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
expect(merged.actionButtons.focusedBackgroundColor).toBe(
|
|
300
|
+
"rgba(20, 20, 20, 1)"
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
expect(merged.actionButtons.fontColor).toBe("#EEEEEE");
|
|
304
|
+
expect(merged.actionButtons.focusedFontColor).toBe("#FFDD00");
|
|
305
|
+
expect(merged.actionButtons.fontSize).toBe(12);
|
|
306
|
+
expect(merged.actionButtons.cornerRadius).toBe(20);
|
|
307
|
+
expect(merged.actionButtons.paddingTop).toBe(8);
|
|
308
|
+
expect(merged.actionButtons.paddingLeft).toBe(12);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it("uses title.default when only platform font and letter spacing are set", () => {
|
|
312
|
+
const merged = mergeHeaderConfiguration(VARIANT_SECTION_HEADER, {
|
|
313
|
+
title: {
|
|
314
|
+
default: {
|
|
315
|
+
iosFontFamily: "SFProText-Semibold",
|
|
316
|
+
androidFontFamily: "Roboto-Medium",
|
|
317
|
+
iosLetterSpacing: -0.4,
|
|
318
|
+
androidLetterSpacing: 0,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
expect(merged.title.iosFontFamily).toBe("SFProText-Semibold");
|
|
324
|
+
expect(merged.title.androidFontFamily).toBe("Roboto-Medium");
|
|
325
|
+
expect(merged.title.iosLetterSpacing).toBe(-0.4);
|
|
326
|
+
expect(merged.title.androidLetterSpacing).toBe(0);
|
|
327
|
+
expect(merged.title.fontSize).toBe(VARIANT_SECTION_HEADER.title.fontSize);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("does not change section defaults when block is empty", () => {
|
|
331
|
+
const merged = mergeHeaderConfiguration(VARIANT_SECTION_HEADER, {
|
|
332
|
+
title: { default: {} },
|
|
333
|
+
actionButtons: { default: {} },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(merged.title.fontSize).toBe(VARIANT_SECTION_HEADER.title.fontSize);
|
|
337
|
+
|
|
338
|
+
expect(merged.actionButtons.defaultBackgroundColor).toBe(
|
|
339
|
+
VARIANT_SECTION_HEADER.actionButtons.defaultBackgroundColor
|
|
340
|
+
);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("leaves single-line header title size untouched when only actionButtons set", () => {
|
|
344
|
+
const merged = mergeHeaderConfiguration(VARIANT_SINGLE_LINE_HEADER, {
|
|
345
|
+
actionButtons: {
|
|
346
|
+
default: { backgroundColor: "#123456" },
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
expect(merged.title.fontSize).toBe(
|
|
351
|
+
VARIANT_SINGLE_LINE_HEADER.title.fontSize
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
expect(merged.actionButtons.defaultBackgroundColor).toBe("#123456");
|
|
355
|
+
});
|
|
356
|
+
});
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "../Components/Item";
|
|
10
10
|
import {
|
|
11
11
|
TitleConfiguration as HeaderTitleConfiguration,
|
|
12
|
+
ActionButtonsStyleConfiguration as HeaderActionButtonsStyleConfiguration,
|
|
12
13
|
HeaderConfiguration,
|
|
13
14
|
} from "../Components/Header";
|
|
14
15
|
import {
|
|
@@ -210,6 +211,122 @@ function mergeHeaderTitle(
|
|
|
210
211
|
...base,
|
|
211
212
|
...(override.fontColor ? { fontColor: override.fontColor } : {}),
|
|
212
213
|
...(override.fontSize !== undefined ? { fontSize: override.fontSize } : {}),
|
|
214
|
+
...(override.lineHeight !== undefined
|
|
215
|
+
? { lineHeight: override.lineHeight }
|
|
216
|
+
: {}),
|
|
217
|
+
...(typeof override.fontFamily === "string"
|
|
218
|
+
? {
|
|
219
|
+
iosFontFamily: override.fontFamily,
|
|
220
|
+
androidFontFamily: override.fontFamily,
|
|
221
|
+
}
|
|
222
|
+
: {}),
|
|
223
|
+
...(override.iosFontFamily
|
|
224
|
+
? { iosFontFamily: override.iosFontFamily }
|
|
225
|
+
: {}),
|
|
226
|
+
...(override.androidFontFamily
|
|
227
|
+
? { androidFontFamily: override.androidFontFamily }
|
|
228
|
+
: {}),
|
|
229
|
+
...(override.letterSpacing !== undefined
|
|
230
|
+
? {
|
|
231
|
+
iosLetterSpacing: override.letterSpacing,
|
|
232
|
+
androidLetterSpacing: override.letterSpacing,
|
|
233
|
+
}
|
|
234
|
+
: {}),
|
|
235
|
+
...(override.iosLetterSpacing !== undefined
|
|
236
|
+
? { iosLetterSpacing: override.iosLetterSpacing }
|
|
237
|
+
: {}),
|
|
238
|
+
...(override.androidLetterSpacing !== undefined
|
|
239
|
+
? { androidLetterSpacing: override.androidLetterSpacing }
|
|
240
|
+
: {}),
|
|
241
|
+
...(override.textTransform
|
|
242
|
+
? { textTransform: override.textTransform }
|
|
243
|
+
: {}),
|
|
244
|
+
...(override.alignment ? { alignment: override.alignment } : {}),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function mergeHeaderActionButtons(
|
|
249
|
+
base: HeaderActionButtonsStyleConfiguration,
|
|
250
|
+
defaultOverride?: Record<string, any>,
|
|
251
|
+
focusedOverride?: Record<string, any>
|
|
252
|
+
): HeaderActionButtonsStyleConfiguration {
|
|
253
|
+
if (!defaultOverride && !focusedOverride) return base;
|
|
254
|
+
|
|
255
|
+
const focusedBackgroundColor =
|
|
256
|
+
focusedOverride?.backgroundColor ??
|
|
257
|
+
defaultOverride?.focusBackgroundColor ??
|
|
258
|
+
defaultOverride?.focusedBackgroundColor;
|
|
259
|
+
|
|
260
|
+
const focusedFontColor =
|
|
261
|
+
focusedOverride?.fontColor ??
|
|
262
|
+
defaultOverride?.focusFontColor ??
|
|
263
|
+
defaultOverride?.focusedFontColor;
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
...base,
|
|
267
|
+
...(defaultOverride?.backgroundColor
|
|
268
|
+
? { defaultBackgroundColor: defaultOverride.backgroundColor }
|
|
269
|
+
: {}),
|
|
270
|
+
...(focusedBackgroundColor != null && focusedBackgroundColor !== ""
|
|
271
|
+
? { focusedBackgroundColor }
|
|
272
|
+
: {}),
|
|
273
|
+
...(defaultOverride?.fontColor
|
|
274
|
+
? { fontColor: defaultOverride.fontColor }
|
|
275
|
+
: {}),
|
|
276
|
+
...(focusedFontColor != null && focusedFontColor !== ""
|
|
277
|
+
? { focusedFontColor }
|
|
278
|
+
: {}),
|
|
279
|
+
...(defaultOverride?.fontSize !== undefined
|
|
280
|
+
? { fontSize: defaultOverride.fontSize }
|
|
281
|
+
: {}),
|
|
282
|
+
...(defaultOverride?.lineHeight !== undefined
|
|
283
|
+
? { lineHeight: defaultOverride.lineHeight }
|
|
284
|
+
: {}),
|
|
285
|
+
...(typeof defaultOverride?.fontFamily === "string"
|
|
286
|
+
? {
|
|
287
|
+
iosFontFamily: defaultOverride.fontFamily,
|
|
288
|
+
androidFontFamily: defaultOverride.fontFamily,
|
|
289
|
+
}
|
|
290
|
+
: {}),
|
|
291
|
+
...(defaultOverride?.iosFontFamily
|
|
292
|
+
? { iosFontFamily: defaultOverride.iosFontFamily }
|
|
293
|
+
: {}),
|
|
294
|
+
...(defaultOverride?.androidFontFamily
|
|
295
|
+
? { androidFontFamily: defaultOverride.androidFontFamily }
|
|
296
|
+
: {}),
|
|
297
|
+
...(defaultOverride?.letterSpacing !== undefined
|
|
298
|
+
? {
|
|
299
|
+
iosLetterSpacing: defaultOverride.letterSpacing,
|
|
300
|
+
androidLetterSpacing: defaultOverride.letterSpacing,
|
|
301
|
+
}
|
|
302
|
+
: {}),
|
|
303
|
+
...(defaultOverride?.iosLetterSpacing !== undefined
|
|
304
|
+
? { iosLetterSpacing: defaultOverride.iosLetterSpacing }
|
|
305
|
+
: {}),
|
|
306
|
+
...(defaultOverride?.androidLetterSpacing !== undefined
|
|
307
|
+
? { androidLetterSpacing: defaultOverride.androidLetterSpacing }
|
|
308
|
+
: {}),
|
|
309
|
+
...(defaultOverride?.textTransform
|
|
310
|
+
? { textTransform: defaultOverride.textTransform }
|
|
311
|
+
: {}),
|
|
312
|
+
...(defaultOverride?.borderRadius !== undefined
|
|
313
|
+
? { cornerRadius: defaultOverride.borderRadius }
|
|
314
|
+
: {}),
|
|
315
|
+
...(defaultOverride?.cornerRadius !== undefined
|
|
316
|
+
? { cornerRadius: defaultOverride.cornerRadius }
|
|
317
|
+
: {}),
|
|
318
|
+
...(defaultOverride?.paddingTop !== undefined
|
|
319
|
+
? { paddingTop: defaultOverride.paddingTop }
|
|
320
|
+
: {}),
|
|
321
|
+
...(defaultOverride?.paddingRight !== undefined
|
|
322
|
+
? { paddingRight: defaultOverride.paddingRight }
|
|
323
|
+
: {}),
|
|
324
|
+
...(defaultOverride?.paddingBottom !== undefined
|
|
325
|
+
? { paddingBottom: defaultOverride.paddingBottom }
|
|
326
|
+
: {}),
|
|
327
|
+
...(defaultOverride?.paddingLeft !== undefined
|
|
328
|
+
? { paddingLeft: defaultOverride.paddingLeft }
|
|
329
|
+
: {}),
|
|
213
330
|
};
|
|
214
331
|
}
|
|
215
332
|
|
|
@@ -221,9 +338,39 @@ export function mergeHeaderConfiguration(
|
|
|
221
338
|
|
|
222
339
|
const bgOverride = headerBlock.container?.default?.backgroundColor;
|
|
223
340
|
|
|
341
|
+
const actionButtonsDefault =
|
|
342
|
+
headerBlock.actionButtons?.default || headerBlock.actionButtons;
|
|
343
|
+
|
|
344
|
+
const actionButtonsFocused =
|
|
345
|
+
headerBlock.actionButtons?.focused || headerBlock.actionButtons?.focus;
|
|
346
|
+
|
|
347
|
+
// Parser shape is title.default.*; hydrate() returns a flat title object.
|
|
348
|
+
const titleDefault = headerBlock.title?.default;
|
|
349
|
+
|
|
350
|
+
const titleOverride =
|
|
351
|
+
titleDefault &&
|
|
352
|
+
(titleDefault.fontColor !== undefined ||
|
|
353
|
+
titleDefault.fontSize !== undefined ||
|
|
354
|
+
titleDefault.lineHeight !== undefined ||
|
|
355
|
+
titleDefault.fontFamily ||
|
|
356
|
+
titleDefault.iosFontFamily ||
|
|
357
|
+
titleDefault.androidFontFamily ||
|
|
358
|
+
titleDefault.letterSpacing !== undefined ||
|
|
359
|
+
titleDefault.iosLetterSpacing !== undefined ||
|
|
360
|
+
titleDefault.androidLetterSpacing !== undefined ||
|
|
361
|
+
titleDefault.textTransform ||
|
|
362
|
+
titleDefault.alignment)
|
|
363
|
+
? titleDefault
|
|
364
|
+
: headerBlock.title;
|
|
365
|
+
|
|
224
366
|
return {
|
|
225
367
|
...base,
|
|
226
|
-
title: mergeHeaderTitle(base.title,
|
|
368
|
+
title: mergeHeaderTitle(base.title, titleOverride),
|
|
369
|
+
actionButtons: mergeHeaderActionButtons(
|
|
370
|
+
base.actionButtons,
|
|
371
|
+
actionButtonsDefault,
|
|
372
|
+
actionButtonsFocused
|
|
373
|
+
),
|
|
227
374
|
...(bgOverride ? { backgroundColor: bgOverride } : {}),
|
|
228
375
|
};
|
|
229
376
|
}
|
|
@@ -150,17 +150,81 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
150
150
|
[plugins, themePluginId]
|
|
151
151
|
);
|
|
152
152
|
|
|
153
|
-
const styleOverrides = useMemo(() => {
|
|
154
|
-
if (propStyleOverrides) {
|
|
155
|
-
return propStyleOverrides;
|
|
156
|
-
}
|
|
157
|
-
|
|
153
|
+
const styleOverrides = useMemo<ModalBlocksConfig | null>(() => {
|
|
158
154
|
const configuration =
|
|
159
155
|
plugin?.configuration || (plugin as any)?.configuration_json;
|
|
160
156
|
|
|
161
|
-
|
|
157
|
+
const parsed =
|
|
158
|
+
configuration && themePluginId
|
|
159
|
+
? parseModalBlocksConfiguration(configuration, themePluginId)
|
|
160
|
+
: null;
|
|
161
|
+
|
|
162
|
+
if (!propStyleOverrides) {
|
|
163
|
+
return parsed;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// hydrate(QueueStyles) only emits sectionHeader after the plugin class
|
|
167
|
+
// includes it. Until then, Studio/local keys in configuration_json still
|
|
168
|
+
// apply via the parser.
|
|
169
|
+
const hydratedSection = propStyleOverrides.sectionHeader;
|
|
170
|
+
|
|
171
|
+
const hasTitleStyle = (style?: Record<string, any>) =>
|
|
172
|
+
Boolean(
|
|
173
|
+
style &&
|
|
174
|
+
(style.fontColor ||
|
|
175
|
+
style.fontSize !== undefined ||
|
|
176
|
+
style.lineHeight !== undefined ||
|
|
177
|
+
style.fontFamily ||
|
|
178
|
+
style.iosFontFamily ||
|
|
179
|
+
style.androidFontFamily ||
|
|
180
|
+
style.letterSpacing !== undefined ||
|
|
181
|
+
style.iosLetterSpacing !== undefined ||
|
|
182
|
+
style.androidLetterSpacing !== undefined ||
|
|
183
|
+
style.textTransform ||
|
|
184
|
+
style.alignment)
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const hasActionStyle = (style?: Record<string, any>) =>
|
|
188
|
+
Boolean(
|
|
189
|
+
style &&
|
|
190
|
+
(style.backgroundColor ||
|
|
191
|
+
style.fontColor ||
|
|
192
|
+
style.fontSize !== undefined ||
|
|
193
|
+
style.lineHeight !== undefined ||
|
|
194
|
+
style.fontFamily ||
|
|
195
|
+
style.iosFontFamily ||
|
|
196
|
+
style.androidFontFamily ||
|
|
197
|
+
style.letterSpacing !== undefined ||
|
|
198
|
+
style.iosLetterSpacing !== undefined ||
|
|
199
|
+
style.androidLetterSpacing !== undefined ||
|
|
200
|
+
style.textTransform ||
|
|
201
|
+
style.borderRadius !== undefined ||
|
|
202
|
+
style.cornerRadius !== undefined ||
|
|
203
|
+
style.paddingTop !== undefined ||
|
|
204
|
+
style.paddingRight !== undefined ||
|
|
205
|
+
style.paddingBottom !== undefined ||
|
|
206
|
+
style.paddingLeft !== undefined)
|
|
207
|
+
);
|
|
162
208
|
|
|
163
|
-
|
|
209
|
+
const actionNode = hydratedSection?.actionButtons;
|
|
210
|
+
|
|
211
|
+
// An empty `default: {}` is truthy and would hide fields set on the flat
|
|
212
|
+
// object or on the focused bucket.
|
|
213
|
+
const hasHydratedSection =
|
|
214
|
+
hasTitleStyle(hydratedSection?.title?.default) ||
|
|
215
|
+
hasTitleStyle(hydratedSection?.title) ||
|
|
216
|
+
hasActionStyle(actionNode?.default) ||
|
|
217
|
+
hasActionStyle(actionNode?.focused) ||
|
|
218
|
+
hasActionStyle(actionNode?.focus) ||
|
|
219
|
+
hasActionStyle(actionNode);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
...parsed,
|
|
223
|
+
...propStyleOverrides,
|
|
224
|
+
sectionHeader: hasHydratedSection
|
|
225
|
+
? hydratedSection
|
|
226
|
+
: parsed?.sectionHeader,
|
|
227
|
+
};
|
|
164
228
|
}, [propStyleOverrides, plugin, themePluginId]);
|
|
165
229
|
|
|
166
230
|
const pluginLocalizations = useLocalizedStrings({
|
|
@@ -178,17 +242,26 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
178
242
|
const nextUpTitle =
|
|
179
243
|
pluginLocalizations?.next_up_title ||
|
|
180
244
|
(normalizedPluginId
|
|
181
|
-
? pluginLocalizations?.[`${normalizedPluginId}_next_up_title`]
|
|
245
|
+
? pluginLocalizations?.[`${normalizedPluginId}_next_up_title`] ||
|
|
246
|
+
pluginLocalizations?.[`${normalizedPluginId}_label_next_up`]
|
|
182
247
|
: undefined) ||
|
|
183
248
|
"Next Up";
|
|
184
249
|
|
|
185
250
|
const nowPlayingTitle =
|
|
186
251
|
pluginLocalizations?.now_playing_title ||
|
|
187
252
|
(normalizedPluginId
|
|
188
|
-
? pluginLocalizations?.[`${normalizedPluginId}_now_playing_title`]
|
|
253
|
+
? pluginLocalizations?.[`${normalizedPluginId}_now_playing_title`] ||
|
|
254
|
+
pluginLocalizations?.[`${normalizedPluginId}_label_now_playing`]
|
|
189
255
|
: undefined) ||
|
|
190
256
|
"Now Playing";
|
|
191
257
|
|
|
258
|
+
const clearLabel =
|
|
259
|
+
pluginLocalizations?.clear_label ||
|
|
260
|
+
(normalizedPluginId
|
|
261
|
+
? pluginLocalizations?.[`${normalizedPluginId}_label_clear`]
|
|
262
|
+
: undefined) ||
|
|
263
|
+
"Clear";
|
|
264
|
+
|
|
192
265
|
const resolvedTitle = props.title || localizedHeaderTitle || title;
|
|
193
266
|
|
|
194
267
|
const itemsToRender = viewModel ? fetchedItems : items;
|
|
@@ -336,6 +409,7 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
336
409
|
onClear={hasClearAll ? handleDeleteCollection : undefined}
|
|
337
410
|
nextUpTitle={nextUpTitle}
|
|
338
411
|
nowPlayingTitle={nowPlayingTitle}
|
|
412
|
+
clearLabel={clearLabel}
|
|
339
413
|
/>
|
|
340
414
|
),
|
|
341
415
|
});
|
|
@@ -361,6 +435,7 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
361
435
|
handleDeleteCollection,
|
|
362
436
|
nextUpTitle,
|
|
363
437
|
nowPlayingTitle,
|
|
438
|
+
clearLabel,
|
|
364
439
|
hasAdd,
|
|
365
440
|
handleCreatePlaylist,
|
|
366
441
|
]);
|
|
@@ -470,6 +545,7 @@ function _BottomSheetModalContent(props: ModalComponentProps) {
|
|
|
470
545
|
onClear={hasClearAll ? handleDeleteCollection : undefined}
|
|
471
546
|
nextUpTitle={nextUpTitle}
|
|
472
547
|
nowPlayingTitle={nowPlayingTitle}
|
|
548
|
+
clearLabel={clearLabel}
|
|
473
549
|
/>
|
|
474
550
|
) : null}
|
|
475
551
|
{hasAdd ? (
|
|
@@ -141,6 +141,10 @@ const {
|
|
|
141
141
|
useGetScreenOrientation,
|
|
142
142
|
} = require("@applicaster/zapp-react-native-utils/appUtils/orientationHelper");
|
|
143
143
|
|
|
144
|
+
const {
|
|
145
|
+
useIsScreenActive,
|
|
146
|
+
} = require("@applicaster/zapp-react-native-utils/reactHooks/navigation/useIsScreenActive");
|
|
147
|
+
|
|
144
148
|
const navigator = {};
|
|
145
149
|
const navBarProps = { nav_items: [] };
|
|
146
150
|
const screenData = {};
|
|
@@ -195,4 +199,24 @@ describe("<Screen Component />", () => {
|
|
|
195
199
|
expect(toJSON()).toMatchSnapshot();
|
|
196
200
|
});
|
|
197
201
|
});
|
|
202
|
+
|
|
203
|
+
describe("when the screen is inactive", () => {
|
|
204
|
+
afterEach(() => {
|
|
205
|
+
useIsScreenActive.mockReturnValue(true);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
it("hides the screen from VoiceOver (iOS) and TalkBack (Android)", () => {
|
|
209
|
+
useIsScreenActive.mockReturnValue(false);
|
|
210
|
+
|
|
211
|
+
const { UNSAFE_root } = renderWithProviders(
|
|
212
|
+
<Screen {...screenProps} />,
|
|
213
|
+
store
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
expect(UNSAFE_root.findByType(View).props).toMatchObject({
|
|
217
|
+
accessibilityElementsHidden: true,
|
|
218
|
+
importantForAccessibility: "no-hide-descendants",
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
198
222
|
});
|
|
@@ -13,6 +13,7 @@ exports[`<Screen Component /> when the navbar should be hidden renders correctly
|
|
|
13
13
|
}
|
|
14
14
|
>
|
|
15
15
|
<View
|
|
16
|
+
accessibilityElementsHidden={false}
|
|
16
17
|
importantForAccessibility="yes"
|
|
17
18
|
style={
|
|
18
19
|
{
|
|
@@ -58,6 +59,7 @@ exports[`<Screen Component /> when the navbar should show renders correctly 1`]
|
|
|
58
59
|
}
|
|
59
60
|
>
|
|
60
61
|
<View
|
|
62
|
+
accessibilityElementsHidden={false}
|
|
61
63
|
importantForAccessibility="yes"
|
|
62
64
|
style={
|
|
63
65
|
{
|
|
@@ -121,6 +121,11 @@ export function Screen(_props: Props) {
|
|
|
121
121
|
ref={ref}
|
|
122
122
|
style={style}
|
|
123
123
|
importantForAccessibility={!isActive ? "no-hide-descendants" : "yes"}
|
|
124
|
+
// `importantForAccessibility` only affects Android (see RN docs).
|
|
125
|
+
// Without the iOS counterpart below, VoiceOver keeps traversing and
|
|
126
|
+
// focusing elements on an inactive/underlying screen (e.g. Albums In
|
|
127
|
+
// Order) even after a new screen (e.g. an Album) is pushed on top.
|
|
128
|
+
accessibilityElementsHidden={!isActive}
|
|
124
129
|
>
|
|
125
130
|
{isReady ? (
|
|
126
131
|
<>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-ui-components",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.93",
|
|
4
4
|
"description": "Applicaster Zapp React Native ui components for the Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.93",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.93",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.93",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.93",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|