@applicaster/zapp-react-native-ui-components 16.0.0-alpha.1263110541 → 16.0.0-alpha.1334507601

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 (51) hide show
  1. package/Components/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
  2. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  3. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  4. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  5. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  6. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  7. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  8. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +4 -2
  9. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  10. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +5 -2
  11. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +4 -1
  12. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +70 -73
  13. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +1 -36
  14. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +1 -26
  15. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +180 -47
  16. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +13 -20
  17. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  18. package/Components/ModalComponent/AudioPlayer/Components/index.ts +0 -2
  19. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +248 -0
  20. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +194 -19
  21. package/Components/ModalComponent/BottomSheetModalContent.tsx +94 -66
  22. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  23. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  24. package/Components/ModalComponent/SortableList.tsx +38 -23
  25. package/Components/ModalComponent/SortableList.web.tsx +1 -1
  26. package/Components/ModalComponent/TextInputContent.tsx +15 -3
  27. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +37 -5
  28. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +36 -0
  29. package/Components/ModalComponent/index.tsx +7 -9
  30. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +24 -0
  31. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +1 -1
  32. package/Components/ModalComponent/presets/selectableCell.tsx +1 -1
  33. package/Components/ModalComponent/utils.ts +6 -1
  34. package/Components/OfflineHandler/hooks.ts +0 -12
  35. package/Components/River/ComponentsMap/ComponentsMap.tsx +85 -59
  36. package/Components/River/River.tsx +4 -0
  37. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +138 -75
  38. package/Components/River/__tests__/componentsMap.test.js +8 -3
  39. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  40. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  41. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  42. package/Components/Screen/index.tsx +5 -0
  43. package/Components/Screen/navigationHandler.ts +8 -8
  44. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  45. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  46. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  47. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  48. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  49. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  50. package/package.json +5 -5
  51. package/Components/ModalComponent/AudioPlayer/Components/Action.tsx +0 -314
@@ -12,14 +12,8 @@
12
12
  */
13
13
 
14
14
  import React from "react";
