@jobber/components-native 0.98.5 → 0.100.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 (72) hide show
  1. package/dist/package.json +3 -6
  2. package/dist/src/AtlantisOverlayProvider/AtlantisOverlayProvider.js +5 -0
  3. package/dist/src/AtlantisOverlayProvider/index.js +1 -0
  4. package/dist/src/BottomSheet/BottomSheet.js +9 -11
  5. package/dist/src/BottomSheet/hooks/useBottomSheetBackHandler.js +2 -2
  6. package/dist/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.js +9 -11
  7. package/dist/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.js +19 -0
  8. package/dist/src/ContentOverlay/ContentOverlay.js +143 -107
  9. package/dist/src/ContentOverlay/ContentOverlay.style.js +8 -12
  10. package/dist/src/ContentOverlay/computeContentOverlayBehavior.js +76 -0
  11. package/dist/src/ContentOverlay/constants.js +1 -0
  12. package/dist/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.js +25 -0
  13. package/dist/src/ContentOverlay/index.js +1 -1
  14. package/dist/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.js +7 -9
  15. package/dist/src/InputText/InputText.js +44 -1
  16. package/dist/src/index.js +1 -0
  17. package/dist/src/utils/meta/meta.json +1 -0
  18. package/dist/tsconfig.build.tsbuildinfo +1 -1
  19. package/dist/types/src/AtlantisOverlayProvider/AtlantisOverlayProvider.d.ts +6 -0
  20. package/dist/types/src/AtlantisOverlayProvider/index.d.ts +1 -0
  21. package/dist/types/src/BottomSheet/hooks/useBottomSheetBackHandler.d.ts +3 -3
  22. package/dist/types/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.d.ts +11 -0
  23. package/dist/types/src/ContentOverlay/ContentOverlay.d.ts +2 -5
  24. package/dist/types/src/ContentOverlay/ContentOverlay.style.d.ts +11 -10
  25. package/dist/types/src/ContentOverlay/computeContentOverlayBehavior.d.ts +32 -0
  26. package/dist/types/src/ContentOverlay/constants.d.ts +1 -0
  27. package/dist/types/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.d.ts +7 -0
  28. package/dist/types/src/ContentOverlay/index.d.ts +1 -1
  29. package/dist/types/src/ContentOverlay/types.d.ts +5 -12
  30. package/dist/types/src/index.d.ts +1 -0
  31. package/jestSetup.js +2 -0
  32. package/package.json +3 -6
  33. package/src/AtlantisOverlayProvider/AtlantisOverlayProvider.tsx +12 -0
  34. package/src/AtlantisOverlayProvider/index.ts +1 -0
  35. package/src/BottomSheet/BottomSheet.tsx +13 -13
  36. package/src/BottomSheet/hooks/useBottomSheetBackHandler.test.ts +10 -10
  37. package/src/BottomSheet/hooks/useBottomSheetBackHandler.ts +4 -4
  38. package/src/ButtonGroup/ButtonGroup.stories.tsx +10 -8
  39. package/src/ButtonGroup/ButtonGroup.test.tsx +7 -10
  40. package/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.tsx +26 -29
  41. package/src/ContentOverlay/BottomSheetKeyboardAwareScrollView.tsx +36 -0
  42. package/src/ContentOverlay/ContentOverlay.stories.tsx +32 -36
  43. package/src/ContentOverlay/ContentOverlay.style.ts +12 -12
  44. package/src/ContentOverlay/ContentOverlay.test.tsx +157 -79
  45. package/src/ContentOverlay/ContentOverlay.tsx +247 -205
  46. package/src/ContentOverlay/computeContentOverlayBehavior.test.ts +276 -0
  47. package/src/ContentOverlay/computeContentOverlayBehavior.ts +119 -0
  48. package/src/ContentOverlay/constants.ts +1 -0
  49. package/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.test.ts +81 -0
  50. package/src/ContentOverlay/hooks/useBottomSheetModalBackHandler.ts +36 -0
  51. package/src/ContentOverlay/index.ts +4 -1
  52. package/src/ContentOverlay/types.ts +5 -13
  53. package/src/Form/Form.stories.tsx +8 -4
  54. package/src/Form/Form.test.tsx +51 -54
  55. package/src/Form/components/FormSaveButton/FormSaveButton.test.tsx +7 -10
  56. package/src/FormatFile/FormatFile.stories.tsx +3 -4
  57. package/src/FormatFile/FormatFile.test.tsx +11 -14
  58. package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.test.tsx +6 -9
  59. package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.tsx +21 -24
  60. package/src/InputDate/InputDate.test.tsx +5 -8
  61. package/src/InputText/InputText.test.tsx +122 -0
  62. package/src/InputText/InputText.tsx +62 -2
  63. package/src/InputTime/InputTime.stories.tsx +8 -4
  64. package/src/InputTime/InputTime.test.tsx +5 -8
  65. package/src/ThumbnailList/ThumbnailList.stories.tsx +6 -4
  66. package/src/ThumbnailList/ThumbnailList.test.tsx +5 -8
  67. package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +101 -150
  68. package/src/index.ts +1 -0
  69. package/src/utils/meta/meta.json +2 -1
  70. package/dist/src/ContentOverlay/UNSAFE_WrappedModalize.js +0 -23
  71. package/dist/types/src/ContentOverlay/UNSAFE_WrappedModalize.d.ts +0 -3
  72. package/src/ContentOverlay/UNSAFE_WrappedModalize.tsx +0 -41
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ interface AtlantisOverlayProviderProps {
3
+ readonly children: React.ReactNode;
4
+ }
5
+ export declare function AtlantisOverlayProvider({ children, }: AtlantisOverlayProviderProps): React.JSX.Element;
6
+ export {};
@@ -0,0 +1 @@
1
+ export { AtlantisOverlayProvider } from "./AtlantisOverlayProvider";
@@ -1,8 +1,8 @@
1
- import type BottomSheet from "@gorhom/bottom-sheet";
1
+ import type { BottomSheetModal } from "@gorhom/bottom-sheet";
2
2
  /**
3
- * Hook that closes the bottom sheet on the hardware back button press if it is visible
3
+ * Hook that dismisses the bottom sheet modal on the hardware back button press if it is visible
4
4
  * @param bottomSheetRef ref to the bottom sheet component
5
5
  */
