@applicaster/zapp-react-native-ui-components 16.0.0-rc.83 → 16.0.0-rc.85
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/MasterCell/DefaultComponents/ActionButton.tsx +4 -1
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/ActionButtonController.tsx +19 -4
- package/Components/MasterCell/DefaultComponents/ActionButtonsCore/components/__tests__/ActionButtonController.test.tsx +43 -1
- package/Components/ModalComponent/ActionsBottomSheet/ActionButton.tsx +4 -2
- package/Components/ModalComponent/ActionsBottomSheet/__tests__/openActionsBottomSheet.test.ts +38 -0
- package/Components/ModalComponent/ActionsBottomSheet/boundActionButton.tsx +5 -2
- package/Components/ModalComponent/ActionsBottomSheet/openActionsBottomSheet.ts +4 -1
- package/Components/ModalComponent/AudioPlayer/Components/Item.tsx +23 -6
- package/Components/ModalComponent/AudioPlayer/Components/__tests__/Item.test.tsx +66 -0
- package/Components/ModalComponent/TextInputContent.tsx +15 -3
- package/Components/ModalComponent/__tests__/TextInputContent.test.tsx +36 -0
- package/Components/ModalComponent/presets/__tests__/getCell.test.ts +24 -0
- package/Components/ModalComponent/presets/selectableCell.tsx +1 -1
- package/package.json +5 -5
|
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect } from "react";
|
|
|
2
2
|
import { TouchableOpacity, ViewStyle } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
+
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
5
6
|
|
|
6
7
|
import Image from "./Image";
|
|
7
8
|
type Props = {
|
|
@@ -57,6 +58,7 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
57
58
|
) {
|
|
58
59
|
const { item, action, asset, flavour = "flavour_1", cellUUID } = props;
|
|
59
60
|
const actionContext = useActions(action?.identifier);
|
|
61
|
+
const component = useUIComponentContext();
|
|
60
62
|
|
|
61
63
|
// TODO: add subscription API for action availability
|
|
62
64
|
const actionDisabled =
|
|
@@ -81,8 +83,9 @@ export const ActionButton = React.memo(function ActionButtonComponent(
|
|
|
81
83
|
updateState: (state) => {
|
|
82
84
|
setActionState(state);
|
|
83
85
|
},
|
|
86
|
+
...(component ? { component } : {}),
|
|
84
87
|
});
|
|
85
|
-
}, [actionState, actionContext?.state, item
|
|
88
|
+
}, [actionState, actionContext?.state, item, component]);
|
|
86
89
|
|
|
87
90
|
useEffect(() => {
|
|
88
91
|
if (typeof actionContext?.addListener === "function") {
|
|
@@ -6,6 +6,7 @@ import React, {
|
|
|
6
6
|
useState,
|
|
7
7
|
} from "react";
|
|
8
8
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
9
|
+
import { useUIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
9
10
|
|
|
10
11
|
type ActionDefinition = {
|
|
11
12
|
identifier?: string;
|
|
@@ -61,6 +62,7 @@ export function ActionButtonController({
|
|
|
61
62
|
const resolvedEntry = entry;
|
|
62
63
|
const resolvedIdentifier = action?.identifier || pluginIdentifier || "";
|
|
63
64
|
const actionContext = useActions(resolvedIdentifier);
|
|
65
|
+
const component = useUIComponentContext();
|
|
64
66
|
|
|
65
67
|
const actionDisabled =
|
|
66
68
|
typeof actionContext?.isActionAvailable === "function" &&
|
|
@@ -128,20 +130,33 @@ export function ActionButtonController({
|
|
|
128
130
|
return;
|
|
129
131
|
}
|
|
130
132
|
|
|
133
|
+
const invokeOptions: InvokeArgsOptions = {
|
|
134
|
+
...(component ? { component } : {}),
|
|
135
|
+
};
|
|
136
|
+
|
|
131
137
|
if (supportsEntryState) {
|
|
132
138
|
return actionContext.invokeAction?.(resolvedEntry, {
|
|
139
|
+
...invokeOptions,
|
|
133
140
|
updateState: setActionState,
|
|
134
141
|
});
|
|
135
142
|
}
|
|
136
143
|
|
|
144
|
+
if (typeof actionContext.invokeAction === "function") {
|
|
145
|
+
return actionContext.invokeAction(resolvedEntry, invokeOptions);
|
|
146
|
+
}
|
|
147
|
+
|
|
137
148
|
const favouritesAction = legacySelected
|
|
138
149
|
? actionContext.removeFavourite
|
|
139
150
|
: actionContext.addFavourite;
|
|
140
151
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
152
|
+
return favouritesAction?.(resolvedEntry);
|
|
153
|
+
}, [
|
|
154
|
+
actionContext,
|
|
155
|
+
supportsEntryState,
|
|
156
|
+
resolvedEntry,
|
|
157
|
+
legacySelected,
|
|
158
|
+
component,
|
|
159
|
+
]);
|
|
145
160
|
|
|
146
161
|
if (!actionContext || actionDisabled) {
|
|
147
162
|
if (!actionContext) {
|
|
@@ -2,6 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { Text } from "react-native";
|
|
3
3
|
import { render, fireEvent } from "@testing-library/react-native";
|
|
4
4
|
import { useActions } from "@applicaster/zapp-react-native-utils/reactHooks/actions";
|
|
5
|
+
import { UIComponentContext } from "@applicaster/zapp-react-native-ui-components/Contexts/UIComponentContext";
|
|
5
6
|
|
|
6
7
|
import { ActionButtonController } from "../ActionButtonController";
|
|
7
8
|
|
|
@@ -89,6 +90,47 @@ describe("ActionButtonController", () => {
|
|
|
89
90
|
);
|
|
90
91
|
});
|
|
91
92
|
|
|
93
|
+
it("passes the List UIComponentContext into invokeAction so the sheet can refresh it", () => {
|
|
94
|
+
const listComponent = {
|
|
95
|
+
id: "list-1",
|
|
96
|
+
data: { source: "https://example.com/user/collections/abc" },
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const actionContext = {
|
|
100
|
+
isActionAvailable: jest.fn(() => true),
|
|
101
|
+
initialEntryState: jest.fn(() => ({ active: false })),
|
|
102
|
+
invokeAction: jest.fn(),
|
|
103
|
+
addListener: jest.fn(() => jest.fn()),
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
mockUseActions.mockReturnValue(actionContext);
|
|
107
|
+
|
|
108
|
+
const { getByTestId } = render(
|
|
109
|
+
<UIComponentContext.Provider value={listComponent}>
|
|
110
|
+
<ActionButtonController
|
|
111
|
+
action={{ identifier: "open-modal-bottom-sheet-cell-action" }}
|
|
112
|
+
entry={entry}
|
|
113
|
+
>
|
|
114
|
+
{({ onPress }) => (
|
|
115
|
+
<Text testID="content" onPress={onPress}>
|
|
116
|
+
press
|
|
117
|
+
</Text>
|
|
118
|
+
)}
|
|
119
|
+
</ActionButtonController>
|
|
120
|
+
</UIComponentContext.Provider>
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
fireEvent.press(getByTestId("content"));
|
|
124
|
+
|
|
125
|
+
expect(actionContext.invokeAction).toHaveBeenCalledWith(
|
|
126
|
+
entry,
|
|
127
|
+
expect.objectContaining({
|
|
128
|
+
updateState: expect.any(Function),
|
|
129
|
+
component: listComponent,
|
|
130
|
+
})
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
|
|
92
134
|
it("updates state from addListener", () => {
|
|
93
135
|
let listener;
|
|
94
136
|
|
|
@@ -328,7 +370,7 @@ describe("ActionButtonController", () => {
|
|
|
328
370
|
|
|
329
371
|
fireEvent.press(getByTestId("content"));
|
|
330
372
|
|
|
331
|
-
expect(invokeAction).toHaveBeenCalledWith(entry);
|
|
373
|
+
expect(invokeAction).toHaveBeenCalledWith(entry, {});
|
|
332
374
|
expect(addFavourite).not.toHaveBeenCalled();
|
|
333
375
|
expect(removeFavourite).not.toHaveBeenCalled();
|
|
334
376
|
});
|
|
@@ -12,6 +12,7 @@ export type ActionButtonProps = {
|
|
|
12
12
|
item: RegisteredAction;
|
|
13
13
|
configuration: any;
|
|
14
14
|
width: number;
|
|
15
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent };
|
|
15
16
|
};
|
|
16
17
|
|
|
17
18
|
type Props = ActionButtonProps & {
|
|
@@ -27,7 +28,7 @@ type Props = ActionButtonProps & {
|
|
|
27
28
|
* (backed by the action's observable / `addListener`).
|
|
28
29
|
*/
|
|
29
30
|
export function ActionButton(props: Props) {
|
|
30
|
-
const { item, entry, onPress: onItemPress } = props;
|
|
31
|
+
const { item, entry, onPress: onItemPress, component } = props;
|
|
31
32
|
|
|
32
33
|
const action = item?.action;
|
|
33
34
|
|
|
@@ -38,9 +39,10 @@ export function ActionButton(props: Props) {
|
|
|
38
39
|
invokeAction({
|
|
39
40
|
context: "toast",
|
|
40
41
|
dismiss: () => onItemPress?.(item),
|
|
42
|
+
...(component ? { component } : {}),
|
|
41
43
|
});
|
|
42
44
|
},
|
|
43
|
-
[invokeAction, item, onItemPress]
|
|
45
|
+
[invokeAction, item, onItemPress, component]
|
|
44
46
|
);
|
|
45
47
|
|
|
46
48
|
if (!isActionAvailableFor(action, entry as ZappEntry)) return null;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { openBottomSheetModal } from "@applicaster/zapp-react-native-utils/modalState";
|
|
2
|
+
import { boundActionButton } from "../boundActionButton";
|
|
3
|
+
import { openActionsBottomSheet } from "../openActionsBottomSheet";
|
|
4
|
+
|
|
5
|
+
jest.mock("@applicaster/zapp-react-native-utils/modalState", () => ({
|
|
6
|
+
openBottomSheetModal: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
jest.mock("../boundActionButton", () => ({
|
|
10
|
+
boundActionButton: jest.fn(() => jest.fn()),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe("openActionsBottomSheet", () => {
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("binds the List component into the sheet row button", () => {
|
|
19
|
+
const entry = { id: "entry-1" } as ZappEntry;
|
|
20
|
+
|
|
21
|
+
const component = {
|
|
22
|
+
data: { source: "https://example.com/user/collections/abc" },
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const actions = [
|
|
26
|
+
{ identifier: "entry_action_0", action: { invokeAction: jest.fn() } },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
openActionsBottomSheet({
|
|
30
|
+
entry,
|
|
31
|
+
actions: actions as any,
|
|
32
|
+
component: component as any,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(boundActionButton).toHaveBeenCalledWith(entry, component);
|
|
36
|
+
expect(openBottomSheetModal).toHaveBeenCalledTimes(1);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -8,9 +8,12 @@ import { ActionButton, ActionButtonProps } from "./ActionButton";
|
|
|
8
8
|
* `BottomSheetModalContent` renders `buttonComponent` per item and does not
|
|
9
9
|
* know about entries, so the entry is closed over here instead.
|
|
10
10
|
*/
|
|
11
|
-
export const boundActionButton = (
|
|
11
|
+
export const boundActionButton = (
|
|
12
|
+
entry: ZappEntry | ZappFeed,
|
|
13
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent }
|
|
14
|
+
) => {
|
|
12
15
|
const ActionButtonWithEntry = (props: ActionButtonProps) => (
|
|
13
|
-
<ActionButton {...props} entry={entry as ZappEntry} />
|
|
16
|
+
<ActionButton {...props} entry={entry as ZappEntry} component={component} />
|
|
14
17
|
);
|
|
15
18
|
|
|
16
19
|
ActionButtonWithEntry.displayName = "ActionButtonWithEntry";
|
|
@@ -17,6 +17,8 @@ export type OpenActionsBottomSheetArgs = {
|
|
|
17
17
|
actions: RegisteredAction[];
|
|
18
18
|
title?: string;
|
|
19
19
|
summary?: string;
|
|
20
|
+
/** List that opened the sheet; forwarded into row invokeAction context. */
|
|
21
|
+
component?: ZappUIComponent & { parent?: ZappUIComponent };
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
/**
|
|
@@ -32,6 +34,7 @@ export function openActionsBottomSheet({
|
|
|
32
34
|
actions,
|
|
33
35
|
title,
|
|
34
36
|
summary,
|
|
37
|
+
component,
|
|
35
38
|
}: OpenActionsBottomSheetArgs): void {
|
|
36
39
|
if (!actions?.length) {
|
|
37
40
|
// Every action resolved away - unavailable for this entry, or not
|
|
@@ -50,7 +53,7 @@ export function openActionsBottomSheet({
|
|
|
50
53
|
// BottomSheetModalContent still requires an onPress.
|
|
51
54
|
onPress: noop,
|
|
52
55
|
items: actions,
|
|
53
|
-
buttonComponent: boundActionButton(entry),
|
|
56
|
+
buttonComponent: boundActionButton(entry, component),
|
|
54
57
|
title,
|
|
55
58
|
summary,
|
|
56
59
|
},
|
|
@@ -706,18 +706,31 @@ function ItemButton({
|
|
|
706
706
|
}
|
|
707
707
|
};
|
|
708
708
|
|
|
709
|
+
const buttonPadding = {
|
|
710
|
+
paddingTop: layout.paddingTop,
|
|
711
|
+
paddingRight: layout.paddingRight,
|
|
712
|
+
paddingBottom: layout.paddingBottom,
|
|
713
|
+
paddingLeft: layout.paddingLeft,
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
// A nested Pressable with no handler swallows the parent row press.
|
|
717
|
+
if (!onPress) {
|
|
718
|
+
return (
|
|
719
|
+
<View pointerEvents="none" style={buttonPadding}>
|
|
720
|
+
{renderIcon()}
|
|
721
|
+
</View>
|
|
722
|
+
);
|
|
723
|
+
}
|
|
724
|
+
|
|
709
725
|
return (
|
|
710
726
|
<Pressable
|
|
711
727
|
onPress={(e: any) => {
|
|
712
728
|
e?.stopPropagation?.();
|
|
713
|
-
onPress
|
|
729
|
+
onPress();
|
|
714
730
|
}}
|
|
715
731
|
style={({ pressed }) => [
|
|
732
|
+
buttonPadding,
|
|
716
733
|
{
|
|
717
|
-
paddingTop: layout.paddingTop,
|
|
718
|
-
paddingRight: layout.paddingRight,
|
|
719
|
-
paddingBottom: layout.paddingBottom,
|
|
720
|
-
paddingLeft: layout.paddingLeft,
|
|
721
734
|
opacity: pressed || focused ? 0.7 : 1,
|
|
722
735
|
},
|
|
723
736
|
]}
|
|
@@ -849,6 +862,10 @@ export function AudioPlayerTrackItem({
|
|
|
849
862
|
|
|
850
863
|
const container = resolveContainerSpec(image);
|
|
851
864
|
|
|
865
|
+
const handleTrailingButtonPress =
|
|
866
|
+
onTrailingButtonPress ??
|
|
867
|
+
(data.trailingButton === "multiSelect" ? onPress : undefined);
|
|
868
|
+
|
|
852
869
|
return (
|
|
853
870
|
<ItemContainer
|
|
854
871
|
image={image}
|
|
@@ -884,7 +901,7 @@ export function AudioPlayerTrackItem({
|
|
|
884
901
|
focusedLeadingButton={focusedLeadingButton}
|
|
885
902
|
focusedTrailingButton={focusedTrailingButton}
|
|
886
903
|
onLeadingButtonPress={onLeadingButtonPress}
|
|
887
|
-
onTrailingButtonPress={
|
|
904
|
+
onTrailingButtonPress={handleTrailingButtonPress}
|
|
888
905
|
isSelected={data.isSelected}
|
|
889
906
|
renderHandle={renderHandle}
|
|
890
907
|
checkboxAssets={checkboxAssets}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable } from "react-native";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react-native";
|
|
4
|
+
import { AudioPlayerTrackItem, VARIANT_PLAYLIST_ITEM } from "../Item";
|
|
5
|
+
|
|
6
|
+
const playlistItemData = {
|
|
7
|
+
textLabel1: "Queue",
|
|
8
|
+
textLabel2: "",
|
|
9
|
+
trailingButton: "multiSelect" as const,
|
|
10
|
+
isSelected: false,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
describe("AudioPlayerTrackItem multiSelect checkbox", () => {
|
|
14
|
+
it("invokes onPress when the checkbox is pressed even if onTrailingButtonPress is omitted", () => {
|
|
15
|
+
const onPress = jest.fn();
|
|
16
|
+
|
|
17
|
+
render(
|
|
18
|
+
<AudioPlayerTrackItem
|
|
19
|
+
configuration={VARIANT_PLAYLIST_ITEM}
|
|
20
|
+
data={playlistItemData}
|
|
21
|
+
onPress={onPress}
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const pressables = screen.UNSAFE_getAllByType(Pressable);
|
|
26
|
+
fireEvent.press(pressables[pressables.length - 1]);
|
|
27
|
+
|
|
28
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("invokes onTrailingButtonPress instead of onPress when a dedicated trailing handler is provided", () => {
|
|
32
|
+
const onPress = jest.fn();
|
|
33
|
+
const onTrailingButtonPress = jest.fn();
|
|
34
|
+
|
|
35
|
+
render(
|
|
36
|
+
<AudioPlayerTrackItem
|
|
37
|
+
configuration={VARIANT_PLAYLIST_ITEM}
|
|
38
|
+
data={playlistItemData}
|
|
39
|
+
onPress={onPress}
|
|
40
|
+
onTrailingButtonPress={onTrailingButtonPress}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const pressables = screen.UNSAFE_getAllByType(Pressable);
|
|
45
|
+
fireEvent.press(pressables[pressables.length - 1]);
|
|
46
|
+
|
|
47
|
+
expect(onTrailingButtonPress).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(onPress).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("invokes onPress when the row is pressed", () => {
|
|
52
|
+
const onPress = jest.fn();
|
|
53
|
+
|
|
54
|
+
render(
|
|
55
|
+
<AudioPlayerTrackItem
|
|
56
|
+
configuration={VARIANT_PLAYLIST_ITEM}
|
|
57
|
+
data={playlistItemData}
|
|
58
|
+
onPress={onPress}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
fireEvent.press(screen.UNSAFE_getAllByType(Pressable)[0]);
|
|
63
|
+
|
|
64
|
+
expect(onPress).toHaveBeenCalledTimes(1);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -20,6 +20,7 @@ export type TextInputContentProps = {
|
|
|
20
20
|
defaultValue?: string;
|
|
21
21
|
buttonLabel: string;
|
|
22
22
|
actions: ActionType[];
|
|
23
|
+
actionContext?: Record<string, any>;
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
const styles = StyleSheet.create({
|
|
@@ -38,7 +39,13 @@ const styles = StyleSheet.create({
|
|
|
38
39
|
* Standard dismiss header comes from BottomSheetModalContent / ModalHeader.
|
|
39
40
|
*/
|
|
40
41
|
export function TextInputContent(props: TextInputContentProps) {
|
|
41
|
-
const {
|
|
42
|
+
const {
|
|
43
|
+
inputLabel,
|
|
44
|
+
defaultValue = "",
|
|
45
|
+
buttonLabel,
|
|
46
|
+
actions,
|
|
47
|
+
actionContext,
|
|
48
|
+
} = props;
|
|
42
49
|
|
|
43
50
|
const [textValue, setTextValue] = useState(defaultValue);
|
|
44
51
|
const [submitting, setSubmitting] = useState(false);
|
|
@@ -68,12 +75,17 @@ export function TextInputContent(props: TextInputContentProps) {
|
|
|
68
75
|
return item;
|
|
69
76
|
});
|
|
70
77
|
|
|
71
|
-
|
|
78
|
+
if (actionContext) {
|
|
79
|
+
await actionExecutor.handleActions(updatedActions, actionContext);
|
|
80
|
+
} else {
|
|
81
|
+
await actionExecutor.handleActions(updatedActions);
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
await actionExecutor.handleAction({ type: "dismissBottomSheet" });
|
|
73
85
|
} finally {
|
|
74
86
|
setSubmitting(false);
|
|
75
87
|
}
|
|
76
|
-
}, [submitting, actions, textValue]);
|
|
88
|
+
}, [submitting, actions, textValue, actionContext]);
|
|
77
89
|
|
|
78
90
|
return (
|
|
79
91
|
<View style={styles.container}>
|
|
@@ -117,4 +117,40 @@ describe("TextInputContent", () => {
|
|
|
117
117
|
type: "dismissBottomSheet",
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
|
+
|
|
121
|
+
it("forwards actionContext into handleActions so rename can refresh the List", async () => {
|
|
122
|
+
const actionContext = {
|
|
123
|
+
component: {
|
|
124
|
+
id: "list-1",
|
|
125
|
+
data: { source: "https://example.com/user/collections/abc" },
|
|
126
|
+
},
|
|
127
|
+
screenData: { id: "playlist-screen" },
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
render(<TextInputContent {...baseProps} actionContext={actionContext} />);
|
|
131
|
+
|
|
132
|
+
fireEvent.press(screen.getByTestId("submit-button"));
|
|
133
|
+
|
|
134
|
+
await waitFor(() => {
|
|
135
|
+
expect(actionExecutor.handleActions).toHaveBeenCalledTimes(1);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
expect(actionExecutor.handleActions).toHaveBeenCalledWith(
|
|
139
|
+
[
|
|
140
|
+
{
|
|
141
|
+
type: "sendCloudEvent",
|
|
142
|
+
options: {
|
|
143
|
+
url: "https://server.com/cloud-events",
|
|
144
|
+
type: "com.applicaster.collection.create.v1",
|
|
145
|
+
subject: "edit_collection",
|
|
146
|
+
data: {
|
|
147
|
+
collectionId: "playlist-123",
|
|
148
|
+
name: "Summer Hits",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
actionContext
|
|
154
|
+
);
|
|
155
|
+
});
|
|
120
156
|
});
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Pressable } from "react-native";
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react-native";
|
|
1
4
|
import { getCell } from "../index";
|
|
2
5
|
import { standardCell } from "../standardCell";
|
|
3
6
|
import { selectableCell } from "../selectableCell";
|
|
@@ -64,3 +67,24 @@ describe("mapMenuItemToPlaylistData", () => {
|
|
|
64
67
|
expect(data.trailingButton).toBeUndefined();
|
|
65
68
|
});
|
|
66
69
|
});
|
|
70
|
+
|
|
71
|
+
describe("selectableCell checkbox press", () => {
|
|
72
|
+
it("invokes onPress with the playlist item when the checkbox is pressed", () => {
|
|
73
|
+
const onPress = jest.fn();
|
|
74
|
+
const item = { id: "queue", title: "Queue" };
|
|
75
|
+
|
|
76
|
+
render(
|
|
77
|
+
selectableCell.renderItem({
|
|
78
|
+
item,
|
|
79
|
+
index: 0,
|
|
80
|
+
onPress,
|
|
81
|
+
context: { behavior: { selectMode: "multi" } } as any,
|
|
82
|
+
}) as React.ReactElement
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const pressables = screen.UNSAFE_getAllByType(Pressable);
|
|
86
|
+
fireEvent.press(pressables[pressables.length - 1]);
|
|
87
|
+
|
|
88
|
+
expect(onPress).toHaveBeenCalledWith(item);
|
|
89
|
+
});
|
|
90
|
+
});
|
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.85",
|
|
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.85",
|
|
32
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.85",
|
|
33
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.85",
|
|
34
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.85",
|
|
35
35
|
"fast-json-stable-stringify": "^2.1.0",
|
|
36
36
|
"promise": "^8.3.0",
|
|
37
37
|
"react-native-sortables": "1.7.1",
|