@jobber/components-native 0.26.0 → 0.27.0

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 (33) hide show
  1. package/dist/src/ButtonGroup/ButtonGroup.js +29 -0
  2. package/dist/src/ButtonGroup/ButtonGroup.style.js +18 -0
  3. package/dist/src/ButtonGroup/ButtonGroupAction.js +10 -0
  4. package/dist/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.js +17 -0
  5. package/dist/src/ButtonGroup/components/SecondaryActionSheet/index.js +1 -0
  6. package/dist/src/ButtonGroup/index.js +2 -0
  7. package/dist/src/ButtonGroup/messages.js +18 -0
  8. package/dist/src/ButtonGroup/types.js +1 -0
  9. package/dist/src/ButtonGroup/utils.js +42 -0
  10. package/dist/src/index.js +1 -0
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/dist/types/src/ButtonGroup/ButtonGroup.d.ts +30 -0
  13. package/dist/types/src/ButtonGroup/ButtonGroup.style.d.ts +16 -0
  14. package/dist/types/src/ButtonGroup/ButtonGroupAction.d.ts +47 -0
  15. package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.d.ts +13 -0
  16. package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/index.d.ts +1 -0
  17. package/dist/types/src/ButtonGroup/index.d.ts +3 -0
  18. package/dist/types/src/ButtonGroup/messages.d.ts +17 -0
  19. package/dist/types/src/ButtonGroup/types.d.ts +7 -0
  20. package/dist/types/src/ButtonGroup/utils.d.ts +19 -0
  21. package/dist/types/src/index.d.ts +1 -0
  22. package/package.json +2 -2
  23. package/src/ButtonGroup/ButtonGroup.style.ts +19 -0
  24. package/src/ButtonGroup/ButtonGroup.test.tsx +325 -0
  25. package/src/ButtonGroup/ButtonGroup.tsx +113 -0
  26. package/src/ButtonGroup/ButtonGroupAction.tsx +66 -0
  27. package/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.tsx +55 -0
  28. package/src/ButtonGroup/components/SecondaryActionSheet/index.ts +1 -0
  29. package/src/ButtonGroup/index.ts +8 -0
  30. package/src/ButtonGroup/messages.ts +19 -0
  31. package/src/ButtonGroup/types.ts +30 -0
  32. package/src/ButtonGroup/utils.ts +86 -0
  33. package/src/index.ts +1 -0
