@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
@@ -1,23 +1,15 @@
1
1
  import React, { useRef } from "react";
2
2
  import { View } from "react-native";
3
3
  import type { Meta, StoryObj } from "@storybook/react-native-web-vite";
4
- import { Host } from "react-native-portalize";
5
4
  import { SafeAreaProvider } from "react-native-safe-area-context";
6
- import type { ContentOverlayRef } from "@jobber/components-native";
7
- import {
8
- Button,
9
- Content,
10
- ContentOverlay,
11
- Text,
12
- } from "@jobber/components-native";
5
+ import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
6
+ import { Button, Heading, Text } from "@jobber/components-native";
7
+ import { ContentOverlay } from "./ContentOverlay";
8
+ import type { ContentOverlayRef } from "./types";
13
9
 
14
10
  const meta = {
15
11
  title: "Components/Overlays/ContentOverlay",
16
12
  component: ContentOverlay,
17
- parameters: {
18
- viewport: { defaultViewport: "mobile1" },
19
- showNativeOnWebDisclaimer: true,
20
- },
21
13
  } satisfies Meta<typeof ContentOverlay>;
22
14
  export default meta;
23
15
 
@@ -30,36 +22,40 @@ interface ContentOverlayStoryArgs {
30
22
 
31
23
  type Story = StoryObj<ContentOverlayStoryArgs>;
32
24
 
33
- const BasicTemplate = (args: ContentOverlayStoryArgs) => {
25
+ const BasicTemplate = () => {
34
26
  const contentOverlayRef = useRef<ContentOverlayRef>(null);
35
27
 
28
+ const openContentOverlay = () => {
29
+ contentOverlayRef.current?.open?.();
30
+ };
31
+
32
+ const closeContentOverlay = () => {
33
+ contentOverlayRef.current?.close?.();
34
+ };
35
+
36
36
  return (
37
37
  <SafeAreaProvider>
38
- <Host>
39
- <View
40
- style={{
41
- width: 300,
42
- }}
38
+ <BottomSheetModalProvider>
39
+ <View style={{ display: "flex", flexDirection: "column", gap: 16 }}>
40
+ <Heading>Basic ContentOverlay</Heading>
41
+ <Text>
42
+ Note that due to the differences between React Native Web and React
43
+ Native, this does not render 100% properly
44
+ </Text>
45
+ <Button label="Open Content Overlay" onPress={openContentOverlay} />
46
+ <Button label="Close Content Overlay" onPress={closeContentOverlay} />
47
+ </View>
48
+ <ContentOverlay
49
+ ref={contentOverlayRef}
50
+ title="Content Overlay Title"
51
+ onClose={() => console.log("closed content overlay")}
52
+ onOpen={() => console.log("opened content overlay")}
43
53
  >
44
- <ContentOverlay
45
- title={args.title}
46
- onClose={args.onClose}
47
- onOpen={args.onOpen}
48
- fullScreen={args.fullScreen}
49
- ref={contentOverlayRef}
50
- >
51
- <Content>
52
- <Text>I am some text inside the ContentOverlay.</Text>
53
- </Content>
54
- </ContentOverlay>
55
- <View>
56
- <Button
57
- label="Open Overlay"
58
- onPress={() => contentOverlayRef.current?.open?.()}
59
- />
54
+ <View style={{ padding: 16 }}>
55
+ <Text>This is the content inside the overlay.</Text>
60
56
  </View>
61
- </View>
62
- </Host>
57
+ </ContentOverlay>
58
+ </BottomSheetModalProvider>
63
59
  </SafeAreaProvider>
64
60
  );
65
61
  };
@@ -2,43 +2,43 @@ import { buildThemedStyles } from "../AtlantisThemeContext";
2
2
 
3
3
  export const useStyles = buildThemedStyles(tokens => {
4
4
  const modalBorderRadius = tokens["radius-larger"];
5
- const titleOffsetFromHandle = tokens["space-base"];
6
5
 
7
6
  return {
7
+ handleWrapper: {
8
+ paddingBottom: tokens["space-smallest"],
9
+ paddingTop: tokens["space-small"],
10
+ },
11
+
8
12
  handle: {
9
13
  width: tokens["space-largest"],
10
14
  height: tokens["space-smaller"] + tokens["space-smallest"],
11
15
  backgroundColor: tokens["color-border"],
12
- top: tokens["space-small"],
13
16
  borderRadius: tokens["radius-circle"],
14
17
  },
15
18
 
16
- overlay: {
19
+ backdrop: {
17
20
  backgroundColor: tokens["color-overlay"],
18
21
  },
19
22
 
20
- modal: {
23
+ background: {
21
24
  borderTopLeftRadius: modalBorderRadius,
22
25
  borderTopRightRadius: modalBorderRadius,
23
26
  },
24
27
 
25
- modalForLargeScreens: {
26
- width: 640,
27
- alignSelf: "center",
28
- },
29
-
30
28
  header: {
31
29
  flexDirection: "row",
32
- backgroundColor: tokens["color-surface"],
33
- paddingTop: titleOffsetFromHandle,
34
30
  zIndex: tokens["elevation-base"],
31
+ minHeight: tokens["space-extravagant"] - tokens["space-base"],
35
32
  borderTopLeftRadius: modalBorderRadius,
36
33
  borderTopRightRadius: modalBorderRadius,
37
- minHeight: tokens["space-extravagant"],
38
34
  },
39
35
 
40
36
  headerShadow: {
41
37
  ...tokens["shadow-base"],
38
+ position: "absolute",
39
+ top: -20,
40
+ height: 20,
41
+ width: "100%",
42
42
  },
43
43
 
44
44
  childrenStyle: {
@@ -1,27 +1,41 @@
1
1
  import React, { createRef } from "react";
2
- import { fireEvent, render, waitFor } from "@testing-library/react-native";
2
+ import {
3
+ act,
4
+ render,
5
+ screen,
6
+ userEvent,
7
+ waitFor,
8
+ } from "@testing-library/react-native";
3
9
  import { AccessibilityInfo, View } from "react-native";
4
- import { Host } from "react-native-portalize";
5
10
  import type { ReactTestInstance } from "react-test-renderer";
6
- import { act } from "react-test-renderer";
7
11
  import type { ContentOverlayRef, ModalBackgroundColor } from "./types";
8
12
  import { ContentOverlay } from "./ContentOverlay";
13
+ import { AtlantisOverlayProvider } from "../AtlantisOverlayProvider";
9
14
  import { tokens } from "../utils/design";
10
15
  import { Button } from "../Button";
11
16
  import { Content } from "../Content";
12
17
  import { Text } from "../Text";
13
18
 
14
19
  jest.unmock("../hooks/useIsScreenReaderEnabled");
20
+ jest.unmock("react-native-reanimated");
21
+ jest.unmock("@gorhom/bottom-sheet");
22
+ jest.mock("@gorhom/bottom-sheet/lib/commonjs/hooks/useAnimatedLayout", () => ({
23
+ // Fix for reanimated not actually running in the test environment.
24
+ useAnimatedLayout: () => {
25
+ const value = {
26
+ containerHeight: 600,
27
+ rawContainerHeight: 600,
28
+ handleHeight: 24,
29
+ footerHeight: 0,
30
+ contentHeight: 400,
31
+ containerOffset: { top: 0, bottom: 0, left: 0, right: 0 },
32
+ };
15
33
 
16
- function fireLayoutEvent(childrenContent: ReactTestInstance) {
17
- fireEvent(childrenContent, "onLayout", {
18
- nativeEvent: {
19
- layout: {
20
- height: 100,
21
- },
22
- },
23
- });
24
- }
34
+ return { value, get: () => value };
35
+ },
36
+ }));
37
+
38
+ const user = userEvent.setup();
25
39
 
26
40
  interface testRendererOptions {
27
41
  text: string;
@@ -44,12 +58,6 @@ function getDefaultOptions(): testRendererOptions {
44
58
  fullScreen: false,
45
59
  showDismiss: false,
46
60
  modalBackgroundColor: "surface",
47
- onCloseCallback: () => {
48
- return;
49
- },
50
- onOpenCallback: () => {
51
- return;
52
- },
53
61
  };
54
62
  }
55
63
 
@@ -69,8 +77,8 @@ function renderContentOverlay(
69
77
  ) {
70
78
  const contentOverlayRef = createRef<ContentOverlayRef>();
71
79
 
72
- const renderResult = render(
73
- <Host>
80
+ render(
81
+ <AtlantisOverlayProvider>
74
82
  <View>
75
83
  <Text>I am a bunch of text</Text>
76
84
  <Button
@@ -95,32 +103,31 @@ function renderContentOverlay(
95
103
  </Content>
96
104
  </ContentOverlay>
97
105
  </View>
98
- </Host>,
106
+ </AtlantisOverlayProvider>,
99
107
  );
100
-
101
- const childrenView = renderResult.getByTestId("ATL-Overlay-Children");
102
- fireLayoutEvent(childrenView);
103
- const headerComponent = renderResult.getByTestId("ATL-Overlay-Header");
104
- fireLayoutEvent(headerComponent);
105
-
106
- return renderResult;
107
108
  }
108
109
 
109
110
  async function renderAndOpenContentOverlay(
110
111
  defaultOptions = getDefaultOptions(),
111
112
  ) {
112
- const rendered = renderContentOverlay(defaultOptions);
113
+ jest.useFakeTimers();
114
+ const props = {
115
+ onOpenCallback: jest.fn(),
116
+ onCloseCallback: jest.fn(),
117
+ ...defaultOptions,
118
+ };
113
119
 
120
+ renderContentOverlay(props);
121
+
122
+ await user.press(screen.getByLabelText(defaultOptions.buttonLabel));
114
123
  await act(async () => {
115
- fireEvent.press(rendered.getByLabelText(defaultOptions.buttonLabel));
124
+ jest.runAllTimers();
116
125
  });
117
126
 
118
- // Wait for the modal to open asynchronously (due to requestAnimationFrame)
119
127
  await waitFor(() => {
120
- expect(rendered.getByTestId("ATL-Overlay-Header")).toBeDefined();
128
+ expect(screen.getByTestId("ATL-Overlay-Header")).toBeDefined();
129
+ expect(props.onOpenCallback).toHaveBeenCalledTimes(1);
121
130
  });
122
-
123
- return rendered;
124
131
  }
125
132
 
126
133
  describe("when open is called on the content overlay ref", () => {
@@ -129,9 +136,9 @@ describe("when open is called on the content overlay ref", () => {
129
136
  ...getDefaultOptions(),
130
137
  text: "I am text within the content overlay",
131
138
  };
132
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
139
+ await renderAndOpenContentOverlay(options);
133
140
 
134
- expect(contentOverlayScreen.getByText(options.text)).toBeDefined();
141
+ expect(screen.getByText(options.text)).toBeDefined();
135
142
  });
136
143
  });
137
144
 
@@ -142,16 +149,16 @@ describe("when the close button is clicked on an open content overlay", () => {
142
149
  text: "I am text within the content overlay",
143
150
  showDismiss: true,
144
151
  };
145
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
152
+ await renderAndOpenContentOverlay(options);
146
153
 
154
+ const closeButton = await screen.findByTestId("ATL-Overlay-CloseButton");
155
+ await user.press(closeButton);
147
156
  await act(async () => {
148
- fireEvent.press(
149
- contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
150
- );
157
+ jest.runAllTimers();
151
158
  });
152
159
 
153
160
  await waitFor(() => {
154
- expect(contentOverlayScreen.queryByText(options.text)).toBeNull();
161
+ expect(screen.queryByText(options.text)).toBeNull();
155
162
  });
156
163
  });
157
164
  });
@@ -163,12 +170,12 @@ describe("when the close button is clicked on an open content overlay with a def
163
170
  onCloseCallback: jest.fn(),
164
171
  showDismiss: true,
165
172
  };
166
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
173
+ await renderAndOpenContentOverlay(options);
167
174
 
175
+ const closeButton = await screen.findByTestId("ATL-Overlay-CloseButton");
176
+ await user.press(closeButton);
168
177
  await act(async () => {
169
- fireEvent.press(
170
- contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
171
- );
178
+ jest.runAllTimers();
172
179
  });
173
180
 
174
181
  await waitFor(() => {
@@ -213,9 +220,9 @@ describe("when title prop passed to content overlay", () => {
213
220
  ...getDefaultOptions(),
214
221
  title: "Awesome Title",
215
222
  };
216
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
223
+ await renderAndOpenContentOverlay(options);
217
224
 
218
- expect(contentOverlayScreen.getByText(options.title)).toBeDefined();
225
+ expect(screen.getByText(options.title)).toBeDefined();
219
226
  });
220
227
  });
221
228
 
@@ -226,11 +233,9 @@ describe("when accessibilityLabel prop passed to content overlay", () => {
226
233
  a11yLabel: "Awesome a11y Label",
227
234
  showDismiss: true,
228
235
  };
229
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
236
+ await renderAndOpenContentOverlay(options);
230
237
 
231
- expect(
232
- contentOverlayScreen.getByLabelText(options.a11yLabel || "ohno"),
233
- ).toBeDefined();
238
+ expect(screen.getByLabelText(options.a11yLabel || "ohno")).toBeDefined();
234
239
  });
235
240
  });
236
241
 
@@ -241,10 +246,10 @@ describe("when accessibilityLabel prop NOT passed to content overlay", () => {
241
246
  title: "Awesome Title",
242
247
  showDismiss: true,
243
248
  };
244
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
249
+ await renderAndOpenContentOverlay(options);
245
250
 
246
251
  expect(
247
- contentOverlayScreen.getAllByLabelText(`Close ${options.title} modal`),
252
+ screen.getAllByLabelText(`Close ${options.title} modal`),
248
253
  ).toHaveLength(2);
249
254
  });
250
255
  });
