@applicaster/zapp-react-native-ui-components 16.0.0-alpha.2851031376 → 16.0.0-alpha.2963644197

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 (141) 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/GeneralContentScreen/GeneralContentScreenHookAdapter.tsx +15 -0
  8. package/Components/GeneralContentScreen/hookAdapter/__tests__/skipExpression.test.ts +294 -0
  9. package/Components/GeneralContentScreen/hookAdapter/__tests__/validationHelper.test.ts +64 -0
  10. package/Components/GeneralContentScreen/hookAdapter/skipExpression.ts +252 -0
  11. package/Components/GeneralContentScreen/hookAdapter/validationHelper.ts +18 -0
  12. package/Components/Layout/FullWidthRow.tsx +38 -0
  13. package/Components/LinkHandler/LinkHandler.tsx +2 -7
  14. package/Components/MasterCell/DefaultComponents/ActionButton.tsx +5 -1
  15. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/__tests__/placement.test.ts +47 -21
  16. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
  17. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
  18. package/Components/MasterCell/DefaultComponents/ActionButtonsCore/placement.ts +34 -6
  19. package/Components/MasterCell/DefaultComponents/Button.tsx +1 -0
  20. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/__tests__/withDimensions.test.ts +420 -125
  21. package/Components/MasterCell/DefaultComponents/Image/hoc/utils/index.ts +114 -19
  22. package/Components/MasterCell/DefaultComponents/Image/hoc/withDimensions.tsx +19 -8
  23. package/Components/MasterCell/DefaultComponents/ImageContainer/index.tsx +5 -3
  24. package/Components/MasterCell/DefaultComponents/LiveImage/executePreloadHooks.ts +2 -4
  25. package/Components/MasterCell/DefaultComponents/LiveImage/index.tsx +2 -3
  26. package/Components/MasterCell/DefaultComponents/PressableView.tsx +29 -9
  27. package/Components/MasterCell/DefaultComponents/Text/hooks/useText.ts +4 -0
  28. package/Components/MasterCell/DefaultComponents/Toggle.tsx +36 -14
  29. package/Components/MasterCell/DefaultComponents/__tests__/Toggle.test.tsx +167 -0
  30. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/ActionButton.tsx +154 -100
  31. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/AssetComponent.tsx +15 -4
  32. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Button.ts +56 -22
  33. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/Spacer.ts +6 -4
  34. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/TextLabelsContainer.ts +3 -1
  35. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/PressableView.test.tsx +32 -21
  36. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/__tests__/index.test.ts +15 -4
  37. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/index.ts +23 -16
  38. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/__tests__/insertButtons.test.ts +8 -3
  39. package/Components/MasterCell/DefaultComponents/mobile/MobileActionButtons/utils/index.ts +2 -2
  40. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/__tests__/insertButtonsBetweenLabels.test.ts +45 -13
  41. package/Components/MasterCell/DefaultComponents/tv/TvActionButtons/utils/index.ts +2 -2
  42. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/__tests__/factory.test.ts +32 -0
  43. package/Components/MasterCell/SharedUI/CollapsibleTextContainer/index.ts +1 -2
  44. package/Components/MasterCell/contexts/PressedStateContext.ts +3 -0
  45. package/Components/MasterCell/dataAdapter.ts +15 -5
  46. package/Components/MasterCell/hooks/index.ts +2 -0
  47. package/Components/MasterCell/hooks/usePressedState.ts +4 -0
  48. package/Components/MasterCell/index.tsx +30 -5
  49. package/Components/MasterCell/utils/__tests__/resolveColor.test.js +169 -132
  50. package/Components/MasterCell/utils/__tests__/resolveColorForProp.test.js +92 -0
  51. package/Components/MasterCell/utils/index.ts +85 -46
  52. package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +56 -0
  53. package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
  54. package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +24 -0
  55. package/Components/ModalComponent/ActionsBottomSheet/index.ts +9 -0
  56. package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +61 -0
  57. package/Components/ModalComponent/AudioPlayer/Components/Button.tsx +392 -0
  58. package/Components/ModalComponent/AudioPlayer/Components/Header.tsx +772 -0
  59. package/Components/ModalComponent/AudioPlayer/Components/Input.tsx +728 -0
  60. package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +1007 -0
  61. package/Components/ModalComponent/AudioPlayer/Components/NowPlayingHeaderSection.tsx +205 -0
  62. package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +113 -0
  63. package/Components/ModalComponent/AudioPlayer/Components/__tests__/NowPlayingHeaderSection.test.tsx +36 -0
  64. package/Components/ModalComponent/AudioPlayer/Components/index.ts +9 -0
  65. package/Components/ModalComponent/AudioPlayer/utils/__tests__/mergeStyles.test.ts +356 -0
  66. package/Components/ModalComponent/AudioPlayer/utils/mergeStyles.ts +521 -0
  67. package/Components/ModalComponent/BottomSheetDragArea.tsx +33 -0
  68. package/Components/ModalComponent/BottomSheetModalContent.tsx +505 -50
  69. package/Components/ModalComponent/Button/index.tsx +5 -3
  70. package/Components/ModalComponent/Header/CloseButton.tsx +12 -0
  71. package/Components/ModalComponent/Header/__tests__/CloseButton.test.tsx +72 -0
  72. package/Components/ModalComponent/Header/index.tsx +20 -7
  73. package/Components/ModalComponent/Header/utils.ts +1 -1
  74. package/Components/ModalComponent/ModalBlocksStyleContext.tsx +10 -0
  75. package/Components/ModalComponent/SortableList.tsx +157 -0
  76. package/Components/ModalComponent/SortableList.web.tsx +34 -0
  77. package/Components/ModalComponent/TextInputContent.tsx +135 -0
  78. package/Components/ModalComponent/__tests__/BottomSheetDragArea.test.tsx +74 -0
  79. package/Components/ModalComponent/__tests__/BottomSheetModalContent.test.tsx +343 -0
  80. package/Components/ModalComponent/__tests__/SortableList.web.test.tsx +16 -0
  81. package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +169 -0
  82. package/Components/ModalComponent/__tests__/showTextInput.test.ts +95 -0
  83. package/Components/ModalComponent/index.tsx +18 -9
  84. package/Components/ModalComponent/presets/__tests__/getCell.test.ts +90 -0
  85. package/Components/ModalComponent/presets/dynamicCollectionCell.tsx +78 -0
  86. package/Components/ModalComponent/presets/index.ts +27 -0
  87. package/Components/ModalComponent/presets/selectableCell.tsx +28 -0
  88. package/Components/ModalComponent/presets/standardCell.tsx +41 -0
  89. package/Components/ModalComponent/presets/types.ts +35 -0
  90. package/Components/ModalComponent/showTextInput.ts +52 -0
  91. package/Components/ModalComponent/utils.ts +82 -21
  92. package/Components/OfflineHandler/__tests__/__snapshots__/index.test.tsx.snap +4 -85
  93. package/Components/OfflineHandler/__tests__/index.test.tsx +78 -18
  94. package/Components/OfflineHandler/hooks.ts +111 -0
  95. package/Components/OfflineHandler/index.tsx +51 -73
  96. package/Components/OfflineHandler/utils/index.ts +1 -13
  97. package/Components/PlayerContainer/PlayerContainer.tsx +18 -10
  98. package/Components/PlayerContainer/ProgramInfo/index.tsx +3 -4
  99. package/Components/PlayerContainer/__tests__/PlayerContainer.test.tsx +66 -13
  100. package/Components/PreloaderWrapper/index.tsx +1 -1
  101. package/Components/River/ComponentsMap/ComponentsMap.tsx +87 -61
  102. package/Components/River/River.tsx +4 -0
  103. package/Components/River/__tests__/__snapshots__/componentsMap.test.js.snap +140 -89
  104. package/Components/River/__tests__/componentsMap.test.js +8 -3
  105. package/Components/River/__tests__/resultCallback.test.tsx +47 -0
  106. package/Components/Screen/__tests__/Screen.test.tsx +24 -0
  107. package/Components/Screen/__tests__/__snapshots__/Screen.test.tsx.snap +2 -0
  108. package/Components/Screen/index.tsx +13 -4
  109. package/Components/Screen/navigationHandler.ts +8 -8
  110. package/Components/ScreenFeedLoader/ScreenFeedLoader.tsx +5 -3
  111. package/Components/ScreenResolverFeedProvider/ScreenResolverFeedProvider.tsx +1 -1
  112. package/Components/ScreenRevealManager/ScreenRevealManager.ts +18 -2
  113. package/Components/ScreenRevealManager/__tests__/ScreenRevealManager.test.ts +36 -0
  114. package/Components/ScreenRevealManager/__tests__/withScreenRevealManager.test.tsx +56 -0
  115. package/Components/ScreenRevealManager/withScreenRevealManager.tsx +21 -4
  116. package/Components/TopCutoffOverlay/index.tsx +2 -2
  117. package/Components/Transitioner/Transitioner.tsx +28 -17
  118. package/Components/VideoLive/LiveImageManager.ts +9 -3
  119. package/Components/VideoLive/__tests__/LiveImageManager.test.ts +59 -0
  120. package/Components/ZappFrameworkComponents/BarView/BarView.tsx +12 -5
  121. package/Components/ZappFrameworkComponents/BarView/__tests__/BarView.test.tsx +2 -2
  122. package/Contexts/CachedDimensionsContext/__tests__/index.test.ts +154 -0
  123. package/Contexts/CachedDimensionsContext/index.ts +17 -2
  124. package/Contexts/ModalNavigationContext/__tests__/index.test.tsx +15 -0
  125. package/Contexts/ModalNavigationContext/index.tsx +2 -2
  126. package/Helpers/ComponentCellSelectionHelper/index.js +0 -6
  127. package/Helpers/index.js +0 -39
  128. package/Hooks/useEntryActionsExpander.ts +63 -0
  129. package/assets/empty-Info.png +0 -0
  130. package/package.json +6 -5
  131. package/Components/OfflineHandler/NotificationView/NotificationView.lg.tsx +0 -112
  132. package/Components/OfflineHandler/NotificationView/NotificationView.samsung.tsx +0 -107
  133. package/Components/OfflineHandler/NotificationView/NotificationView.tsx +0 -153
  134. package/Components/OfflineHandler/NotificationView/__tests__/index.test.tsx +0 -66
  135. package/Components/OfflineHandler/NotificationView/utils.ts +0 -34
  136. package/Components/Screen/utils.ts +0 -16
  137. package/Helpers/Analytics/index.js +0 -95
  138. /package/Components/BackgroundImage/{BackgroundImage.tv.android.tsx → BackgroundImage.android.tv.tsx} +0 -0
  139. /package/Components/BackgroundImage/{BackgroundImage.tv.ios.tsx → BackgroundImage.ios.tv.tsx} +0 -0
  140. /package/Components/MasterCell/DefaultComponents/ButtonContainerView/{index.tv.android.tsx → index.android.tv.tsx} +0 -0
  141. /package/Decorators/ZappPipesDataConnector/__tests__/{Hero.js → Hero.tsx} +0 -0