15
- import {
16
- Image,
17
- Platform,
18
- Pressable,
19
- StyleSheet,
20
- Text,
21
- View,
22
- } from "react-native";
15
+ import { QBImage as Image } from "@applicaster/zapp-react-native-ui-components/Components/Image";
16
+ import { Platform, Pressable, StyleSheet, Text, View } from "react-native";
23
17
  import {
24
18
  MenuItem,
25
19
  SelectionBehavior,
@@ -56,6 +50,12 @@ export type BackgroundConfiguration = {
56
50
  defaultColor: string;
57
51
  focusedColor: string;
58
52
  cornerRadius: number;
53
+ paddingTop?: number;
54
+ paddingRight?: number;
55
+ paddingBottom?: number;
56
+ paddingLeft?: number;
57
+ highlightInsetLeft?: number;
58
+ highlightInsetRight?: number;
59
59
  };
60
60
 
61
61
  export type ImageConfiguration = {
@@ -82,6 +82,20 @@ export type NowPlayingAssetConfiguration = {
82
82
  asset?: string;
83
83
  };
84
84
 
85
+ export type CheckboxAssetsConfiguration = {
86
+ /** Unchecked multiSelect control (item.checkbox_asset). */
87
+ asset?: string;
88
+ /** Checked multiSelect control (item.checked_checkbox_asset). */
89
+ checkedAsset?: string;
90
+ };
91
+
92
+ export type ActionButtonAssetsConfiguration = {
93
+ /** Leading remove control on Edit Order rows. */
94
+ removeAsset?: string;
95
+ /** Trailing drag handle on Edit Order / queue rows. */
96
+ dragAsset?: string;
97
+ };
98
+
85
99
  export type ItemConfiguration = {
86
100
  background: BackgroundConfiguration;
87
101
  image: ImageConfiguration;
@@ -89,6 +103,8 @@ export type ItemConfiguration = {
89
103
  nowPlayingLabel: LabelConfiguration;
90
104
  textLabel1: LabelConfiguration;
91
105
  textLabel2: LabelConfiguration;
106
+ checkboxAssets?: CheckboxAssetsConfiguration;
107
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
92
108
  };
93
109
 
94
110
  export const DEFAULT_TEXT_LABEL_1_CONFIGURATION: LabelConfiguration = {
@@ -189,9 +205,9 @@ const CONTAINER_SPEC_LARGE_IMAGE: ContainerSpec = {
189
205
 
190
206
  const CONTAINER_SPEC_DEFAULT: ContainerSpec = {
191
207
  paddingTop: 8,
192
- paddingRight: 20,
208
+ paddingRight: 10,
193
209
  paddingBottom: 8,
194
- paddingLeft: 20,
210
+ paddingLeft: 10,
195
211
  horizontalGutter: 12,
196
212
  verticalGutter: 2,
197
213
  };
@@ -271,13 +287,14 @@ function resolveContainerSpec(image: ImageConfiguration): ContainerSpec {
271
287
 
272
288
  export function mapMenuItemToNowPlayingData(
273
289
  item: MenuItem,
274
- isPaused?: boolean
290
+ isPaused?: boolean,
291
+ nowPlayingLabel = "Now Playing"
275
292
  ): ItemData {
276
293
  return {
277
294
  imageUri: item.icon,
278
295
  textLabel1: item.title,
279
296
  textLabel2: item.summary ?? "",
280
- nowPlayingLabel: "Now Playing",
297
+ nowPlayingLabel,
281
298
  trailingButton: "playPause",
282
299
  isSelected: isPaused !== undefined ? !isPaused : item.isSelected,
283
300
  };
@@ -301,10 +318,13 @@ export function mapMenuItemToPlaylistData(
301
318
  const showSelectionWidget =
302
319
  behavior?.selectMode !== "none" && item.trailingButton !== "none";
303
320
 
321
+ const summary =
322
+ typeof item.summary === "string" ? item.summary.trim() : item.summary;
323
+
304
324
  return {
305
325
  imageUri: item.icon,
306
326
  textLabel1: item.title,
307
- textLabel2: item.summary ?? "",
327
+ textLabel2: summary || "",
308
328
  trailingButton: showSelectionWidget
309
329
  ? item.trailingButton || ("multiSelect" as ItemButtonAction)
310
330
  : undefined,
@@ -332,21 +352,30 @@ function ItemContainer({
332
352
  onPress,
333
353
  }: ItemContainerProps) {
334
354
  const container = resolveContainerSpec(image);
335
- const bg = focused ? background.focusedColor : background.defaultColor;
355
+ const insetLeft = background.highlightInsetLeft ?? 0;
356
+ const insetRight = background.highlightInsetRight ?? 0;
336
357
 
337
358
  return (
338
- <Pressable
339
- onPress={onPress}
340
- style={({ pressed }) => ({
341
- backgroundColor: pressed ? background.focusedColor : bg,
342
- borderRadius: background.cornerRadius,
343
- paddingTop: container.paddingTop,
344
- paddingRight: container.paddingRight,
345
- paddingBottom: container.paddingBottom,
346
- paddingLeft: container.paddingLeft,
347
- })}
348
- >
349
- {children}
359
+ <Pressable onPress={onPress}>
360
+ {({ pressed }) => (
361
+ <View
362
+ style={{
363
+ marginLeft: insetLeft,
364
+ marginRight: insetRight,
365
+ backgroundColor:
366
+ pressed || focused
367
+ ? background.focusedColor
368
+ : background.defaultColor,
369
+ borderRadius: background.cornerRadius,
370
+ paddingTop: background.paddingTop ?? container.paddingTop,
371
+ paddingRight: background.paddingRight ?? container.paddingRight,
372
+ paddingBottom: background.paddingBottom ?? container.paddingBottom,
373
+ paddingLeft: background.paddingLeft ?? container.paddingLeft,
374
+ }}
375
+ >
376
+ {children}
377
+ </View>
378
+ )}
350
379
  </Pressable>
351
380
  );
352
381
  }
@@ -379,6 +408,10 @@ type ItemLabelProps = {
379
408
  };
380
409
 
381
410
  function ItemLabel({ text, configuration, focused = false }: ItemLabelProps) {
411
+ if (!text) {
412
+ return null;
413
+ }
414
+
382
415
  const color =
383
416
  focused && configuration.focusedFontColor
384
417
  ? configuration.focusedFontColor
@@ -450,6 +483,8 @@ type ItemButtonProps = {
450
483
  focused?: boolean;
451
484
  onPress?: () => void;
452
485
  isSelected?: boolean;
486
+ checkboxAssets?: CheckboxAssetsConfiguration;
487
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
453
488
  };
454
489
 
455
490
  function ItemButton({
@@ -457,6 +492,8 @@ function ItemButton({
457
492
  focused = false,
458
493
  onPress,
459
494
  isSelected = false,
495
+ checkboxAssets,
496
+ actionButtonAssets,
460
497
  }: ItemButtonProps) {
461
498
  const layout = BUTTON_LAYOUT_SPEC[action];
462
499
 
@@ -531,6 +568,19 @@ function ItemButton({
531
568
  }
532
569
 
533
570
  case "drag": {
571
+ if (actionButtonAssets?.dragAsset) {
572
+ return (
573
+ <Image
574
+ source={{ uri: actionButtonAssets.dragAsset }}
575
+ style={{
576
+ width: layout.assetWidth,
577
+ height: layout.assetHeight,
578
+ }}
579
+ resizeMode="contain"
580
+ />
581
+ );
582
+ }
583
+
534
584
  return (
535
585
  <View
536
586
  style={{
@@ -565,6 +615,19 @@ function ItemButton({
565
615
  }
566
616
 
567
617
  case "remove": {
618
+ if (actionButtonAssets?.removeAsset) {
619
+ return (
620
+ <Image
621
+ source={{ uri: actionButtonAssets.removeAsset }}
622
+ style={{
623
+ width: layout.assetWidth,
624
+ height: layout.assetHeight,
625
+ }}
626
+ resizeMode="contain"
627
+ />
628
+ );
629
+ }
630
+
568
631
  return (
569
632
  <View
570
633
  style={{
@@ -590,6 +653,23 @@ function ItemButton({
590
653
  }
591
654
 
592
655
  case "multiSelect": {
656
+ const studioAsset = isSelected
657
+ ? checkboxAssets?.checkedAsset
658
+ : checkboxAssets?.asset;
659
+
660
+ if (studioAsset) {
661
+ return (
662
+ <Image
663
+ source={{ uri: studioAsset }}
664
+ style={{
665
+ width: layout.assetWidth,
666
+ height: layout.assetHeight,
667
+ }}
668
+ resizeMode="contain"
669
+ />
670
+ );
671
+ }
672
+
593
673
  if (!isSelected) {
594
674
  return (
595
675
  <View
@@ -636,18 +716,31 @@ function ItemButton({
636
716
  }
637
717
  };
638
718
 
719
+ const buttonPadding = {
720
+ paddingTop: layout.paddingTop,
721
+ paddingRight: layout.paddingRight,
722
+ paddingBottom: layout.paddingBottom,
723
+ paddingLeft: layout.paddingLeft,
724
+ };
725
+
726
+ // A nested Pressable with no handler swallows the parent row press.
727
+ if (!onPress) {
728
+ return (
729
+ <View pointerEvents="none" style={buttonPadding}>
730
+ {renderIcon()}
731
+ </View>
732
+ );
733
+ }
734
+
639
735
  return (
640
736
  <Pressable
641
737
  onPress={(e: any) => {
642
738
  e?.stopPropagation?.();
643
- onPress?.();
739
+ onPress();
644
740
  }}
645
741
  style={({ pressed }) => [
742
+ buttonPadding,
646
743
  {
647
- paddingTop: layout.paddingTop,
648
- paddingRight: layout.paddingRight,
649
- paddingBottom: layout.paddingBottom,
650
- paddingLeft: layout.paddingLeft,
651
744
  opacity: pressed || focused ? 0.7 : 1,
652
745
  },
653
746
  ]}
@@ -666,6 +759,8 @@ type ItemButtonsProps = {
666
759
  onTrailingButtonPress?: () => void;
667
760
  isSelected?: boolean;
668
761
  renderHandle?: (element: React.ReactElement) => React.ReactElement;
762
+ checkboxAssets?: CheckboxAssetsConfiguration;
763
+ actionButtonAssets?: ActionButtonAssetsConfiguration;
669
764
  };
670
765
 
671
766
  function ItemButtons({
@@ -677,6 +772,8 @@ function ItemButtons({
677
772
  onTrailingButtonPress,
678
773
  isSelected = false,
679
774
  renderHandle,
775
+ checkboxAssets,
776
+ actionButtonAssets,
680
777
  }: ItemButtonsProps) {
681
778
  if (!leadingButton && !trailingButton) {
682
779
  return null;
@@ -691,6 +788,8 @@ function ItemButtons({
691
788
  focused={focusedLeadingButton}
692
789
  onPress={onLeadingButtonPress}
693
790
  isSelected={isSelected}
791
+ checkboxAssets={checkboxAssets}
792
+ actionButtonAssets={actionButtonAssets}
694
793
  />
695
794
  <View style={{ width: BUTTONS_CONTAINER_HORIZONTAL_GUTTER }} />
696
795
  </>
@@ -704,6 +803,8 @@ function ItemButtons({
704
803
  focused={focusedTrailingButton}
705
804
  onPress={onTrailingButtonPress}
706
805
  isSelected={isSelected}
806
+ checkboxAssets={checkboxAssets}
807
+ actionButtonAssets={actionButtonAssets}
707
808
  />
708
809
  )
709
810
  ) : (
@@ -712,6 +813,8 @@ function ItemButtons({
712
813
  focused={focusedTrailingButton}
713
814
  onPress={onTrailingButtonPress}
714
815
  isSelected={isSelected}
816
+ checkboxAssets={checkboxAssets}
817
+ actionButtonAssets={actionButtonAssets}
715
818
  />
716
819
  )
717
820
  ) : (
@@ -720,6 +823,8 @@ function ItemButtons({
720
823
  focused={focusedTrailingButton}
721
824
  onPress={onTrailingButtonPress}
722
825
  isSelected={isSelected}
826
+ checkboxAssets={checkboxAssets}
827
+ actionButtonAssets={actionButtonAssets}
723
828
  />
724
829
  )
725
830
  ) : null}
@@ -761,10 +866,16 @@ export function AudioPlayerTrackItem({
761
866
  textLabel2,
762
867
  nowPlayingLabel,
763
868
  nowPlayingAsset,
869
+ checkboxAssets,
870
+ actionButtonAssets,
764
871
  } = effectiveConfig;
765
872
 
766
873
  const container = resolveContainerSpec(image);
767
874
 
875
+ const handleTrailingButtonPress =
876
+ onTrailingButtonPress ??
877
+ (data.trailingButton === "multiSelect" ? onPress : undefined);
878
+
768
879
  return (
769
880
  <ItemContainer
770
881
  image={image}
@@ -777,22 +888,28 @@ export function AudioPlayerTrackItem({
777
888
  <View style={{ width: container.horizontalGutter }} />
778
889
  <View style={styles.contentColumn}>
779
890
  {showNowPlaying ? (
780
- <>
781
- <NowPlayingRow
782
- assetConfiguration={nowPlayingAsset}
783
- labelConfiguration={nowPlayingLabel}
784
- label={data.nowPlayingLabel}
785
- />
786
- <View style={{ height: container.verticalGutter }} />
787
- </>
891
+ <NowPlayingRow
892
+ assetConfiguration={nowPlayingAsset}
893
+ labelConfiguration={nowPlayingLabel}
894
+ label={data.nowPlayingLabel}
895
+ />
896
+ ) : null}
897
+ {showNowPlaying && (data.textLabel1 || data.textLabel2) ? (
898
+ <View style={{ height: container.verticalGutter }} />
899
+ ) : null}
900
+ {data.textLabel1 ? (
901
+ <ItemLabel
902
+ text={data.textLabel1}
903
+ configuration={textLabel1}
904
+ focused={focused}
905
+ />
906
+ ) : null}
907
+ {data.textLabel1 && data.textLabel2 ? (
908
+ <View style={{ height: container.verticalGutter }} />
909
+ ) : null}
910
+ {data.textLabel2 ? (
911
+ <ItemLabel text={data.textLabel2} configuration={textLabel2} />
788
912
  ) : null}
789
- <ItemLabel
790
- text={data.textLabel1}
791
- configuration={textLabel1}
792
- focused={focused}
793
- />
794
- <View style={{ height: container.verticalGutter }} />
795
- <ItemLabel text={data.textLabel2} configuration={textLabel2} />
796
913
  </View>
797
914
  <ItemButtons
798
915
  leadingButton={data.leadingButton}
@@ -800,9 +917,11 @@ export function AudioPlayerTrackItem({
800
917
  focusedLeadingButton={focusedLeadingButton}
801
918
  focusedTrailingButton={focusedTrailingButton}
802
919
  onLeadingButtonPress={onLeadingButtonPress}
803
- onTrailingButtonPress={onTrailingButtonPress}
920
+ onTrailingButtonPress={handleTrailingButtonPress}
804
921
  isSelected={data.isSelected}
805
922
  renderHandle={renderHandle}
923
+ checkboxAssets={checkboxAssets}
924
+ actionButtonAssets={actionButtonAssets}
806
925
  />
807
926
  </View>
808
927
  </ItemContainer>
@@ -819,6 +938,10 @@ export const VARIANT_NOW_PLAYING: ItemConfiguration = {
819
938
  defaultColor: "transparent",
820
939
  focusedColor: "transparent",
821
940
  cornerRadius: 0,
941
+ paddingTop: 8,
942
+ paddingRight: 20,
943
+ paddingBottom: 8,
944
+ paddingLeft: 20,
822
945
  },
823
946
  image: {
824
947
  width: 72,
@@ -839,6 +962,12 @@ export const VARIANT_QUEUED_ITEM: ItemConfiguration = {
839
962
  defaultColor: "transparent",
840
963
  focusedColor: "rgba(62, 62, 62, 1)",
841
964
  cornerRadius: 12,
965
+ paddingTop: 8,
966
+ paddingRight: 10,
967
+ paddingBottom: 8,
968
+ paddingLeft: 10,
969
+ highlightInsetLeft: 10,
970
+ highlightInsetRight: 10,
842
971
  },
843
972
  image: {
844
973
  width: 48,
@@ -859,6 +988,10 @@ export const VARIANT_PLAYLIST_ITEM: ItemConfiguration = {
859
988
  defaultColor: "transparent",
860
989
  focusedColor: "rgba(62, 62, 62, 1)",
861
990
  cornerRadius: 12,
991
+ paddingTop: 8,
992
+ paddingRight: 10,
993
+ paddingBottom: 8,
994
+ paddingLeft: 10,
862
995
  },
863
996
  image: {
864
997
  width: 48,
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { StyleSheet, View } from "react-native";
2
+ import { View } from "react-native";
3
3
  import {
4
4
  AudioPlayerTrackItem,
5
5
  VARIANT_NOW_PLAYING,
@@ -10,21 +10,16 @@ import type { MenuItem } from "@applicaster/zapp-react-native-utils/modalState/t
10
10
  import { usePlayer } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayer";
11
11
  import { usePlayerState } from "@applicaster/zapp-react-native-utils/appUtils/playerManager/usePlayerState";
12
12
 
13
- const styles = StyleSheet.create({
14
- divider: {
15
- height: StyleSheet.hairlineWidth,
16
- backgroundColor: "rgba(255, 255, 255, 0.1)",
17
- marginHorizontal: 20,
18
- marginVertical: 8,
19
- },
20
- });
21
-
22
13
  type NowPlayingHeaderSectionProps = {
23
14
  onClear?: () => void;
15
+ nextUpTitle?: string;
16
+ nowPlayingTitle?: string;
24
17
  };
25
18
 
26
19
  export function NowPlayingHeaderSection({
27
20
  onClear,
21
+ nextUpTitle = "Next Up",
22
+ nowPlayingTitle = "Now Playing",
28
23
  }: NowPlayingHeaderSectionProps) {
29
24
  const player = usePlayer();
30
25
  const playerState = usePlayerState("queue-now-playing-section");
@@ -53,7 +48,8 @@ export function NowPlayingHeaderSection({
53
48
 
54
49
  const data = mapMenuItemToNowPlayingData(
55
50
  nowPlayingItem,
56
- playerState?.isPaused
51
+ playerState?.isPaused,
52
+ nowPlayingTitle
57
53
  );
58
54
 
59
55
  const handlePlayPause = () => {
@@ -65,14 +61,11 @@ export function NowPlayingHeaderSection({
65
61
  };
66
62
 
67
63
  return (
68
- <View>
69
- <AudioPlayerTrackItem
70
- configuration={VARIANT_NOW_PLAYING}
71
- data={data}
72
- onTrailingButtonPress={handlePlayPause}
73
- />
74
- <View style={styles.divider} />
75
- </View>
64
+ <AudioPlayerTrackItem
65
+ configuration={VARIANT_NOW_PLAYING}
66
+ data={data}
67
+ onTrailingButtonPress={handlePlayPause}
68
+ />
76
69
  );
77
70
  };
78
71
 
@@ -81,7 +74,7 @@ export function NowPlayingHeaderSection({
81
74
  {renderNowPlayingTrack()}
82
75
  <AudioPlayerHeader
83
76
  configuration={VARIANT_SECTION_HEADER}
84
- data={{ title: "Next Up" }}
77
+ data={{ title: nextUpTitle }}
85
78
  onClear={onClear}
86
79
  />
87
80
  </View>
@@ -0,0 +1,113 @@
1
+ import React from "react";
2
+ import { Pressable } from "react-native";
3
+ import { fireEvent, render, screen } from "@testing-library/react-native";
4
+ import {
5
+ AudioPlayerTrackItem,
6
+ VARIANT_PLAYLIST_ITEM,
7
+ mapMenuItemToNowPlayingData,
8
+ } from "../Item";
9
+
10
+ const playlistItemData = {
11
+ textLabel1: "Queue",
12
+ textLabel2: "",
13
+ trailingButton: "multiSelect" as const,
14
+ isSelected: false,
15
+ };
16
+
17
+ describe("AudioPlayerTrackItem multiSelect checkbox", () => {
18
+ it("invokes onPress when the checkbox is pressed even if onTrailingButtonPress is omitted", () => {
19
+ const onPress = jest.fn();
20
+
21
+ render(
22
+ <AudioPlayerTrackItem
23
+ configuration={VARIANT_PLAYLIST_ITEM}
24
+ data={playlistItemData}
25
+ onPress={onPress}
26
+ />
27
+ );
28
+
29
+ const pressables = screen.UNSAFE_getAllByType(Pressable);
30
+ fireEvent.press(pressables[pressables.length - 1]);
31
+
32
+ expect(onPress).toHaveBeenCalledTimes(1);
33
+ });
34
+
35
+ it("invokes onTrailingButtonPress instead of onPress when a dedicated trailing handler is provided", () => {
36
+ const onPress = jest.fn();
37
+ const onTrailingButtonPress = jest.fn();
38
+
39
+ render(
40
+ <AudioPlayerTrackItem
41
+ configuration={VARIANT_PLAYLIST_ITEM}
42
+ data={playlistItemData}
43
+ onPress={onPress}
44
+ onTrailingButtonPress={onTrailingButtonPress}
45
+ />
46
+ );
47
+
48
+ const pressables = screen.UNSAFE_getAllByType(Pressable);
49
+ fireEvent.press(pressables[pressables.length - 1]);
50
+
51
+ expect(onTrailingButtonPress).toHaveBeenCalledTimes(1);
52
+ expect(onPress).not.toHaveBeenCalled();
53
+ });
54
+
55
+ it("invokes onPress when the row is pressed", () => {
56
+ const onPress = jest.fn();
57
+
58
+ render(
59
+ <AudioPlayerTrackItem
60
+ configuration={VARIANT_PLAYLIST_ITEM}
61
+ data={playlistItemData}
62
+ onPress={onPress}
63
+ />
64
+ );
65
+
66
+ fireEvent.press(screen.UNSAFE_getAllByType(Pressable)[0]);
67
+
68
+ expect(onPress).toHaveBeenCalledTimes(1);
69
+ });
70
+
71
+ it("renders only textLabel1 Text element when textLabel2 is missing or empty", () => {
72
+ const { UNSAFE_getAllByType, queryByText } = render(
73
+ <AudioPlayerTrackItem
74
+ configuration={VARIANT_PLAYLIST_ITEM}
75
+ data={{
76
+ textLabel1: "Only Title",
77
+ textLabel2: "",
78
+ }}
79
+ />
80
+ );
81
+
82
+ const { Text } = require("react-native");
83
+ const textNodes = UNSAFE_getAllByType(Text);
84
+ expect(textNodes).toHaveLength(1);
85
+ expect(queryByText("Only Title")).toBeTruthy();
86
+ });
87
+ });
88
+
89
+ describe("mapMenuItemToNowPlayingData", () => {
90
+ const item = {
91
+ id: "item_1",
92
+ title: "Track Title",
93
+ summary: "Artist Name",
94
+ icon: "https://example.com/cover.png",
95
+ };
96
+
97
+ it("uses default 'Now Playing' label when omitted", () => {
98
+ const data = mapMenuItemToNowPlayingData(item as any);
99
+ expect(data.nowPlayingLabel).toBe("Now Playing");
100
+ expect(data.textLabel1).toBe("Track Title");
101
+ expect(data.textLabel2).toBe("Artist Name");
102
+ });
103
+
104
+ it("uses custom nowPlayingLabel when provided", () => {
105
+ const data = mapMenuItemToNowPlayingData(
106
+ item as any,
107
+ false,
108
+ "En cours de lecture"
109
+ );
110
+
111
+ expect(data.nowPlayingLabel).toBe("En cours de lecture");
112
+ });
113
+ });
@@ -1,5 +1,3 @@
1
- export { AudioPlayerAction } from "./Action";
2
-
3
1
  export { AudioPlayerButton, VARIANT_WITH_ICON } from "./Button";
4
2
 
5
3
  export { AudioPlayerHeader } from "./Header";