@@ -258,11 +263,9 @@ describe("when there is a screen reader enabled", () => {
258
263
  const options: testRendererOptions = {
259
264
  ...getDefaultOptions(),
260
265
  };
261
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
266
+ await renderAndOpenContentOverlay(options);
262
267
 
263
- expect(
264
- await contentOverlayScreen.findByTestId("ATL-Overlay-CloseButton"),
265
- ).toBeDefined();
268
+ expect(await screen.findByTestId("ATL-Overlay-CloseButton")).toBeDefined();
266
269
  });
267
270
  });
268
271
 
@@ -272,10 +275,8 @@ describe("when fullScreen is set to true", () => {
272
275
  ...getDefaultOptions(),
273
276
  fullScreen: true,
274
277
  };
275
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
276
- expect(
277
- contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
278
- ).toBeDefined();
278
+ await renderAndOpenContentOverlay(options);
279
+ expect(screen.getByTestId("ATL-Overlay-CloseButton")).toBeDefined();
279
280
  });
280
281
  });
281
282
 
@@ -285,10 +286,8 @@ describe("when showDismiss is set to true", () => {
285
286
  ...getDefaultOptions(),
286
287
  showDismiss: true,
287
288
  };
288
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
289
- expect(
290
- contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
291
- ).toBeDefined();
289
+ await renderAndOpenContentOverlay(options);
290
+ expect(screen.getByTestId("ATL-Overlay-CloseButton")).toBeDefined();
292
291
  });