6
- export declare function useBottomSheetBackHandler(bottomSheetRef: React.RefObject<BottomSheet | null>): {
6
+ export declare function useBottomSheetBackHandler(bottomSheetRef: React.RefObject<BottomSheetModal | null>): {
7
7
  handleSheetPositionChange: (index: number) => void;
8
8
  };
@@ -0,0 +1,11 @@
1
+ import type { KeyboardAwareScrollViewProps } from "react-native-keyboard-controller";
2
+ import { type BottomSheetScrollViewMethods } from "@gorhom/bottom-sheet";
3
+ declare const BottomSheetKeyboardAwareScrollView: import("react").NamedExoticComponent<{
4
+ bottomOffset?: number;
5
+ disableScrollOnKeyboardHide?: boolean;
6
+ enabled?: boolean;
7
+ extraKeyboardSpace?: number;
8
+ ScrollViewComponent?: React.ComponentType<import("react-native").ScrollViewProps>;
9
+ } & import("react-native").ScrollViewProps & import("react").RefAttributes<BottomSheetScrollViewMethods>>;
10
+ export { BottomSheetKeyboardAwareScrollView };
11
+ export type { KeyboardAwareScrollViewProps };
@@ -1,7 +1,4 @@
1
1
  import React from "react";
2
- import type { Modalize } from "react-native-modalize";
3
2
  import type { ContentOverlayProps } from "./types";
4
- export declare const ContentOverlay: React.ForwardRefExoticComponent<ContentOverlayProps & React.RefAttributes<{
5
- open?: Modalize["open"];
6
- close?: Modalize["close"];
7
- } | undefined>>;
3
+ export declare function useIsKeyboardHandledByScrollView(): boolean;
4
+ export declare function ContentOverlay({ children, title, accessibilityLabel, fullScreen, showDismiss, isDraggable, adjustToContentHeight, keyboardShouldPersistTaps, scrollEnabled, modalBackgroundColor, onClose, onOpen, onBeforeExit, loading, ref, }: ContentOverlayProps): React.JSX.Element;
@@ -1,32 +1,33 @@
1
1
  export declare const useStyles: () => {
2
+ handleWrapper: {
3
+ paddingBottom: number;
4
+ paddingTop: number;
5
+ };
2
6
  handle: {
3
7
  width: number;
4
8
  height: number;
5
9
  backgroundColor: string;
6
- top: number;
7
10
  borderRadius: number;
8
11
  };
9
- overlay: {
12
+ backdrop: {
10
13
  backgroundColor: string;
11
14
  };
12
- modal: {
15
+ background: {
13
16
  borderTopLeftRadius: number;
14
17
  borderTopRightRadius: number;
15
18
  };
16
- modalForLargeScreens: {
17
- width: number;
18
- alignSelf: "center";
19
- };
20
19
  header: {
21
20
  flexDirection: "row";
22
- backgroundColor: string;
23
- paddingTop: number;
24
21
  zIndex: number;
22
+ minHeight: number;
25
23
  borderTopLeftRadius: number;
26
24
  borderTopRightRadius: number;
27
- minHeight: number;
28
25
  };
29
26
  headerShadow: {
27
+ position: "absolute";
28
+ top: number;
29
+ height: number;
30
+ width: "100%";
30
31
  shadowColor: string;
31
32
  shadowOffset: {
32
33
  width: number;
@@ -0,0 +1,32 @@
1
+ export interface ContentOverlayConfig {
2
+ fullScreen: boolean;
3
+ adjustToContentHeight: boolean;
4
+ isDraggable: boolean;
5
+ hasOnBeforeExit: boolean;
6
+ showDismiss: boolean;
7
+ }
8
+ export interface ContentOverlayState {
9
+ isScreenReaderEnabled: boolean;
10
+ position: number;
11
+ }
12
+ export type InitialHeight = "fullScreen" | "contentHeight";
13
+ export interface ContentOverlayBehavior {
14
+ initialHeight: InitialHeight;
15
+ isDraggable: boolean;
16
+ showDismiss: boolean;
17
+ }
18
+ /**
19
+ * Computes the abstract behavior of ContentOverlay from its props and state.
20
+ *
21
+ * This pure function documents and centralizes the complex logic that determines:
22
+ * - Initial height mode (fullScreen vs contentHeight)
23
+ * - Whether the overlay is draggable
24
+ * - Whether the dismiss button should be shown
25
+ *
26
+ * The logic accounts for legacy behavior where:
27
+ * - `onBeforeExit` silently overrides `isDraggable` to false
28
+ * - Default props (neither fullScreen nor adjustToContentHeight) are treated
29
+ * as contentHeight for the new implementation
30
+ * - Dismiss button visibility depends on multiple factors including position state
31
+ */
32
+ export declare function computeContentOverlayBehavior(config: ContentOverlayConfig, state: ContentOverlayState): ContentOverlayBehavior;
@@ -0,0 +1 @@
1
+ export declare const KEYBOARD_TOP_PADDING_AUTO_SCROLL = 20;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Hook that dismisses the bottom sheet on the hardware back button press if it is visible
3
+ * @param bottomSheetModalRef ref to the bottom sheet modal component
4
+ */
5
+ export declare function useBottomSheetModalBackHandler(onCloseController: () => void): {
6
+ handleSheetPositionChange: (index: number) => void;
7
+ };
@@ -1,2 +1,2 @@
1
- export { ContentOverlay } from "./ContentOverlay";
1
+ export { ContentOverlay, useIsKeyboardHandledByScrollView, } from "./ContentOverlay";
2
2
  export type { ContentOverlayRef, ModalBackgroundColor } from "./types";
@@ -1,5 +1,4 @@
1
- import type { ReactNode } from "react";
2
- import type { Modalize } from "react-native-modalize";
1
+ import type { ReactNode, Ref } from "react";
3
2
  export interface ContentOverlayProps {
4
3
  /**
5
4
  * Content to be passed into the overlay
@@ -64,24 +63,18 @@ export interface ContentOverlayProps {
64
63
  * Callback that is called between overlay is closed and when the "x" button is pressed
65
64
  */
66
65
  readonly onBeforeExit?: () => void;
67
- /**
68
- * Define the behavior of the keyboard when having inputs inside the modal.
69
- * @default padding
70
- */
71
- readonly keyboardAvoidingBehavior?: "height" | "padding" | "position";
72
66
  /**
73
67
  * Boolean to show a disabled state
74
68
  * @default false
75
69
  */
76
70
  readonly loading?: boolean;
77
71
  /**
78
- * Define keyboard's Android behavior like iOS's one.
79
- * @default Platform.select({ ios: true, android: false })
72
+ * Ref to the content overlay component.
80
73
  */
81
- readonly avoidKeyboardLikeIOS?: boolean;
74
+ readonly ref?: Ref<ContentOverlayRef>;
82
75
  }
83
76
  export type ModalBackgroundColor = "surface" | "background";
84
77
  export type ContentOverlayRef = {
85
- open?: Modalize["open"];
86
- close?: Modalize["close"];
78
+ open?: () => void;
79
+ close?: () => void;
87
80
  } | undefined;
@@ -2,6 +2,7 @@ export * from "./ActionItem";
2
2
  export * from "./ActionLabel";
3
3
  export * from "./ActivityIndicator";
4
4
  export * from "./AtlantisContext";
5
+ export * from "./AtlantisOverlayProvider";
5
6
  export * from "./AtlantisThemeContext";
6
7
  export * from "./AutoLink";
7
8
  export * from "./Banner";
package/jestSetup.js CHANGED
@@ -16,6 +16,8 @@ jest.mock("./dist/src/Button/components/InternalButtonLoading", () => {
16
16
  };
17
17
  });
18
18
 
19
+ // NOTE: this is the old way we used to mock reanimated. We actually do not need to mock it anymore.
20
+ // To ensure correct test behaviour, please add `jest.unmock("react-native-reanimated")` to your test suite.
19
21
  jest.mock("react-native-reanimated", () => {
20
22
  const reanimated = require("react-native-reanimated/mock");
21
23
  const timing = () => ({ start: () => undefined });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.98.5",
3
+ "version": "0.100.0",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -46,8 +46,6 @@
46
46
  "react-hook-form": "^7.52.0",
47
47
  "react-intl": "^7.1.11",
48
48
  "react-native-keyboard-controller": "^1.20.7",
49
- "react-native-modalize": "^2.0.13",
50
- "react-native-portalize": "^1.0.7",
51
49
  "react-native-toast-message": "^2.1.6",
52
50
  "react-native-uuid": "^1.4.9",
53
51
  "ts-xor": "^1.1.0"
@@ -69,6 +67,7 @@
69
67
  "date-fns-tz": "^2.0.0",
70
68
  "react-native": "^0.82.1",
71
69
  "react-native-gesture-handler": "^2.29.1",
70
+ "react-native-keyboard-controller": "^1.12.0",
72
71
  "react-native-modal-datetime-picker": "^18.0.0",
73
72
  "react-native-reanimated": "^3.7.1",
74
73
  "react-native-safe-area-context": "^5.4.0",
@@ -90,11 +89,9 @@
90
89
  "react-native-gesture-handler": ">=2.22.0",
91
90
  "react-native-keyboard-controller": "^1.20.7",
92
91
  "react-native-modal-datetime-picker": " >=13.0.0",
93
- "react-native-modalize": "^2.0.13",
94
- "react-native-portalize": "^1.0.7",
95
92
  "react-native-reanimated": "^3.0.0",
96
93
  "react-native-safe-area-context": "^5.4.0",
97
94
  "react-native-svg": ">=12.0.0"
98
95
  },
99
- "gitHead": "60dbce9880c63f02f43d8b3f1f5ef9090dba32e3"
96
+ "gitHead": "2ef76c22620e1627eca8cf576b35e78228262574"
100
97
  }
@@ -0,0 +1,12 @@
1
+ import React from "react";
2
+ import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
3
+
4
+ interface AtlantisOverlayProviderProps {
5
+ readonly children: React.ReactNode;
6
+ }
7
+
8
+ export function AtlantisOverlayProvider({
9
+ children,
10
+ }: AtlantisOverlayProviderProps) {
11
+ return <BottomSheetModalProvider>{children}</BottomSheetModalProvider>;
12
+ }
@@ -0,0 +1 @@
1
+ export { AtlantisOverlayProvider } from "./AtlantisOverlayProvider";
@@ -2,8 +2,9 @@ import type { ReactNode } from "react";
2
2
  import React, { useCallback, useImperativeHandle, useRef } from "react";
3
3
  import { Keyboard, View } from "react-native";
4
4
  import { useSafeAreaInsets } from "react-native-safe-area-context";
5
- import RNBottomSheet, {
5
+ import {
6
6
  BottomSheetBackdrop,
7
+ BottomSheetModal,
7
8
  BottomSheetView,
8
9
  } from "@gorhom/bottom-sheet";
9
10
  import type { BottomSheetBackdropProps } from "@gorhom/bottom-sheet";
@@ -67,13 +68,14 @@ export function BottomSheet({
67
68
  const { t } = useAtlantisI18n();
68
69
  const insets = useSafeAreaInsets();
69
70
  const previousIndexRef = useRef(-1);
70
- const bottomSheetRef = useRef<RNBottomSheet>(null);
71
+ const bottomSheetRef = useRef<BottomSheetModal>(null);
71
72
  const { handleSheetPositionChange } =
72
73
  useBottomSheetBackHandler(bottomSheetRef);
73
74
 
74
75
  useImperativeHandle(ref, () => ({
75
76
  open: () => {
76
- bottomSheetRef.current?.expand();
77
+ dismissKeyboard();
78
+ bottomSheetRef.current?.present();
77
79
  },
78
80
  close: () => {
79
81
  close();
@@ -81,7 +83,7 @@ export function BottomSheet({
81
83
  }));
82
84
 
83
85
  const close = useCallback(() => {
84
- bottomSheetRef.current?.close();
86
+ bottomSheetRef.current?.dismiss();
85
87
  }, []);
86
88
 
87
89
  const handleChange = (index: number) => {
@@ -91,26 +93,24 @@ export function BottomSheet({
91
93
  const previousIndex = previousIndexRef.current;
92
94
 
93
95
  if (previousIndex === -1 && index >= 0) {
94
- // Transitioned from closed to open
95
- dismissKeyboard();
96
96
  onOpen?.();
97
- } else if (previousIndex >= 0 && index === -1) {
98
- // Transitioned from open to closed
99
- dismissKeyboard();
100
- onClose?.();
101
97
  }
102
98
 
103
99
  previousIndexRef.current = index;
104
100
  };
105
101
 
106
102
  return (
107
- <RNBottomSheet
103
+ <BottomSheetModal
108
104
  ref={bottomSheetRef}
109
- index={-1}
110
105
  backdropComponent={Backdrop}
111
106
  backgroundStyle={styles.background}
112
107
  enablePanDownToClose={true}
113
108
  onChange={handleChange}
109
+ onDismiss={() => {
110
+ previousIndexRef.current = -1;
111
+ dismissKeyboard();
112
+ onClose?.();
113
+ }}
114
114
  keyboardBlurBehavior="restore"
115
115
  handleStyle={styles.handle}
116
116
  >
@@ -126,7 +126,7 @@ export function BottomSheet({
126
126
  <Footer styles={styles} close={close} cancelLabel={t("cancel")} />
127
127
  )}
128
128
  </BottomSheetView>
129
- </RNBottomSheet>
129
+ </BottomSheetModal>
130
130
  );
131
131
  }
132
132
 
@@ -2,7 +2,7 @@ import { createRef } from "react";
2
2
  import type { RefObject } from "react";
3
3
  import { act, renderHook } from "@testing-library/react-native";
4
4
  import { BackHandler } from "react-native";
5
- import type BottomSheet from "@gorhom/bottom-sheet";
5
+ import type { BottomSheetModal } from "@gorhom/bottom-sheet";
6
6
  import { useBottomSheetBackHandler } from "./useBottomSheetBackHandler";
7
7
 
8
8
  describe("useBottomSheetBackHandler", () => {
@@ -20,7 +20,7 @@ describe("useBottomSheetBackHandler", () => {
20
20
  });
21
21
 
22
22
  it("should register BackHandler listener when sheet becomes visible", async () => {
23
- const bottomSheetRef = createRef<BottomSheet | null>();
23
+ const bottomSheetRef = createRef<BottomSheetModal | null>();
24
24
  const { result } = renderHook(() =>
25
25
  useBottomSheetBackHandler(bottomSheetRef),
26
26
  );
@@ -35,13 +35,13 @@ describe("useBottomSheetBackHandler", () => {
35
35
  );
36
36
  });
37
37
 
38
- it("should call close() when back button is pressed", async () => {
39
- const mockClose = jest.fn();
38
+ it("should call dismiss() when back button is pressed", async () => {
39
+ const mockDismiss = jest.fn();
40
40
  const bottomSheetRef = {
41
41
  current: {
42
- close: mockClose,
43
- } as unknown as BottomSheet,
44
- } as RefObject<BottomSheet | null>;
42
+ dismiss: mockDismiss,
43
+ } as unknown as BottomSheetModal,
44
+ } as RefObject<BottomSheetModal | null>;
45
45
 
46
46
  const { result } = renderHook(() =>
47
47
  useBottomSheetBackHandler(bottomSheetRef),
@@ -54,12 +54,12 @@ describe("useBottomSheetBackHandler", () => {
54
54
  const registeredCallback = mockAddEventListener.mock.calls[0][1];
55
55
  const returnValue = registeredCallback();
56
56
 
57
- expect(mockClose).toHaveBeenCalled();
57
+ expect(mockDismiss).toHaveBeenCalled();
58
58
  expect(returnValue).toBe(true);
59
59
  });
60
60
 
61
61
  it("should remove listener when sheet is dismissed", async () => {
62
- const bottomSheetRef = createRef<BottomSheet | null>();
62
+ const bottomSheetRef = createRef<BottomSheetModal | null>();
63
63
  const { result } = renderHook(() =>
64
64
  useBottomSheetBackHandler(bottomSheetRef),
65
65
  );
@@ -76,7 +76,7 @@ describe("useBottomSheetBackHandler", () => {
76
76
  });
77
77
 
78
78
  it("should not register listener when index is negative", async () => {
79
- const bottomSheetRef = createRef<BottomSheet | null>();
79
+ const bottomSheetRef = createRef<BottomSheetModal | null>();
80
80
  const { result } = renderHook(() =>
81
81
  useBottomSheetBackHandler(bottomSheetRef),
82
82
  );
@@ -1,13 +1,13 @@
1
1
  import { useCallback, useRef } from "react";
2
2
  import { BackHandler, type NativeEventSubscription } from "react-native";
3
- import type BottomSheet from "@gorhom/bottom-sheet";
3
+ import type { BottomSheetModal } from "@gorhom/bottom-sheet";
4
4
 
5
5
  /**
6
- * Hook that closes the bottom sheet on the hardware back button press if it is visible
6
+ * Hook that dismisses the bottom sheet modal on the hardware back button press if it is visible
7
7
  * @param bottomSheetRef ref to the bottom sheet component
8
8
  */
9
9
  export function useBottomSheetBackHandler(
10
- bottomSheetRef: React.RefObject<BottomSheet | null>,
10
+ bottomSheetRef: React.RefObject<BottomSheetModal | null>,
11
11
  ): {
12
12
  handleSheetPositionChange: (index: number) => void;
13
13
  } {
@@ -24,7 +24,7 @@ export function useBottomSheetBackHandler(
24
24
  backHandlerSubscriptionRef.current = BackHandler.addEventListener(
25
25
  "hardwareBackPress",
26
26
  () => {
27
- bottomSheetRef.current?.close();
27
+ bottomSheetRef.current?.dismiss();
28
28
 
29
29
  return true;
30
30
  },
@@ -1,8 +1,10 @@
1
1
  import React from "react";
2
2
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
3
- import { Host } from "react-native-portalize";
4
3
  import { SafeAreaProvider } from "react-native-safe-area-context";
5
- import { ButtonGroup } from "@jobber/components-native";
4
+ import {
5
+ AtlantisOverlayProvider,
6
+ ButtonGroup,
7
+ } from "@jobber/components-native";
6
8
 
7
9
  const meta = {
8
10
  title: "Components/Actions/ButtonGroup",
@@ -31,7 +33,7 @@ type SecondaryActionStory = StoryObj<
31
33
  export const Basic: ButtonGroupStory = {
32
34
  render: args => (
33
35
  <SafeAreaProvider>
34
- <Host>
36
+ <AtlantisOverlayProvider>
35
37
  <ButtonGroup {...args}>
36
38
  <ButtonGroup.PrimaryAction
37
39
  label={"Create"}
@@ -49,7 +51,7 @@ export const Basic: ButtonGroupStory = {
49
51
  onPress={() => console.log("delete")}
50
52
  />
51
53
  </ButtonGroup>
52
- </Host>
54
+ </AtlantisOverlayProvider>
53
55
  </SafeAreaProvider>
54
56
  ),
55
57
  args: {},
@@ -58,7 +60,7 @@ export const Basic: ButtonGroupStory = {
58
60
  export const Primary: PrimaryActionStory = {
59
61
  render: args => (
60
62
  <SafeAreaProvider>
61
- <Host>
63
+ <AtlantisOverlayProvider>
62
64
  <ButtonGroup>
63
65
  <ButtonGroup.PrimaryAction {...args} />
64
66
  <ButtonGroup.SecondaryAction
@@ -72,7 +74,7 @@ export const Primary: PrimaryActionStory = {
72
74
  onPress={() => console.log("delete")}
73
75
  />
74
76
  </ButtonGroup>
75
- </Host>
77
+ </AtlantisOverlayProvider>
76
78
  </SafeAreaProvider>
77
79
  ),
78
80
  args: {
@@ -85,7 +87,7 @@ export const Primary: PrimaryActionStory = {
85
87
  export const Secondary: SecondaryActionStory = {
86
88
  render: args => (
87
89
  <SafeAreaProvider>
88
- <Host>
90
+ <AtlantisOverlayProvider>
89
91
  <ButtonGroup
90
92
  bottomSheetHeading="What would you like to do"
91
93
  showCancelInBottomSheet={true}
@@ -97,7 +99,7 @@ export const Secondary: SecondaryActionStory = {
97
99
  />
98
100
  <ButtonGroup.SecondaryAction {...args} />
99
101
  </ButtonGroup>
100
- </Host>
102
+ </AtlantisOverlayProvider>
101
103
  </SafeAreaProvider>
102
104
  ),
103
105
  args: {
@@ -1,6 +1,5 @@
1
1
  import React from "react";
2
2
  import { fireEvent, render, waitFor } from "@testing-library/react-native";
3
- import { Host } from "react-native-portalize";
4
3
  import { Alert } from "react-native";
5
4
  import type { ButtonGroupProps } from "./ButtonGroup";
6
5
  import { ButtonGroup } from "./ButtonGroup";
@@ -12,15 +11,13 @@ const mockOnOpen = jest.fn();
12
11
 
13
12
  function ButtonGroupForTest(props: ButtonGroupProps) {
14
13
  return (
15
- <Host>
16
- <ButtonGroup
17
- bottomSheetHeading={props.bottomSheetHeading}
18
- showCancelInBottomSheet={props.showCancelInBottomSheet}
19
- onOpenBottomSheet={mockOnOpen}
20
- >
21
- {props.children}
22
- </ButtonGroup>
23
- </Host>
14
+ <ButtonGroup
15
+ bottomSheetHeading={props.bottomSheetHeading}
16
+ showCancelInBottomSheet={props.showCancelInBottomSheet}
17
+ onOpenBottomSheet={mockOnOpen}
18
+ >
19
+ {props.children}
20
+ </ButtonGroup>
24
21
  );
25
22
  }
26
23
 
@@ -1,7 +1,6 @@
1
1
  import type { RefObject } from "react";
2
2
  import React from "react";
3
3
  import { View } from "react-native";
4
- import { Portal } from "react-native-portalize";
5
4
  import type { ButtonGroupSecondaryActionProps } from "../../types";
6
5
  import { BottomSheetOption } from "../../../BottomSheet/components/BottomSheetOption";
7
6
  import type { BottomSheetRef } from "../../../BottomSheet/BottomSheet";
@@ -25,34 +24,32 @@ export function SecondaryActionSheet({
25
24
  onCloseBottomSheet,
26
25
  }: SecondaryActionSheetProps) {
27
26
  return (
28
- <Portal>
29
- <BottomSheet
30
- heading={heading}
31
- showCancel={showCancel}
32
- ref={secondaryActionsRef}
33
- onOpen={onOpenBottomSheet}
34
- onClose={onCloseBottomSheet}
35
- >
36
- <View>
37
- {actions.map((action, index) => {
38
- const { label, onPress, icon, iconColor, destructive } = action;
27
+ <BottomSheet
28
+ heading={heading}
29
+ showCancel={showCancel}
30
+ ref={secondaryActionsRef}
31
+ onOpen={onOpenBottomSheet}
32
+ onClose={onCloseBottomSheet}
33
+ >
34
+ <View>
35
+ {actions.map((action, index) => {
36
+ const { label, onPress, icon, iconColor, destructive } = action;
39
37
 
40
- return (
41
- <BottomSheetOption
42
- destructive={destructive}
43
- key={index}
44
- text={label}
45
- onPress={() => {
46
- secondaryActionsRef?.current?.close();
47
- onPress();
48
- }}
49
- icon={icon}
50
- iconColor={iconColor}
51
- />
52
- );
53
- })}
54
- </View>
55
- </BottomSheet>
56
- </Portal>
38
+ return (
39
+ <BottomSheetOption
40
+ destructive={destructive}
41
+ key={index}
42
+ text={label}
43
+ onPress={() => {
44
+ secondaryActionsRef?.current?.close();
45
+ onPress();
46
+ }}
47
+ icon={icon}
48
+ iconColor={iconColor}
49
+ />
50
+ );
51
+ })}
52
+ </View>
53
+ </BottomSheet>
57
54
  );
58
55
  }
@@ -0,0 +1,36 @@
1
+ import { memo } from "react";
2
+ import type { KeyboardAwareScrollViewProps } from "react-native-keyboard-controller";
3
+ import { KeyboardAwareScrollView } from "react-native-keyboard-controller";
4
+ import {
5
+ type BottomSheetScrollViewMethods,
6
+ SCROLLABLE_TYPE,
7
+ createBottomSheetScrollableComponent,
8
+ } from "@gorhom/bottom-sheet";
9
+ import Reanimated from "react-native-reanimated";
10
+
11
+ /**
12
+ * A keyboard-aware scroll view component that integrates with @gorhom/bottom-sheet.
13
+ *
14
+ * This component wraps `KeyboardAwareScrollView` from `react-native-keyboard-controller`
15
+ * with the bottom sheet HOCs to ensure proper keyboard handling on Android when using
16
+ * TextInputs inside a BottomSheet.
17
+ *
18
+ * @see https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-aware-scroll-view#gorhombottom-sheet
19
+ */
20
+ const AnimatedScrollView =
21
+ Reanimated.createAnimatedComponent<KeyboardAwareScrollViewProps>(
22
+ KeyboardAwareScrollView,
23
+ );
24
+
25
+ const BottomSheetScrollViewComponent = createBottomSheetScrollableComponent<
26
+ BottomSheetScrollViewMethods,
27
+ KeyboardAwareScrollViewProps
28
+ >(SCROLLABLE_TYPE.SCROLLVIEW, AnimatedScrollView);
29
+
30
+ const BottomSheetKeyboardAwareScrollView = memo(BottomSheetScrollViewComponent);
31
+
32
+ BottomSheetKeyboardAwareScrollView.displayName =
33
+ "BottomSheetKeyboardAwareScrollView";
34
+
35
+ export { BottomSheetKeyboardAwareScrollView };
36
+ export type { KeyboardAwareScrollViewProps };