@@ -0,0 +1,72 @@
1
+ import React from "react";
2
+ import { fireEvent, render, screen } from "@testing-library/react-native";
3
+ import { CloseButton, CloseButtonProps } from "../CloseButton";
4
+
5
+ jest.mock("@applicaster/zapp-react-native-ui-components/Components", () => {
6
+ const { Image } = require("react-native");
7
+
8
+ return { QBImage: Image };
9
+ });
10
+
11
+ const mockGetButtonA11yProps = jest.fn(() => ({
12
+ accessible: true,
13
+ accessibilityLabel: "Close",
14
+ accessibilityHint: "Closes the sheet",
15
+ accessibilityRole: "button",
16
+ }));
17
+
18
+ jest.mock(
19
+ "@applicaster/zapp-react-native-utils/appUtils/accessibilityManager",
20
+ () => ({
21
+ AccessibilityManager: {
22
+ getInstance: () => ({
23
+ getButtonAccessibilityProps: mockGetButtonA11yProps,
24
+ }),
25
+ },
26
+ })
27
+ );
28
+
29
+ const baseProps: CloseButtonProps = {
30
+ enabled: true,
31
+ close: jest.fn(),
32
+ asset: "close.png",
33
+ assetFocused: "close_focused.png",
34
+ height: 24,
35
+ width: 24,
36
+ marginTop: 0,
37
+ marginBottom: 0,
38
+ marginLeft: 0,
39
+ marginRight: 0,
40
+ };
41
+
42
+ describe("ModalComponent Header CloseButton", () => {
43
+ beforeEach(() => {
44
+ jest.clearAllMocks();
45
+ });
46
+
47
+ it("renders as an accessible button reachable by screen readers", () => {
48
+ render(<CloseButton {...baseProps} />);
49
+
50
+ const button = screen.getByTestId("closeButton");
51
+
52
+ expect(button.props.accessible).toBe(true);
53
+ expect(button.props.accessibilityRole).toBe("button");
54
+ expect(button.props.accessibilityLabel).toBe("Close");
55
+ expect(mockGetButtonA11yProps).toHaveBeenCalledWith("close");
56
+ });
57
+
58
+ it("invokes close on press", () => {
59
+ const close = jest.fn();
60
+ render(<CloseButton {...baseProps} close={close} />);
61
+
62
+ fireEvent.press(screen.getByTestId("closeButton"));
63
+
64
+ expect(close).toHaveBeenCalledTimes(1);
65
+ });
66
+
67
+ it("renders nothing when disabled", () => {
68
+ render(<CloseButton {...baseProps} enabled={false} />);
69
+
70
+ expect(screen.queryByTestId("closeButton")).toBeNull();
71
+ });
72
+ });
@@ -4,6 +4,7 @@ import { Title, TitleProps } from "./Title";
4
4
  import { CloseButton, CloseButtonProps } from "./CloseButton";