293
292
  });
294
293
 
@@ -299,13 +298,9 @@ describe("when the close button is clicked on an open content overlay with a def
299
298
  onBeforeExitCallback: jest.fn(),
300
299
  showDismiss: true,
301
300
  };
302
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
301
+ await renderAndOpenContentOverlay(options);
303
302
 
304
- await act(async () => {
305
- fireEvent.press(
306
- contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
307
- );
308
- });
303
+ await user.press(screen.getByTestId("ATL-Overlay-CloseButton"));
309
304
 
310
305
  await waitFor(() => {
311
306
  expect(options.onBeforeExitCallback).toHaveBeenCalled();
@@ -319,10 +314,9 @@ describe("modalBackgroundColor prop", () => {
319
314
  const options: testRendererOptions = {
320
315
  ...getDefaultOptions(),
321
316
  };
322
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
323
- const OverlayHeader = contentOverlayScreen.getByTestId(
324
- "ATL-Overlay-Header",
325
- ).children[0] as ReactTestInstance;
317
+ await renderAndOpenContentOverlay(options);
318
+ const OverlayHeader = screen.getByTestId("ATL-Overlay-Header")
319
+ .children[0] as ReactTestInstance;
326
320
  const OverlayHeaderStyles = OverlayHeader.props.style;
327
321
 
328
322
  expect(OverlayHeaderStyles).toEqual(
@@ -349,10 +343,9 @@ describe("modalBackgroundColor prop", () => {
349
343
  ...getDefaultOptions(),
350
344
  modalBackgroundColor: "background",
351
345
  };
352
- const contentOverlayScreen = await renderAndOpenContentOverlay(options);
353
- const OverlayHeader = contentOverlayScreen.getByTestId(
354
- "ATL-Overlay-Header",
355
- ).children[0] as ReactTestInstance;
346
+ await renderAndOpenContentOverlay(options);
347
+ const OverlayHeader = screen.getByTestId("ATL-Overlay-Header")
348
+ .children[0] as ReactTestInstance;
356
349
  const OverlayHeaderStyles = OverlayHeader.props.style;
357
350
 
358
351
  expect(OverlayHeaderStyles).toEqual(
@@ -365,3 +358,88 @@ describe("modalBackgroundColor prop", () => {
365
358
  });
366
359
  });
367
360
  });
361
+
362
+ describe("scrollEnabled prop", () => {
363
+ describe("when scrollEnabled is false (default)", () => {
364
+ it("should render content in BottomSheetView", async () => {
365
+ const options: testRendererOptions = {
366
+ ...getDefaultOptions(),
367
+ };
368
+ await renderAndOpenContentOverlay(options);
369
+
370
+ expect(screen.getByText(options.text)).toBeDefined();
371
+ expect(screen.getByTestId("ATL-Overlay-Children")).toBeDefined();
372
+ });
373
+ });
374
+ });
375
+
376
+ describe("loading prop", () => {
377
+ describe("when loading is true", () => {
378
+ it("should show subdued heading text", async () => {
379
+ const overlayRef = createRef<ContentOverlayRef>();
380
+
381
+ render(
382
+ <AtlantisOverlayProvider>
383
+ <View>
384
+ <ContentOverlay
385
+ ref={overlayRef}
386
+ title="Loading Overlay"
387
+ loading={true}
388
+ showDismiss={true}
389
+ >
390
+ <Text>Loading content</Text>
391
+ </ContentOverlay>
392
+ </View>
393
+ </AtlantisOverlayProvider>,
394
+ );
395
+
396
+ await act(async () => {
397
+ overlayRef.current?.open?.();
398
+ });
399
+
400
+ await waitFor(() => {
401
+ expect(screen.getByText("Loading Overlay")).toBeDefined();
402
+ });
403
+ });
404
+ });
405
+ });
406
+
407
+ describe("onBeforeExit callback", () => {
408
+ describe("when close button is pressed with onBeforeExit defined", () => {
409
+ it("should call onBeforeExit instead of immediately closing", async () => {
410
+ const overlayRef = createRef<ContentOverlayRef>();
411
+ const onBeforeExitCallback = jest.fn();
412
+
413
+ render(
414
+ <AtlantisOverlayProvider>
415
+ <View>
416
+ <ContentOverlay
417
+ ref={overlayRef}
418
+ title="Confirmation Required"
419
+ onBeforeExit={onBeforeExitCallback}
420
+ showDismiss={true}
421
+ >
422
+ <Text>Must confirm to close</Text>
423
+ </ContentOverlay>
424
+ </View>
425
+ </AtlantisOverlayProvider>,
426
+ );
427
+
428
+ await act(async () => {
429
+ overlayRef.current?.open?.();
430
+ });
431
+
432
+ await waitFor(() => {
433
+ expect(screen.getByText("Must confirm to close")).toBeDefined();
434
+ });
435
+
436
+ const closeButton = screen.getByTestId("ATL-Overlay-CloseButton");
437
+ await user.press(closeButton);
438
+
439
+ await waitFor(() => {
440
+ expect(onBeforeExitCallback).toHaveBeenCalled();
441
+ expect(screen.getByText("Must confirm to close")).toBeDefined();
442
+ });
443
+ });
444
+ });
445
+ });