@@ -0,0 +1,30 @@
1
+ /// <reference types="react" />
2
+ import { ButtonGroupActionElement } from "./types";
3
+ export interface ButtonGroupProps {
4
+ readonly children: ButtonGroupActionElement | ButtonGroupActionElement[];
5
+ /**
6
+ * Display a cancel button in the secondary bottom sheet footer.
7
+ */
8
+ readonly showCancelInBottomSheet?: boolean;
9
+ /**
10
+ * An optional heading to display in the secondary bottom sheet header.
11
+ */
12
+ readonly bottomSheetHeading?: string;
13
+ /**
14
+ * Callback that is called when the secondary actions bottom sheet is opened.
15
+ */
16
+ readonly onOpenBottomSheet?: () => void;
17
+ /**
18
+ * Callback that is called when the secondary actions bottom sheet is closed.
19
+ */
20
+ readonly onCloseBottomSheet?: () => void;
21
+ /**
22
+ * Allows you to Tap the button while offline
23
+ */
24
+ readonly allowTapWhenOffline?: boolean;
25
+ }
26
+ export declare function ButtonGroup({ children, showCancelInBottomSheet, bottomSheetHeading, onOpenBottomSheet, onCloseBottomSheet, allowTapWhenOffline, }: ButtonGroupProps): JSX.Element;
27
+ export declare namespace ButtonGroup {
28
+ var PrimaryAction: typeof import("./ButtonGroupAction").PrimaryAction;
29
+ var SecondaryAction: typeof import("./ButtonGroupAction").SecondaryAction;
30
+ }
@@ -0,0 +1,16 @@
1
+ export declare const styles: {
2
+ buttonGroup: {
3
+ width: string;
4
+ flexDirection: "row";
5
+ justifyContent: "flex-end";
6
+ };
7
+ button: {
8
+ flexBasis: number;
9
+ flexGrow: number;
10
+ paddingRight: number;
11
+ };
12
+ moreButton: {
13
+ flexBasis: number;
14
+ flexGrow: number;
15
+ };
16
+ };
@@ -0,0 +1,47 @@
1
+ /// <reference types="react" />
2
+ import { IconColorNames, IconNames } from "@jobber/design";
3
+ import { ButtonType, ButtonVariation } from "../Button";
4
+ export interface ButtonGroupActionProps {
5
+ /**
6
+ * Text to be displayed on the action button
7
+ */
8
+ label: string;
9
+ /**
10
+ * Icon to be displayed on the action button
11
+ */
12
+ icon?: IconNames;
13
+ /**
14
+ * Determines the color of the icon. If not specified, some icons have a default system colour which will be used
15
+ * Others that don't have a system colour fall back to greyBlue.
16
+ * Only applies the iconColor to ButtonGroup.PrimaryAction when it is rendered under the "more" menu.
17
+ */
18
+ iconColor?: IconColorNames;
19
+ /**
20
+ * Press handler for the action button
21
+ */
22
+ onPress: () => void;
23
+ loading?: boolean;
24
+ }
25
+ export interface ButtonGroupSecondaryActionProps extends ButtonGroupActionProps {
26
+ /**
27
+ * Indicates whether the secondary action is destructive in nature.
28
+ */
29
+ destructive?: boolean;
30
+ }
31
+ export interface ButtonGroupPrimaryActionProps extends ButtonGroupActionProps {
32
+ /**
33
+ * Sets the action button style (default: "primary")
34
+ */
35
+ buttonType?: ButtonType;
36
+ /**
37
+ * Themes the action button to the type of action it performs (default: "work")
38
+ */
39
+ buttonVariation?: ButtonVariation;
40
+ /**
41
+ * Optional custom button that can be rendered in place of the primary action button
42
+ */
43
+ customButton?: JSX.Element;
44
+ loading?: boolean;
45
+ }
46
+ export declare function PrimaryAction(_: ButtonGroupPrimaryActionProps): JSX.Element;
47
+ export declare function SecondaryAction(_: ButtonGroupSecondaryActionProps): JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { RefObject } from "react";
2
+ import { ButtonGroupSecondaryActionProps } from "../../types";
3
+ import { BottomSheetRef } from "../../../BottomSheet/BottomSheet";
4
+ interface SecondaryActionSheetProps {
5
+ actions: ButtonGroupSecondaryActionProps[];
6
+ secondaryActionsRef: RefObject<BottomSheetRef>;
7
+ showCancel?: boolean;
8
+ heading?: string;
9
+ onOpenBottomSheet?: () => void;
10
+ onCloseBottomSheet?: () => void;
11
+ }
12
+ export declare function SecondaryActionSheet({ actions, secondaryActionsRef, heading, showCancel, onOpenBottomSheet, onCloseBottomSheet, }: SecondaryActionSheetProps): JSX.Element;
13
+ export {};
@@ -0,0 +1 @@
1
+ export { SecondaryActionSheet } from "./SecondaryActionSheet";
@@ -0,0 +1,3 @@
1
+ export { ButtonGroup } from "./ButtonGroup";
2
+ export type { ButtonGroupActionProps, ButtonGroupPrimaryActionProps, ButtonGroupSecondaryActionProps, ButtonGroupActionElement, } from "./types";
3
+ export { usePreventTapWhenOffline } from "./utils";
@@ -0,0 +1,17 @@
1
+ export declare const messages: {
2
+ more: {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ unavailableNetworkTitle: {
8
+ id: string;
9
+ defaultMessage: string;
10
+ description: string;
11
+ };
12
+ unavailableNetworkMessage: {
13
+ id: string;
14
+ defaultMessage: string;
15
+ description: string;
16
+ };
17
+ };
@@ -0,0 +1,7 @@
1
+ import { ReactElement } from "react";
2
+ import { ButtonGroupActionProps, ButtonGroupPrimaryActionProps, ButtonGroupSecondaryActionProps, PrimaryAction, SecondaryAction } from "./ButtonGroupAction";
3
+ export type ButtonGroupPrimaryActionElement = ReactElement<ButtonGroupPrimaryActionProps, typeof PrimaryAction>;
4
+ export type ButtonGroupSecondaryActionElement = ReactElement<ButtonGroupActionProps, typeof SecondaryAction>;
5
+ export type ButtonGroupActionElement = ButtonGroupPrimaryActionElement | ButtonGroupSecondaryActionElement;
6
+ export type BottomSheetTextTransform = "capitalize" | "none";
7
+ export type { ButtonGroupActionProps, ButtonGroupPrimaryActionProps, ButtonGroupSecondaryActionProps, };
@@ -0,0 +1,19 @@
1
+ import { ButtonGroupActionElement, ButtonGroupPrimaryActionElement } from "./types";
2
+ import { ButtonGroupActionProps } from "./ButtonGroupAction";
3
+ interface UsePreventTapWhenOfflineShape {
4
+ readonly handlePress: (callback: ButtonGroupActionProps["onPress"]) => () => void;
5
+ }
6
+ /**
7
+ * Determine if the onPress should be fired or an alert when the device is
8
+ * online or offline
9
+ */
10
+ export declare function usePreventTapWhenOffline(): UsePreventTapWhenOfflineShape;
11
+ interface GetActionShape {
12
+ readonly primaryActions: ButtonGroupPrimaryActionElement[];
13
+ readonly secondaryActions: ButtonGroupActionElement[];
14
+ }
15
+ /**
16
+ * Separate the Primary and Secondary actions into 2 const
17
+ */
18
+ export declare function getActions(children: ButtonGroupActionElement | ButtonGroupActionElement[]): GetActionShape;
19
+ export {};
@@ -4,6 +4,7 @@ export * from "./ActivityIndicator";
4
4
  export * from "./AtlantisContext";
5
5
  export * from "./BottomSheet";
6
6
  export * from "./Button";
7
+ export * from "./ButtonGroup";
7
8
  export * from "./Card";
8
9
  export * from "./Chip";
9
10
  export * from "./Content";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "license": "MIT",
5
5
  "main": "dist/src/index.js",
6
6
  "module": "dist/src/index.js",
@@ -55,5 +55,5 @@
55
55
  "react": "^18",
56
56
  "react-native": ">=0.69.2"
57
57
  },