5
5
  import { getCloseButtonProps, getTitleProps } from "./utils";
6
6
  import { PluginConfiguration } from "../index";
7
+ import { useModalBlocksStyle } from "../ModalBlocksStyleContext";
7
8
 
8
9
  type Props = {
9
10
  dismiss: () => void;
@@ -28,6 +29,14 @@ const styles = StyleSheet.create({
28
29
 
29
30
  export function ModalHeader(props: Props) {
30
31
  const { configuration, dismiss, onLayout, summary, title } = props;
32
+ const modalBlocksStyle = useModalBlocksStyle();
33
+ const headerBlock = modalBlocksStyle?.header;
34
+
35
+ const containerStyle = useMemo(() => {
36
+ const bg = headerBlock?.container?.default?.backgroundColor;
37
+
38
+ return [styles.container, bg ? { backgroundColor: bg } : null];
39
+ }, [headerBlock?.container?.default?.backgroundColor]);
31
40
 
32
41
  const closeButtonProps = useMemo<CloseButtonProps>(
33
42
  () => ({
@@ -37,13 +46,17 @@ export function ModalHeader(props: Props) {
37
46
  [configuration, dismiss]
38
47
  );
39
48
 
40
- const titleProps = useMemo<TitleProps>(
41
- () => ({
42
- ...getTitleProps(configuration, "title"),
49
+ const titleProps = useMemo<TitleProps>(() => {
50
+ const baseProps = getTitleProps(configuration, "title");
51
+ const titleOverride = headerBlock?.title?.default;
52
+
53
+ return {
54
+ ...baseProps,
43
55
  text: title,
44
- }),
45
- [configuration, title]
46
- );
56
+ ...(titleOverride?.fontColor ? { color: titleOverride.fontColor } : {}),
57
+ ...(titleOverride?.fontSize ? { fontSize: titleOverride.fontSize } : {}),
58
+ };
59
+ }, [configuration, title, headerBlock?.title?.default]);
47
60
 
48
61
  const summaryProps = useMemo<TitleProps>(
49
62
  () => ({
@@ -59,7 +72,7 @@ export function ModalHeader(props: Props) {
59
72
  configuration.modal_bottom_sheet_close_margin_right;
60
73
 
61
74
  return (
62
- <View onLayout={onLayout} style={styles.container}>
75
+ <View onLayout={onLayout} style={containerStyle}>
63
76
  <View style={styles.flex}>
64
77
  {title ? <Title {...titleProps} /> : null}
65
78
  {summary ? <Title {...summaryProps} /> : null}
@@ -18,7 +18,7 @@ export function getCloseButtonProps(config: PluginConfiguration) {
18
18
  return {
19
19
  asset: modal_bottom_sheet_close_asset,
20
20
  assetFocused: modal_bottom_sheet_close_focus_asset,
21
- enabled: modal_bottom_sheet_close_switch,
21
+ enabled: modal_bottom_sheet_close_switch !== false,
22
22
  width: modal_bottom_sheet_close_width,
23
23
  height: modal_bottom_sheet_close_height,
24
24
  marginTop: modal_bottom_sheet_close_margin_top,
@@ -0,0 +1,10 @@
1
+ import React, { useContext } from "react";
2
+ import type { ModalBlocksConfig } from "@applicaster/zapp-react-native-utils/configurationUtils/modalBlocksParser";
3
+
4
+ export const ModalBlocksStyleContext = React.createContext<
5
+ ModalBlocksConfig | Record<string, any> | undefined
6
+ >(undefined);
7
+
8
+ export const useModalBlocksStyle = () => {
9
+ return useContext(ModalBlocksStyleContext);
10
+ };
@@ -0,0 +1,157 @@
1
+ import React, { useCallback, useEffect, useMemo, useState } from "react";
2
+ import Sortable from "react-native-sortables";
3
+ import { Text, View, ViewStyle } from "react-native";
4
+ import Animated, { useAnimatedRef } from "react-native-reanimated";
5
+
6
+ const DEFAULT_HEADERS: any[] = [];
7
+ const DEFAULT_SORTABLE_DATA: any[] = [];
8
+
9
+ const renderHeaderComponent = (component: any) => {
10
+ if (!component) return null;
11
+ if (React.isValidElement(component)) return component;
12
+
13
+ if (typeof component === "function") {
14
+ const HeaderComponent = component;
15
+
16
+ return <HeaderComponent />;
17
+ }
18
+
19
+ return null;
20
+ };
21
+
22
+ const SortableListHeader = ({
23
+ title,
24
+ style,
25
+ }: {
26
+ title?: string;
27
+ style?: ViewStyle;
28
+ }) => {
29
+ const displayTitle = typeof title === "string" ? title : String(title ?? "");
30
+
31
+ return (
32
+ <View style={style}>
33
+ {displayTitle ? <Text>{displayTitle}</Text> : null}
34
+ </View>
35
+ );
36
+ };
37
+
38
+ export type SortableListProps = {
39
+ scrollViewStyle?: ViewStyle;
40
+ contentContainerStyle?: ViewStyle;
41
+ itemStyle?: ViewStyle;
42
+ headerStyle?: ViewStyle;
43
+ columns?: number;
44
+ rowGap?: number;
45
+ columnGap?: number;
46
+ spacing?: number;
47
+ keyExtractor?: (item: any, index?: number) => string;
48
+ renderItem: ({
49
+ item,
50
+ index,
51
+ renderHandle,
52
+ }: {
53
+ item: any;
54
+ index: number;
55
+ renderHandle: (children: React.ReactElement) => React.ReactElement;
56
+ }) => React.ReactNode;
57
+ sortableData?: any[];
58
+ headers?: { index: number; title?: string; component: any }[];
59
+ onDragEnd?: (params: {
60
+ fromIndex?: number;
61
+ toIndex?: number;
62
+ from?: number;
63
+ to?: number;
64
+ data: any[];
65
+ }) => void;
66
+ };
67
+
68
+ const defaultKeyExtractor = (item, index) =>
69
+ `sortable-item-${item?.id ?? item?.title ?? index}`;
70
+
71
+ export const SortableList = ({
72
+ scrollViewStyle,
73
+ contentContainerStyle,
74
+ itemStyle,
75
+ headerStyle,
76
+ columns = 1,
77
+ rowGap = 0,
78
+ columnGap = 0,
79
+ keyExtractor = defaultKeyExtractor,
80
+ renderItem,
81
+ sortableData = DEFAULT_SORTABLE_DATA,
82
+ headers = DEFAULT_HEADERS,
83
+ onDragEnd,
84
+ }: SortableListProps) => {
85
+ const scrollableRef = useAnimatedRef<Animated.ScrollView>();
86
+
87
+ const [localData, setLocalData] = useState(sortableData);
88
+
89
+ useEffect(() => {
90
+ setLocalData((localData) =>
91
+ localData !== sortableData ? sortableData : localData
92
+ );
93
+ }, [sortableData]);
94
+
95
+ const handleDragEnd = useCallback(
96
+ (params: any) => {
97
+ if (params?.data) {
98
+ setLocalData(params.data);
99
+ }
100
+
101
+ if (onDragEnd) {
102
+ onDragEnd(params);
103
+ }
104
+ },
105
+ [onDragEnd]
106
+ );
107
+
108
+ const _renderItem = useCallback(
109
+ ({ item, index }) => (
110
+ <View key={keyExtractor(item, index)} style={itemStyle}>
111
+ {renderItem({
112
+ item,
113
+ index,
114
+ renderHandle: (children) => (
115
+ <Sortable.Handle>{children}</Sortable.Handle>
116
+ ),
117
+ })}
118
+ </View>
119
+ ),
120
+ [itemStyle, keyExtractor, renderItem]
121
+ );
122
+
123
+ const renderHeader = useMemo(
124
+ () =>
125
+ (headers || []).map((header, idx) => (
126
+ <View key={`static-header-${idx}`} style={headerStyle}>
127
+ {header.title ? (
128
+ <SortableListHeader title={header.title} />
129
+ ) : (
130
+ renderHeaderComponent(header.component)
131
+ )}
132
+ </View>
133
+ )),
134
+ [headers, headerStyle]
135
+ );
136
+
137
+ return (
138
+ <Animated.ScrollView
139
+ ref={scrollableRef}
140
+ style={scrollViewStyle}
141
+ contentContainerStyle={contentContainerStyle}
142
+ >
143
+ {renderHeader}
144
+ <Sortable.Grid
145
+ scrollableRef={scrollableRef}
146
+ customHandle
147
+ columns={columns}
148
+ rowGap={rowGap}
149
+ columnGap={columnGap}
150
+ data={localData}
151
+ keyExtractor={keyExtractor}
152
+ onDragEnd={handleDragEnd}
153
+ renderItem={_renderItem}
154
+ />
155
+ </Animated.ScrollView>
156
+ );
157
+ };
@@ -0,0 +1,34 @@
1
+ import React from "react";
2
+ import type { ViewStyle } from "react-native";
3
+
4
+ export type SortableListProps = {
5
+ scrollViewStyle?: ViewStyle;
6
+ contentContainerStyle?: ViewStyle;
7
+ itemStyle?: ViewStyle;
8
+ headerStyle?: ViewStyle;
9
+ columns?: number;
10
+ rowGap?: number;
11
+ columnGap?: number;
12
+ spacing?: number;
13
+ keyExtractor?: (item: any, index?: number) => string;
14
+ renderItem: ({
15
+ item,
16
+ index,
17
+ renderHandle,
18
+ }: {
19
+ item: any;
20
+ index: number;
21
+ renderHandle: (children: React.ReactElement) => React.ReactElement;
22
+ }) => React.ReactNode;
23
+ sortableData?: any[];
24
+ headers?: { index: number; title?: string; component: any }[];
25
+ onDragEnd?: (params: {
26
+ fromIndex?: number;
27
+ toIndex?: number;
28
+ from?: number;
29
+ to?: number;
30
+ data: any[];
31
+ }) => void;
32
+ };
33
+
34
+ export const SortableList = (_props: SortableListProps) => null;
@@ -0,0 +1,135 @@
1
+ import React, { useCallback, useState } from "react";
2
+ import { StyleSheet, View } from "react-native";
3
+ import { actionExecutor } from "@applicaster/zapp-react-native-utils/actionsExecutor/ActionExecutor";
4
+ import {
5
+ AudioPlayerInput,
6
+ VARIANT_NAME_PLAYLIST_INPUT,
7
+ } from "./AudioPlayer/Components/Input";
8
+ import {
9
+ AudioPlayerButton,
10
+ VARIANT_TEXT_ONLY,
11
+ } from "./AudioPlayer/Components/Button";
12
+ import { logger } from "@applicaster/zapp-react-native-utils/logger";
13
+
14
+ export type TextInputContentProps = {
15
+ /** Injected by the sheet host; unused for layout (header is owned by ModalComponent). */
16
+ dismiss?: () => void;
17
+ width?: number;
18
+ maxHeight?: number;
19
+ currentRoute?: boolean;
20
+ inputLabel?: string;
21
+ defaultValue?: string;
22
+ buttonLabel: string;
23
+ validationLabel?: string;
24
+ maxLength?: number;
25
+ actions: ActionType[];
26
+ actionContext?: Record<string, any>;
27
+ };
28
+
29
+ // Spacing mirrors the `component_style_*` keys in text input component.
30
+ const styles = StyleSheet.create({
31
+ container: {
32
+ backgroundColor: "transparent", // component_style_background_color
33
+ paddingLeft: 40, // component_style_padding_left
34
+ paddingRight: 40, // component_style_padding_right
35
+ paddingTop: 0, // component_style_padding_top
36
+ paddingBottom: 0, // component_style_padding_bottom
37
+ marginTop: 20, // component_style_margin_top
38
+ marginBottom: 20, // component_style_margin_bottom
39
+ },
40
+ inputWrap: {
41
+ // Gap down to the submit button; owned by the modal, not the reference.
42
+ marginBottom: 24,
43
+ },
44
+ });
45
+
46
+ /**
47
+ * Modal body only — input + submit.
48
+ * Standard dismiss header comes from BottomSheetModalContent / ModalHeader.
49
+ */
50
+ export function TextInputContent(props: TextInputContentProps) {
51
+ const {
52
+ inputLabel,
53
+ defaultValue = "",
54
+ buttonLabel,
55
+ actions,
56
+ actionContext,
57
+ maxLength = 255,
58
+ validationLabel,
59
+ } = props;
60
+
61
+ const [textValue, setTextValue] = useState(defaultValue);
62
+ const [submitting, setSubmitting] = useState(false);
63
+
64
+ const validationMessage =
65
+ textValue.length > maxLength
66
+ ? validationLabel || "Too many characters"
67
+ : undefined;
68
+
69
+ if (!validationLabel) {
70
+ logger.warn(
71
+ "Modal/TextInputContent: Validation label (validationLabel) is not provided, using default message 'Too many characters'."
72
+ );
73
+ }
74
+
75
+ const onSubmit = useCallback(async () => {
76
+ if (submitting) {
77
+ return;
78
+ }
79
+
80
+ setSubmitting(true);
81
+
82
+ try {
83
+ const updatedActions = (actions || []).map((item) => {
84
+ if (item.type === "sendCloudEvent" || item.options?.data) {
85
+ return {
86
+ ...item,
87
+ options: {
88
+ ...item.options,
89
+ data: {
90
+ ...(item.options?.data || {}),
91
+ name: textValue,
92
+ },
93
+ },
94
+ };
95
+ }
96
+
97
+ return item;
98
+ });
99
+
100
+ if (actionContext) {
101
+ await actionExecutor.handleActions(updatedActions, actionContext);
102
+ } else {
103
+ await actionExecutor.handleActions(updatedActions);
104
+ }
105
+
106
+ await actionExecutor.handleAction({ type: "dismissBottomSheet" });
107
+ } finally {
108
+ setSubmitting(false);
109
+ }
110
+ }, [submitting, actions, textValue, actionContext]);
111
+
112
+ return (
113
+ <View style={styles.container}>
114
+ <View style={styles.inputWrap}>
115
+ <AudioPlayerInput
116
+ configuration={VARIANT_NAME_PLAYLIST_INPUT}
117
+ data={{
118
+ placeholder: inputLabel || "",
119
+ label: inputLabel,
120
+ }}
121
+ value={textValue}
122
+ validationMessage={validationMessage}
123
+ onChangeText={setTextValue}
124
+ onClearPress={() => setTextValue("")}
125
+ />
126
+ </View>
127
+ <AudioPlayerButton
128
+ configuration={VARIANT_TEXT_ONLY}
129
+ data={{ title: buttonLabel }}
130
+ onPress={onSubmit}
131
+ disabled={!!validationMessage || submitting}
132
+ />
133
+ </View>
134
+ );
135
+ }
@@ -0,0 +1,74 @@
1
+ import React from "react";
2
+ import { Text } from "react-native";
3
+ import { render, screen } from "@testing-library/react-native";
4
+ import { BottomSheetDragArea } from "../BottomSheetDragArea";
5
+ import { BottomSheetContext } from "@applicaster/zapp-react-native-utils/bottomSheet";
6
+
7
+ // Reveal PanGestureHandler and capture its props.
8
+ jest.mock("react-native-gesture-handler", () => {
9
+ const { View } = require("react-native");
10
+ const actual = jest.requireActual("react-native-gesture-handler");
11
+
12
+ return {
13
+ __esModule: true,
14
+ ...actual,
15
+ PanGestureHandler: ({
16
+ children,
17
+ onGestureEvent,
18
+ onHandlerStateChange,
19
+ simultaneousHandlers,
20
+ }: any) => (
21
+ <View
22
+ testID="drag-pan"
23
+ onGestureEvent={onGestureEvent}
24
+ onHandlerStateChange={onHandlerStateChange}
25
+ simultaneousHandlers={simultaneousHandlers}
26
+ >
27
+ {children}
28
+ </View>
29
+ ),
30
+ };
31
+ });
32
+
33
+ const makeValue = () => ({
34
+ scrollRef: React.createRef<any>(),
35
+ masterDrawerRef: React.createRef<any>(),
36
+ drawerRef: React.createRef<any>(),
37
+ onScroll: jest.fn(),
38
+ setBodyDragEnabled: jest.fn(),
39
+ onDragGestureEvent: jest.fn(),
40
+ onDragStateChange: jest.fn(),
41
+ });
42
+
43
+ describe("BottomSheetDragArea", () => {
44
+ it("wraps children in a sheet-wired PanGestureHandler when inside a sheet", () => {
45
+ const value = makeValue();
46
+
47
+ render(
48
+ <BottomSheetContext.Provider value={value}>
49
+ <BottomSheetDragArea>
50
+ <Text testID="child">hi</Text>
51
+ </BottomSheetDragArea>
52
+ </BottomSheetContext.Provider>
53
+ );
54
+
55
+ const pan = screen.getByTestId("drag-pan");
56
+ expect(pan.props.onGestureEvent).toBe(value.onDragGestureEvent);
57
+ expect(pan.props.onHandlerStateChange).toBe(value.onDragStateChange);
58
+
59
+ expect(pan.props.simultaneousHandlers).toEqual([value.masterDrawerRef]);
60
+
61
+ expect(screen.getByTestId("child")).toBeTruthy();
62
+ });
63
+
64
+ it("renders children bare when used outside a sheet", () => {
65
+ render(
66
+ <BottomSheetDragArea>
67
+ <Text testID="child">hi</Text>
68
+ </BottomSheetDragArea>
69
+ );
70
+
71
+ expect(screen.queryByTestId("drag-pan")).toBeNull();
72
+ expect(screen.getByTestId("child")).toBeTruthy();
73
+ });
74
+ });