58
- "gitHead": "8b036ebea9748a095b6a1d29ea2b45c0958a286b"
58
+ "gitHead": "4cac408646d885509b88823f5186df1a9534b641"
59
59
  }
@@ -0,0 +1,19 @@
1
+ import { StyleSheet } from "react-native";
2
+ import { tokens } from "../utils/design";
3
+
4
+ export const styles = StyleSheet.create({
5
+ buttonGroup: {
6
+ width: "100%",
7
+ flexDirection: "row",
8
+ justifyContent: "flex-end",
9
+ },
10
+ button: {
11
+ flexBasis: tokens["space-largest"],
12
+ flexGrow: 1,
13
+ paddingRight: tokens["space-smaller"],
14
+ },
15
+ moreButton: {
16
+ flexBasis: tokens["space-largest"],
17
+ flexGrow: 0,
18
+ },
19
+ });
@@ -0,0 +1,325 @@
1
+ import React from "react";
2
+ import { fireEvent, render } from "@testing-library/react-native";
3
+ import { Host } from "react-native-portalize";
4
+ import { Alert } from "react-native";
5
+ import { messages } from "./messages";
6
+ import { ButtonGroup, ButtonGroupProps } from "./ButtonGroup";
7
+ import { Button } from "../Button";
8
+ import * as atlantisContext from "../AtlantisContext/AtlantisContext";
9
+ import { defaultValues as contextDefaultValue } from "../AtlantisContext";
10
+
11
+ const mockOnOpen = jest.fn();
12
+
13
+ function ButtonGroupForTest(props: ButtonGroupProps) {
14
+ return (
15
+ <Host>
16
+ <ButtonGroup
17
+ bottomSheetHeading={props.bottomSheetHeading}
18
+ showCancelInBottomSheet={props.showCancelInBottomSheet}
19
+ onOpenBottomSheet={mockOnOpen}
20
+ >
21
+ {props.children}
22
+ </ButtonGroup>
23
+ </Host>
24
+ );
25
+ }
26
+
27
+ it("renders a single primary action", () => {
28
+ const createAction = jest.fn();
29
+ const { getByText, queryByLabelText } = render(
30
+ <ButtonGroupForTest>
31
+ <ButtonGroup.PrimaryAction
32
+ label="Create"
33
+ icon="plus"
34
+ onPress={createAction}
35
+ />
36
+ </ButtonGroupForTest>,
37
+ );
38
+
39
+ expect(getByText("Create")).not.toBeNull();
40
+ expect(queryByLabelText(messages.more.defaultMessage)).toBeNull();
41
+ });
42
+
43
+ it("renders 2 primary actions", () => {
44
+ const createAction = jest.fn();
45
+ const editAction = jest.fn();
46
+ const { getByText, queryByLabelText } = render(
47
+ <ButtonGroupForTest>
48
+ <ButtonGroup.PrimaryAction
49
+ label="Create"
50
+ icon="plus"
51
+ onPress={createAction}
52
+ />
53
+ <ButtonGroup.PrimaryAction
54
+ label="Edit"
55
+ icon="edit"
56
+ onPress={editAction}
57
+ />
58
+ </ButtonGroupForTest>,
59
+ );
60
+
61
+ expect(getByText("Create")).not.toBeNull();
62
+ expect(getByText("Edit")).not.toBeNull();
63
+ expect(queryByLabelText(messages.more.defaultMessage)).toBeNull();
64
+ });
65
+
66
+ it("does not render more than 2 primary actions but adds a More button", () => {
67
+ const createAction = jest.fn();
68
+ const editAction = jest.fn();
69
+ const mysteryAction = jest.fn();
70
+ const { getByText, queryByText, getByLabelText } = render(
71
+ <ButtonGroupForTest>
72
+ <ButtonGroup.PrimaryAction
73
+ label="Create"
74
+ icon="plus"
75
+ onPress={createAction}
76
+ />
77
+ <ButtonGroup.PrimaryAction
78
+ label="Edit"
79
+ icon="edit"
80
+ onPress={editAction}
81
+ />
82
+ <ButtonGroup.PrimaryAction
83
+ label="Mystery"
84
+ icon="edit"
85
+ onPress={mysteryAction}
86
+ />
87
+ </ButtonGroupForTest>,
88
+ );
89
+
90
+ expect(getByText("Create")).not.toBeNull();
91
+ expect(getByText("Edit")).not.toBeNull();
92
+ expect(queryByText("Mystery")).toBeNull();
93
+ expect(getByLabelText(messages.more.defaultMessage)).toBeDefined();
94
+ });
95
+
96
+ it("does not render secondary actions", () => {
97
+ const createAction = jest.fn();
98
+ const editAction = jest.fn();
99
+ const deleteAction = jest.fn();
100
+ const { getByText, queryByText, getByLabelText } = render(
101
+ <ButtonGroupForTest>
102
+ <ButtonGroup.PrimaryAction
103
+ label="Create"
104
+ icon="plus"
105
+ onPress={createAction}
106
+ />
107
+ <ButtonGroup.SecondaryAction
108
+ label="Edit"
109
+ icon="edit"
110
+ onPress={editAction}
111
+ />
112
+ <ButtonGroup.SecondaryAction
113
+ label="Delete"
114
+ icon="trash"
115
+ onPress={deleteAction}
116
+ />
117
+ </ButtonGroupForTest>,
118
+ );
119
+ expect(getByText("Create")).not.toBeNull();
120
+ expect(queryByText("Edit")).toBeNull();
121
+ expect(queryByText("Delete")).toBeNull();
122
+ expect(getByLabelText(messages.more.defaultMessage)).toBeDefined();
123
+ });
124
+
125
+ it("renders first secondary action as a primary action button if no primary action specified", () => {
126
+ const editAction = jest.fn();
127
+ const deleteAction = jest.fn();
128
+ const { queryByText, getByText, getByLabelText } = render(
129
+ <ButtonGroupForTest>
130
+ <ButtonGroup.SecondaryAction
131
+ label="Edit"
132
+ icon="edit"
133
+ onPress={editAction}
134
+ />
135
+ <ButtonGroup.SecondaryAction
136
+ label="Delete"
137
+ icon="trash"
138
+ onPress={deleteAction}
139
+ />
140
+ </ButtonGroupForTest>,
141
+ );
142
+ expect(getByText("Edit")).not.toBeNull();
143
+ expect(queryByText("Delete")).toBeNull();
144
+ expect(getByLabelText(messages.more.defaultMessage)).toBeDefined();
145
+ });
146
+
147
+ it("fires the press handlers when the primary action buttons are pressed", () => {
148
+ const createAction = jest.fn();
149
+ const editAction = jest.fn();
150
+ const { getByText } = render(
151
+ <ButtonGroupForTest>
152
+ <ButtonGroup.PrimaryAction
153
+ label="Create"
154
+ icon="plus"
155
+ onPress={createAction}
156
+ />
157
+ <ButtonGroup.PrimaryAction
158
+ label="Edit"
159
+ icon="edit"
160
+ onPress={editAction}
161
+ />
162
+ </ButtonGroupForTest>,
163
+ );
164
+
165
+ fireEvent.press(getByText("Create"));
166
+ expect(createAction).toHaveBeenCalled();
167
+ fireEvent.press(getByText("Edit"));
168
+ expect(editAction).toHaveBeenCalled();
169
+ });
170
+
171
+ it("opens the secondary action menu when the More button is pressed", () => {
172
+ const createAction = jest.fn();
173
+ const editAction = jest.fn();
174
+ const deleteAction = jest.fn();
175
+ const { getByText, queryByText, getByLabelText } = render(
176
+ <ButtonGroupForTest>
177
+ <ButtonGroup.PrimaryAction
178
+ label="Create"
179
+ icon="plus"
180
+ onPress={createAction}
181
+ />
182
+ <ButtonGroup.SecondaryAction
183
+ label="Edit"
184
+ icon="edit"
185
+ onPress={editAction}
186
+ />
187
+ <ButtonGroup.SecondaryAction
188
+ label="Delete"
189
+ icon="trash"
190
+ onPress={deleteAction}
191
+ />
192
+ </ButtonGroupForTest>,
193
+ );
194
+
195
+ expect(queryByText("Edit")).toBeNull();
196
+
197
+ fireEvent.press(getByLabelText(messages.more.defaultMessage));
198
+
199
+ expect(getByText("Edit")).not.toBeNull();
200
+ expect(getByText("Delete")).not.toBeNull();
201
+ });
202
+
203
+ it("renders heading and cancel options if passed in", () => {
204
+ const createAction = jest.fn();
205
+ const editAction = jest.fn();
206
+ const { getByText, getByLabelText } = render(
207
+ <ButtonGroupForTest
208
+ bottomSheetHeading={"Heading"}
209
+ showCancelInBottomSheet={true}
210
+ >
211
+ <ButtonGroup.PrimaryAction
212
+ label="Create"
213
+ icon="plus"
214
+ onPress={createAction}
215
+ />
216
+ <ButtonGroup.SecondaryAction
217
+ label="Edit"
218
+ icon="edit"
219
+ onPress={editAction}
220
+ />
221
+ </ButtonGroupForTest>,
222
+ );
223
+
224
+ fireEvent.press(getByLabelText(messages.more.defaultMessage));
225
+
226
+ expect(getByText("Heading")).not.toBeNull();
227
+ expect(getByText("Cancel")).not.toBeNull();
228
+ });
229
+
230
+ it("renders custom button for primary action if passed in", () => {
231
+ const createAction = jest.fn();
232
+
233
+ const customCreateButton = (
234
+ <Button label="CustomCreate" onPress={createAction} />
235
+ );
236
+
237
+ const { queryByText, getByText } = render(
238
+ <ButtonGroupForTest>
239
+ <ButtonGroup.PrimaryAction
240
+ label="Create"
241
+ icon="plus"
242
+ onPress={createAction}
243
+ customButton={customCreateButton}
244
+ />
245
+ </ButtonGroupForTest>,
246
+ );
247
+
248
+ expect(getByText("CustomCreate")).not.toBeNull();
249
+ expect(queryByText("Create")).toBeNull();
250
+ });
251
+
252
+ it("calls onOpenBottomSheet when the secondary actions are opened", () => {
253
+ const createAction = jest.fn();
254
+ const editAction = jest.fn();
255
+ const deleteAction = jest.fn();
256
+ const { getByLabelText } = render(
257
+ <ButtonGroupForTest>
258
+ <ButtonGroup.PrimaryAction
259
+ label="Create"
260
+ icon="plus"
261
+ onPress={createAction}
262
+ />
263
+ <ButtonGroup.SecondaryAction
264
+ label="Edit"
265
+ icon="edit"
266
+ onPress={editAction}
267
+ />
268
+ <ButtonGroup.SecondaryAction
269
+ label="Delete"
270
+ icon="trash"
271
+ onPress={deleteAction}
272
+ />
273
+ </ButtonGroupForTest>,
274
+ );
275
+
276
+ fireEvent.press(getByLabelText(messages.more.defaultMessage));
277
+
278
+ expect(mockOnOpen).toHaveBeenCalled();
279
+ });
280
+
281
+ describe("ButtonGroup Offline/Online", () => {
282
+ const atlantisContextSpy = jest.spyOn(atlantisContext, "useAtlantisContext");
283
+
284
+ afterEach(() => {
285
+ jest.restoreAllMocks();
286
+ });
287
+
288
+ const handlePress = jest.fn();
289
+ const label = "Click me";
290
+ const setup = () =>
291
+ render(
292
+ <ButtonGroupForTest>
293
+ <ButtonGroup.PrimaryAction
294
+ label={label}
295
+ icon="plus"
296
+ onPress={handlePress}
297
+ />
298
+ </ButtonGroupForTest>,
299
+ );
300
+
301
+ it("should show an alert and not fire the onPress", () => {
302
+ const alertSpy = jest.spyOn(Alert, "alert");
303
+ atlantisContextSpy.mockReturnValue({
304
+ ...contextDefaultValue,
305
+ isOnline: false,
306
+ });
307
+
308
+ const { getByText } = setup();
309
+
310
+ fireEvent.press(getByText(label));
311
+
312
+ expect(alertSpy).toHaveBeenCalled();
313
+ expect(handlePress).not.toHaveBeenCalled();
314
+ });
315
+
316
+ it("should fire the onPress and not show an alert", () => {
317
+ const alertSpy = jest.spyOn(Alert, "alert");
318
+ const { getByText } = setup();
319
+
320
+ fireEvent.press(getByText(label));
321
+
322
+ expect(handlePress).toHaveBeenCalled();
323
+ expect(alertSpy).not.toHaveBeenCalled();
324
+ });
325
+ });
@@ -0,0 +1,113 @@
1
+ import React, { useRef } from "react";
2
+ import { useIntl } from "react-intl";
3
+ import { View } from "react-native";
4
+ import { PrimaryAction, SecondaryAction } from "./ButtonGroupAction";
5
+ import { messages } from "./messages";
6
+ import { styles } from "./ButtonGroup.style";
7
+ import { SecondaryActionSheet } from "./components/SecondaryActionSheet";
8
+ import { getActions, usePreventTapWhenOffline } from "./utils";
9
+ import { ButtonGroupActionElement } from "./types";
10
+ import { Button } from "../Button";
11
+ import { BottomSheetRef } from "../BottomSheet/BottomSheet";
12
+
13
+ export interface ButtonGroupProps {
14
+ readonly children: ButtonGroupActionElement | ButtonGroupActionElement[];
15
+
16
+ /**
17
+ * Display a cancel button in the secondary bottom sheet footer.
18
+ */
19
+ readonly showCancelInBottomSheet?: boolean;
20
+
21
+ /**
22
+ * An optional heading to display in the secondary bottom sheet header.
23
+ */
24
+ readonly bottomSheetHeading?: string;
25
+
26
+ /**
27
+ * Callback that is called when the secondary actions bottom sheet is opened.
28
+ */
29
+ readonly onOpenBottomSheet?: () => void;
30
+
31
+ /**
32
+ * Callback that is called when the secondary actions bottom sheet is closed.
33
+ */
34
+ readonly onCloseBottomSheet?: () => void;
35
+
36
+ /**
37
+ * Allows you to Tap the button while offline
38
+ */
39
+ readonly allowTapWhenOffline?: boolean;
40
+ }
41
+
42
+ export function ButtonGroup({
43
+ children,
44
+ showCancelInBottomSheet,
45
+ bottomSheetHeading,
46
+ onOpenBottomSheet,
47
+ onCloseBottomSheet,
48
+ allowTapWhenOffline = false,
49
+ }: ButtonGroupProps): JSX.Element {
50
+ const { formatMessage } = useIntl();
51
+ const { handlePress } = usePreventTapWhenOffline();
52
+ const secondaryActionsRef = useRef<BottomSheetRef>();
53
+ const { primaryActions, secondaryActions } = getActions(children);
54
+ return (
55
+ <View style={styles.buttonGroup}>
56
+ {primaryActions.map((action, index) => {
57
+ const {
58
+ label,
59
+ onPress,
60
+ buttonType,
61
+ buttonVariation,
62
+ icon,
63
+ customButton,
64
+ loading,
65
+ } = action.props;
66
+
67
+ return (
68
+ <View style={styles.button} key={index}>
69
+ {customButton || (
70
+ <Button
71
+ label={label}
72
+ accessibilityLabel={label}
73
+ onPress={allowTapWhenOffline ? onPress : handlePress(onPress)}
74
+ type={buttonType}
75
+ variation={buttonVariation}
76
+ fullHeight={true}
77
+ icon={icon}
78
+ loading={loading}
79
+ />
80
+ )}
81
+ </View>
82
+ );
83
+ })}
84
+
85
+ {secondaryActions.length > 0 && (
86
+ <View style={styles.moreButton}>
87
+ <Button
88
+ icon={"more"}
89
+ accessibilityLabel={formatMessage(messages.more)}
90
+ onPress={handlePress(openBottomSheet)}
91
+ fullHeight={true}
92
+ />
93
+ </View>
94
+ )}
95
+
96
+ <SecondaryActionSheet
97
+ heading={bottomSheetHeading}
98
+ showCancel={showCancelInBottomSheet}
99
+ secondaryActionsRef={secondaryActionsRef}
100
+ actions={secondaryActions.map(secondaryAction => secondaryAction.props)}
101
+ onOpenBottomSheet={onOpenBottomSheet}
102
+ onCloseBottomSheet={onCloseBottomSheet}
103
+ />
104
+ </View>
105
+ );
106
+
107
+ function openBottomSheet() {
108
+ secondaryActionsRef.current?.open();
109
+ }
110
+ }
111
+
112
+ ButtonGroup.PrimaryAction = PrimaryAction;
113
+ ButtonGroup.SecondaryAction = SecondaryAction;