@jobber/components-native 0.89.5-JOB-140604-2d32fc8.44 → 0.89.5-JOB-140604-8777ec5.52

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 (229) hide show
  1. package/dist/package.json +7 -7
  2. package/dist/src/ActionItem/ActionItemGroup.js +1 -1
  3. package/dist/src/AutoLink/components/Link/Link.js +1 -1
  4. package/dist/src/BottomSheet/BottomSheet.js +4 -2
  5. package/dist/src/BottomSheet/hooks/useBottomSheetBackHandler.js +26 -0
  6. package/dist/src/Chip/Chip.js +12 -1
  7. package/dist/src/Chip/Chip.style.js +1 -1
  8. package/dist/src/ContentOverlay/ContentOverlay.js +3 -5
  9. package/dist/src/ErrorMessageWrapper/context/ErrorMessageProvider.js +1 -1
  10. package/dist/src/Form/Form.js +2 -1
  11. package/dist/src/Form/components/FormMessageBanner/FormMessageBanner.js +1 -1
  12. package/dist/src/Form/hooks/useInternalForm.js +6 -3
  13. package/dist/src/FormatFile/components/MediaView/MediaView.js +22 -5
  14. package/dist/src/InputDate/InputDate.js +2 -2
  15. package/dist/src/InputFieldWrapper/InputFieldWrapper.js +14 -12
  16. package/dist/src/InputFieldWrapper/components/Prefix/Prefix.js +5 -2
  17. package/dist/src/InputFieldWrapper/components/Suffix/Suffix.js +5 -2
  18. package/dist/src/InputPressable/InputPressable.js +20 -8
  19. package/dist/src/InputPressable/InputPressable.style.js +3 -0
  20. package/dist/src/InputText/InputText.js +22 -11
  21. package/dist/src/InputText/InputText.style.js +4 -0
  22. package/dist/src/InputTime/InputTime.js +2 -2
  23. package/dist/src/Menu/Menu.js +2 -2
  24. package/dist/src/Select/components/SelectPressable/SelectPressable.js +1 -1
  25. package/dist/src/utils/meta/meta.json +0 -1
  26. package/dist/tsconfig.build.tsbuildinfo +1 -1
  27. package/dist/types/src/ActionItem/ActionItem.d.ts +2 -1
  28. package/dist/types/src/ActionItem/ActionItemGroup.d.ts +2 -1
  29. package/dist/types/src/ActionItem/components/ActionItemContainer.d.ts +2 -1
  30. package/dist/types/src/ActionLabel/ActionLabel.d.ts +1 -1
  31. package/dist/types/src/ActivityIndicator/ActivityIndicator.d.ts +2 -1
  32. package/dist/types/src/AutoLink/AutoLink.d.ts +2 -1
  33. package/dist/types/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.d.ts +2 -1
  34. package/dist/types/src/AutoLink/components/Link/Link.d.ts +2 -1
  35. package/dist/types/src/Banner/Banner.d.ts +2 -1
  36. package/dist/types/src/Banner/components/BannerIcon/BannerIcon.d.ts +2 -1
  37. package/dist/types/src/BottomSheet/BottomSheet.d.ts +4 -9
  38. package/dist/types/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.d.ts +2 -1
  39. package/dist/types/src/BottomSheet/hooks/useBottomSheetBackHandler.d.ts +8 -0
  40. package/dist/types/src/Button/Button.d.ts +2 -1
  41. package/dist/types/src/Button/components/InternalButtonLoading/InternalButtonLoading.d.ts +1 -1
  42. package/dist/types/src/ButtonGroup/ButtonGroup.d.ts +2 -1
  43. package/dist/types/src/ButtonGroup/ButtonGroupAction.d.ts +4 -3
  44. package/dist/types/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.d.ts +3 -2
  45. package/dist/types/src/Card/Card.d.ts +2 -1
  46. package/dist/types/src/Card/components/InternalCardHeader.d.ts +2 -1
  47. package/dist/types/src/Checkbox/Checkbox.d.ts +2 -1
  48. package/dist/types/src/Checkbox/CheckboxGroup.d.ts +2 -1
  49. package/dist/types/src/Chip/Chip.d.ts +2 -1
  50. package/dist/types/src/Content/Content.d.ts +2 -1
  51. package/dist/types/src/ContentOverlay/UNSAFE_WrappedModalize.d.ts +1 -1
  52. package/dist/types/src/Disclosure/Disclosure.d.ts +1 -1
  53. package/dist/types/src/Divider/Divider.d.ts +2 -1
  54. package/dist/types/src/EmptyState/EmptyState.d.ts +2 -1
  55. package/dist/types/src/ErrorMessageWrapper/ErrorMessageWrapper.d.ts +2 -1
  56. package/dist/types/src/ErrorMessageWrapper/context/ErrorMessageProvider.d.ts +2 -1
  57. package/dist/types/src/ErrorMessageWrapper/context/types.d.ts +1 -1
  58. package/dist/types/src/Flex/Flex.d.ts +2 -1
  59. package/dist/types/src/Form/Form.d.ts +2 -1
  60. package/dist/types/src/Form/components/FormActionBar/FormActionBar.d.ts +3 -2
  61. package/dist/types/src/Form/components/FormBody/FormBody.d.ts +3 -2
  62. package/dist/types/src/Form/components/FormCache/FormCache.d.ts +2 -1
  63. package/dist/types/src/Form/components/FormErrorBanner/FormErrorBanner.d.ts +2 -1
  64. package/dist/types/src/Form/components/FormMask/FormMask.d.ts +2 -1
  65. package/dist/types/src/Form/components/FormMessage/FormMessage.d.ts +2 -1
  66. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.d.ts +2 -1
  67. package/dist/types/src/Form/components/FormMessageBanner/FormMessageBanner.d.ts +2 -1
  68. package/dist/types/src/Form/components/FormSaveButton/FormSaveButton.d.ts +2 -1
  69. package/dist/types/src/Form/context/AtlantisFormContext.d.ts +1 -1
  70. package/dist/types/src/Form/context/types.d.ts +2 -2
  71. package/dist/types/src/Form/hooks/useFormViewRefs.d.ts +2 -2
  72. package/dist/types/src/Form/hooks/useInternalForm.d.ts +5 -5
  73. package/dist/types/src/Form/types.d.ts +10 -4
  74. package/dist/types/src/FormField/FormField.d.ts +1 -1
  75. package/dist/types/src/FormatFile/FormatFile.d.ts +2 -1
  76. package/dist/types/src/FormatFile/components/ErrorIcon/ErrorIcon.d.ts +2 -1
  77. package/dist/types/src/FormatFile/components/FileView/FileView.d.ts +2 -1
  78. package/dist/types/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.d.ts +3 -2
  79. package/dist/types/src/FormatFile/components/MediaView/MediaView.d.ts +2 -1
  80. package/dist/types/src/FormatFile/components/ProgressBar/ProgressBar.d.ts +2 -1
  81. package/dist/types/src/Heading/Heading.d.ts +1 -1
  82. package/dist/types/src/Icon/Icon.d.ts +2 -1
  83. package/dist/types/src/IconButton/IconButton.d.ts +1 -1
  84. package/dist/types/src/InputCurrency/InputCurrency.d.ts +2 -1
  85. package/dist/types/src/InputDate/InputDate.d.ts +4 -2
  86. package/dist/types/src/InputFieldWrapper/InputFieldWrapper.d.ts +9 -2
  87. package/dist/types/src/InputFieldWrapper/components/ClearAction/ClearAction.d.ts +2 -1
  88. package/dist/types/src/InputFieldWrapper/components/Prefix/Prefix.d.ts +4 -4
  89. package/dist/types/src/InputFieldWrapper/components/Suffix/Suffix.d.ts +4 -4
  90. package/dist/types/src/InputPressable/InputPressable.d.ts +9 -1
  91. package/dist/types/src/InputPressable/InputPressable.style.d.ts +3 -0
  92. package/dist/types/src/InputSearch/InputSearch.d.ts +1 -1
  93. package/dist/types/src/InputSearch/components/FilterButton.d.ts +2 -1
  94. package/dist/types/src/InputText/InputText.d.ts +8 -0
  95. package/dist/types/src/InputText/InputText.style.d.ts +4 -0
  96. package/dist/types/src/InputText/context/InputAccessoriesProvider.d.ts +2 -1
  97. package/dist/types/src/InputTime/InputTime.d.ts +4 -2
  98. package/dist/types/src/Menu/Menu.d.ts +2 -1
  99. package/dist/types/src/Menu/components/MenuOption/MenuOption.d.ts +2 -1
  100. package/dist/types/src/Menu/components/Overlay/Overlay.d.ts +2 -1
  101. package/dist/types/src/Menu/types.d.ts +6 -1
  102. package/dist/types/src/ProgressBar/ProgressBar.d.ts +2 -1
  103. package/dist/types/src/ProgressBar/ProgressBarInner.d.ts +2 -1
  104. package/dist/types/src/Select/Select.d.ts +3 -2
  105. package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.d.ts +2 -1
  106. package/dist/types/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.d.ts +2 -1
  107. package/dist/types/src/Select/components/SelectInternalPicker/SelectInternalPicker.d.ts +2 -1
  108. package/dist/types/src/Select/components/SelectPressable/SelectPressable.d.ts +2 -1
  109. package/dist/types/src/StatusLabel/StatusLabel.d.ts +2 -1
  110. package/dist/types/src/Switch/Switch.d.ts +2 -1
  111. package/dist/types/src/Switch/components/BaseSwitch/BaseSwitch.d.ts +2 -1
  112. package/dist/types/src/Text/Text.d.ts +1 -1
  113. package/dist/types/src/TextList/TextList.d.ts +2 -1
  114. package/dist/types/src/ThumbnailList/ThumbnailList.d.ts +2 -1
  115. package/dist/types/src/Toast/Toast.d.ts +2 -1
  116. package/dist/types/src/Typography/Typography.d.ts +1 -1
  117. package/dist/types/src/Typography/TypographyGestureDetector.d.ts +2 -1
  118. package/dist/types/src/utils/test/MockSafeAreaProvider.d.ts +1 -1
  119. package/package.json +7 -7
  120. package/src/ActionItem/ActionItem.tsx +1 -1
  121. package/src/ActionItem/ActionItemGroup.tsx +1 -3
  122. package/src/ActionItem/components/ActionItemContainer.tsx +1 -1
  123. package/src/ActionLabel/ActionLabel.tsx +1 -1
  124. package/src/ActivityIndicator/ActivityIndicator.tsx +1 -3
  125. package/src/AutoLink/AutoLink.tsx +1 -1
  126. package/src/AutoLink/components/ComposeTextWithLinks/ComposeTextWithLinks.tsx +1 -1
  127. package/src/AutoLink/components/Link/Link.tsx +1 -5
  128. package/src/Banner/Banner.tsx +2 -2
  129. package/src/Banner/components/BannerIcon/BannerIcon.tsx +1 -1
  130. package/src/BottomSheet/BottomSheet.stories.tsx +0 -1
  131. package/src/BottomSheet/BottomSheet.tsx +9 -10
  132. package/src/BottomSheet/components/BottomSheetOption/BottomSheetOption.tsx +1 -1
  133. package/src/BottomSheet/hooks/useBottomSheetBackHandler.test.ts +90 -0
  134. package/src/BottomSheet/hooks/useBottomSheetBackHandler.ts +41 -0
  135. package/src/Button/Button.tsx +1 -1
  136. package/src/Button/components/InternalButtonLoading/InternalButtonLoading.tsx +1 -1
  137. package/src/ButtonGroup/ButtonGroup.tsx +1 -1
  138. package/src/ButtonGroup/ButtonGroupAction.tsx +4 -4
  139. package/src/ButtonGroup/components/SecondaryActionSheet/SecondaryActionSheet.tsx +2 -2
  140. package/src/Card/Card.tsx +1 -1
  141. package/src/Card/components/InternalCardHeader.tsx +1 -1
  142. package/src/Checkbox/Checkbox.tsx +2 -2
  143. package/src/Checkbox/CheckboxGroup.test.tsx +1 -1
  144. package/src/Checkbox/CheckboxGroup.tsx +2 -2
  145. package/src/Chip/Chip.style.ts +1 -1
  146. package/src/Chip/Chip.tsx +20 -2
  147. package/src/Content/Content.tsx +1 -1
  148. package/src/ContentOverlay/ContentOverlay.tsx +3 -4
  149. package/src/Disclosure/Disclosure.tsx +1 -1
  150. package/src/Divider/Divider.tsx +1 -1
  151. package/src/EmptyState/EmptyState.tsx +1 -1
  152. package/src/ErrorMessageWrapper/ErrorMessageWrapper.tsx +1 -1
  153. package/src/ErrorMessageWrapper/context/ErrorMessageProvider.tsx +3 -5
  154. package/src/ErrorMessageWrapper/context/types.ts +1 -1
  155. package/src/Flex/Flex.tsx +2 -2
  156. package/src/Form/Form.test.tsx +145 -2
  157. package/src/Form/Form.tsx +3 -1
  158. package/src/Form/components/FormActionBar/FormActionBar.tsx +3 -3
  159. package/src/Form/components/FormBody/FormBody.tsx +3 -3
  160. package/src/Form/components/FormCache/FormCache.tsx +1 -1
  161. package/src/Form/components/FormErrorBanner/FormErrorBanner.tsx +1 -1
  162. package/src/Form/components/FormMask/FormMask.tsx +1 -1
  163. package/src/Form/components/FormMessage/FormMessage.tsx +1 -1
  164. package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.tsx +1 -1
  165. package/src/Form/components/FormMessageBanner/FormMessageBanner.tsx +1 -3
  166. package/src/Form/components/FormSaveButton/FormSaveButton.tsx +1 -1
  167. package/src/Form/context/types.ts +2 -2
  168. package/src/Form/hooks/useFormViewRefs.ts +4 -5
  169. package/src/Form/hooks/useInternalForm.ts +12 -5
  170. package/src/Form/types.ts +11 -4
  171. package/src/FormField/FormField.tsx +1 -1
  172. package/src/FormatFile/FormatFile.tsx +3 -3
  173. package/src/FormatFile/components/ErrorIcon/ErrorIcon.tsx +1 -1
  174. package/src/FormatFile/components/FileView/FileView.tsx +1 -1
  175. package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.tsx +2 -2
  176. package/src/FormatFile/components/MediaView/MediaView.test.tsx +283 -0
  177. package/src/FormatFile/components/MediaView/MediaView.tsx +28 -7
  178. package/src/FormatFile/components/ProgressBar/ProgressBar.tsx +1 -1
  179. package/src/Heading/Heading.tsx +1 -1
  180. package/src/Icon/Icon.tsx +1 -1
  181. package/src/IconButton/IconButton.tsx +1 -1
  182. package/src/InputCurrency/InputCurrency.tsx +1 -1
  183. package/src/InputDate/InputDate.tsx +7 -3
  184. package/src/InputFieldWrapper/InputFieldWrapper.test.tsx +48 -1
  185. package/src/InputFieldWrapper/InputFieldWrapper.tsx +39 -29
  186. package/src/InputFieldWrapper/components/ClearAction/ClearAction.tsx +1 -1
  187. package/src/InputFieldWrapper/components/Prefix/Prefix.test.tsx +3 -5
  188. package/src/InputFieldWrapper/components/Prefix/Prefix.tsx +8 -6
  189. package/src/InputFieldWrapper/components/Suffix/Suffix.test.tsx +2 -4
  190. package/src/InputFieldWrapper/components/Suffix/Suffix.tsx +8 -6
  191. package/src/InputPassword/InputPassword.tsx +1 -1
  192. package/src/InputPressable/InputPressable.style.ts +4 -0
  193. package/src/InputPressable/InputPressable.test.tsx +75 -1
  194. package/src/InputPressable/InputPressable.tsx +34 -8
  195. package/src/InputSearch/InputSearch.tsx +1 -0
  196. package/src/InputSearch/components/FilterButton.tsx +1 -1
  197. package/src/InputText/InputText.style.ts +5 -0
  198. package/src/InputText/InputText.test.tsx +75 -0
  199. package/src/InputText/InputText.tsx +32 -12
  200. package/src/InputText/context/InputAccessoriesProvider.tsx +1 -1
  201. package/src/InputTime/InputTime.tsx +7 -3
  202. package/src/Menu/Menu.tsx +3 -3
  203. package/src/Menu/components/MenuOption/MenuOption.tsx +1 -1
  204. package/src/Menu/components/Overlay/Overlay.tsx +1 -1
  205. package/src/Menu/types.ts +7 -1
  206. package/src/ProgressBar/ProgressBar.tsx +1 -1
  207. package/src/ProgressBar/ProgressBarInner.tsx +1 -1
  208. package/src/Select/Select.tsx +2 -2
  209. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.ios.tsx +1 -1
  210. package/src/Select/components/SelectDefaultPicker/SelectDefaultPicker.tsx +1 -1
  211. package/src/Select/components/SelectInternalPicker/SelectInternalPicker.tsx +1 -1
  212. package/src/Select/components/SelectPressable/SelectPressable.tsx +1 -4
  213. package/src/StatusLabel/StatusLabel.tsx +1 -1
  214. package/src/Switch/Switch.tsx +1 -1
  215. package/src/Switch/components/BaseSwitch/BaseSwitch.tsx +1 -1
  216. package/src/Text/Text.tsx +1 -1
  217. package/src/TextList/TextList.tsx +1 -1
  218. package/src/ThumbnailList/ThumbnailList.tsx +1 -1
  219. package/src/Toast/Toast.tsx +2 -2
  220. package/src/Typography/Typography.tsx +1 -1
  221. package/src/Typography/TypographyGestureDetector.tsx +1 -3
  222. package/src/utils/meta/meta.json +0 -1
  223. package/src/utils/test/MockSafeAreaProvider.tsx +1 -1
  224. package/dist/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.js +0 -45
  225. package/dist/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.js +0 -8
  226. package/dist/types/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.d.ts +0 -9
  227. package/dist/types/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.d.ts +0 -5
  228. package/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.ts +0 -9
  229. package/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.tsx +0 -89
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { type ReactElement } from "react";
2
2
  import { act, fireEvent, render, waitFor } from "@testing-library/react-native";
3
3
  import { Alert, Keyboard } from "react-native";
4
4
  import { Host } from "react-native-portalize";
@@ -94,7 +94,7 @@ interface FormTestProps {
94
94
  onSubmit: () => void,
95
95
  label: string | undefined,
96
96
  isSubmitting: boolean,
97
- ) => JSX.Element;
97
+ ) => ReactElement;
98
98
  readonly initialLoading?: boolean;
99
99
  readonly initialValues?: FormFields;
100
100
  readonly bannerMessages?: FormBannerMessage[];
@@ -104,6 +104,7 @@ interface FormTestProps {
104
104
  readonly onBeforeSubmit?: jest.Mock;
105
105
  readonly renderFooter?: React.ReactNode;
106
106
  readonly saveButtonOffset?: number;
107
+ readonly UNSAFE_allowDiscardLocalCacheWhenOffline?: boolean;
107
108
  }
108
109
 
109
110
  function FormTest(props: FormTestProps) {
@@ -125,6 +126,7 @@ function MockForm({
125
126
  localCacheId,
126
127
  renderFooter,
127
128
  saveButtonOffset,
129
+ UNSAFE_allowDiscardLocalCacheWhenOffline = false,
128
130
  }: FormTestProps) {
129
131
  const formErrors: FormBannerErrors = {};
130
132
 
@@ -154,6 +156,9 @@ function MockForm({
154
156
  onBeforeSubmit={onBeforeSubmit}
155
157
  renderFooter={renderFooter}
156
158
  saveButtonOffset={saveButtonOffset}
159
+ UNSAFE_allowDiscardLocalCacheWhenOffline={
160
+ UNSAFE_allowDiscardLocalCacheWhenOffline
161
+ }
157
162
  >
158
163
  <InputText
159
164
  name={testInputTextName}
@@ -561,4 +566,142 @@ describe("Form", () => {
561
566
  expect(queryByTestId("ATL-FormSafeArea")).toBeNull();
562
567
  });
563
568
  });
569
+
570
+ describe("Leaving the form", () => {
571
+ let mockUseConfirmBeforeBack: jest.Mock;
572
+ let mockRemoveLocalCache: jest.Mock;
573
+ const atlantisContextSpy = jest.spyOn(
574
+ atlantisContext,
575
+ "useAtlantisContext",
576
+ );
577
+
578
+ beforeEach(() => {
579
+ mockUseConfirmBeforeBack = jest
580
+ .fn()
581
+ .mockReturnValue({ current: jest.fn() });
582
+ mockRemoveLocalCache = jest.fn();
583
+
584
+ jest
585
+ .spyOn(
586
+ require("../Form/context/AtlantisFormContext"),
587
+ "useAtlantisFormContext",
588
+ )
589
+ .mockReturnValue({
590
+ useConfirmBeforeBack: mockUseConfirmBeforeBack,
591
+ useInternalFormLocalCache: () => ({
592
+ setLocalCache: jest.fn(),
593
+ removeLocalCache: mockRemoveLocalCache,
594
+ }),
595
+ edgeToEdgeEnabled: false,
596
+ });
597
+ });
598
+
599
+ afterEach(() => {
600
+ jest.restoreAllMocks();
601
+ });
602
+
603
+ describe("when UNSAFE_allowDiscardLocalCacheWhenOffline is false", () => {
604
+ it("should NOT pass onAcceptEvent when offline", () => {
605
+ atlantisContextSpy.mockReturnValue({
606
+ ...atlantisContextDefaultValues,
607
+ isOnline: false,
608
+ });
609
+
610
+ render(
611
+ <FormTest
612
+ onSubmit={onSubmitMock}
613
+ UNSAFE_allowDiscardLocalCacheWhenOffline={false}
614
+ localCacheKey="testCacheKey"
615
+ />,
616
+ );
617
+
618
+ expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
619
+ const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
620
+ expect(callArgs.onAcceptEvent).toBeUndefined();
621
+ expect(callArgs.showLostProgressMessage).toBe(false);
622
+ });
623
+
624
+ it("should pass onAcceptEvent when online", () => {
625
+ atlantisContextSpy.mockReturnValue({
626
+ ...atlantisContextDefaultValues,
627
+ isOnline: true,
628
+ });
629
+
630
+ render(
631
+ <FormTest
632
+ onSubmit={onSubmitMock}
633
+ UNSAFE_allowDiscardLocalCacheWhenOffline={false}
634
+ localCacheKey="testCacheKey"
635
+ />,
636
+ );
637
+
638
+ expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
639
+ const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
640
+ expect(callArgs.onAcceptEvent).toBe(mockRemoveLocalCache);
641
+ expect(callArgs.showLostProgressMessage).toBe(true);
642
+ });
643
+ });
644
+
645
+ describe("when UNSAFE_allowDiscardLocalCacheWhenOffline is true", () => {
646
+ it("should pass onAcceptEvent when offline", () => {
647
+ atlantisContextSpy.mockReturnValue({
648
+ ...atlantisContextDefaultValues,
649
+ isOnline: false,
650
+ });
651
+
652
+ render(
653
+ <FormTest
654
+ onSubmit={onSubmitMock}
655
+ UNSAFE_allowDiscardLocalCacheWhenOffline={true}
656
+ localCacheKey="testCacheKey"
657
+ />,
658
+ );
659
+
660
+ expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
661
+ const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
662
+ expect(callArgs.onAcceptEvent).toBe(mockRemoveLocalCache);
663
+ expect(callArgs.showLostProgressMessage).toBe(true);
664
+ });
665
+
666
+ it("should pass onAcceptEvent when online", () => {
667
+ atlantisContextSpy.mockReturnValue({
668
+ ...atlantisContextDefaultValues,
669
+ isOnline: true,
670
+ });
671
+
672
+ render(
673
+ <FormTest
674
+ onSubmit={onSubmitMock}
675
+ UNSAFE_allowDiscardLocalCacheWhenOffline={true}
676
+ localCacheKey="testCacheKey"
677
+ />,
678
+ );
679
+
680
+ expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
681
+ const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
682
+ expect(callArgs.onAcceptEvent).toBe(mockRemoveLocalCache);
683
+ expect(callArgs.showLostProgressMessage).toBe(true);
684
+ });
685
+ });
686
+
687
+ describe("without localCacheKey", () => {
688
+ it("should always show lost progress message when no cache key is provided", () => {
689
+ atlantisContextSpy.mockReturnValue({
690
+ ...atlantisContextDefaultValues,
691
+ isOnline: false,
692
+ });
693
+
694
+ render(
695
+ <FormTest
696
+ onSubmit={onSubmitMock}
697
+ UNSAFE_allowDiscardLocalCacheWhenOffline={false}
698
+ />,
699
+ );
700
+
701
+ expect(mockUseConfirmBeforeBack).toHaveBeenCalled();
702
+ const callArgs = mockUseConfirmBeforeBack.mock.calls[0][0];
703
+ expect(callArgs.showLostProgressMessage).toBe(true);
704
+ });
705
+ });
706
+ });
564
707
  });
package/src/Form/Form.tsx CHANGED
@@ -34,7 +34,7 @@ import { ErrorMessageProvider } from "../ErrorMessageWrapper";
34
34
  export function Form<T extends FieldValues, S>({
35
35
  initialLoading,
36
36
  ...rest
37
- }: FormProps<T, S>): JSX.Element {
37
+ }: FormProps<T, S>) {
38
38
  const child = initialLoading ? <FormMask /> : <InternalForm {...rest} />;
39
39
 
40
40
  return (
@@ -66,6 +66,7 @@ function InternalForm<T extends FieldValues, S>({
66
66
  saveButtonOffset,
67
67
  showStickySaveButton = false,
68
68
  renderFooter,
69
+ UNSAFE_allowDiscardLocalCacheWhenOffline,
69
70
  }: InternalFormProps<T, S>) {
70
71
  const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
71
72
  const [saveButtonHeight, setSaveButtonHeight] = useState(0);
@@ -87,6 +88,7 @@ function InternalForm<T extends FieldValues, S>({
87
88
  scrollViewRef,
88
89
  saveButtonHeight,
89
90
  messageBannerHeight,
91
+ UNSAFE_allowDiscardLocalCacheWhenOffline,
90
92
  });
91
93
  const { windowHeight, headerHeight } = useScreenInformation();
92
94
  const [keyboardHeight, setKeyboardHeight] = useState(0);
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { type ReactElement } from "react";
2
2
  import type { LayoutChangeEvent } from "react-native";
3
3
  import { StyleSheet } from "react-native";
4
4
  import Reanimated from "react-native-reanimated";
@@ -18,7 +18,7 @@ export interface FormActionBarProps {
18
18
  onSubmit: () => void,
19
19
  label: string | undefined,
20
20
  isSubmitting: boolean,
21
- ) => JSX.Element;
21
+ ) => ReactElement;
22
22
  readonly secondaryActions?: SecondaryActionProp[];
23
23
  readonly setSecondaryActionLoading?: (bool: boolean) => void;
24
24
  }
@@ -32,7 +32,7 @@ export function FormActionBar({
32
32
  setSaveButtonHeight,
33
33
  secondaryActions,
34
34
  setSecondaryActionLoading,
35
- }: FormActionBarProps): JSX.Element {
35
+ }: FormActionBarProps) {
36
36
  const styles = useStyles();
37
37
 
38
38
  const buttonStyle = StyleSheet.flatten([
@@ -1,4 +1,4 @@
1
- import React, { useMemo } from "react";
1
+ import React, { type ReactElement, useMemo } from "react";
2
2
  import { View } from "react-native";
3
3
  import { useStyles } from "./FormBody.style";
4
4
  import { useScreenInformation } from "../../hooks/useScreenInformation";
@@ -7,7 +7,7 @@ import { FormActionBar } from "../FormActionBar";
7
7
  import { tokens } from "../../../utils/design";
8
8
 
9
9
  interface FormBodyProps extends FormActionBarProps {
10
- readonly children: JSX.Element;
10
+ readonly children: ReactElement;
11
11
  readonly shouldRenderActionBar?: boolean;
12
12
  readonly saveButtonOffset?: number;
13
13
  }
@@ -24,7 +24,7 @@ export function FormBody({
24
24
  setSecondaryActionLoading,
25
25
  setSaveButtonHeight,
26
26
  saveButtonOffset,
27
- }: FormBodyProps): JSX.Element {
27
+ }: FormBodyProps) {
28
28
  const paddingBottom = useBottomPadding();
29
29
  const fullViewPadding = useMemo(() => ({ paddingBottom }), [paddingBottom]);
30
30
  const styles = useStyles();
@@ -14,7 +14,7 @@ export function FormCache<T extends FieldValues>({
14
14
  localCacheExclude,
15
15
  localCacheKey,
16
16
  setLocalCache,
17
- }: FormCacheProps<T>): JSX.Element {
17
+ }: FormCacheProps<T>) {
18
18
  const { control, formState } = useFormContext<T>();
19
19
  const { isDirty } = formState;
20
20
 
@@ -6,7 +6,7 @@ import { useAtlantisI18n } from "../../../hooks/useAtlantisI18n";
6
6
  export function FormErrorBanner({
7
7
  networkError,
8
8
  bannerError,
9
- }: FormBannerErrors): JSX.Element {
9
+ }: FormBannerErrors) {
10
10
  const { t } = useAtlantisI18n();
11
11
 
12
12
  if (networkError) {
@@ -4,7 +4,7 @@ import { useStyles } from "./FormMask.style";
4
4
  import { ActivityIndicator } from "../../../ActivityIndicator";
5
5
  import { useAtlantisI18n } from "../../../hooks/useAtlantisI18n";
6
6
 
7
- export function FormMask(): JSX.Element {
7
+ export function FormMask() {
8
8
  const { t } = useAtlantisI18n();
9
9
  const styles = useStyles();
10
10
 
@@ -17,7 +17,7 @@ let close: (() => void) | undefined;
17
17
  * message to the user. Use `FormMessage.close()` to close the most
18
18
  * recent message.
19
19
  */
20
- export const FormMessage = (): JSX.Element => {
20
+ export const FormMessage = () => {
21
21
  const [data, setData] = useState<FormMessageData[]>([]);
22
22
 
23
23
  open = useCallback(
@@ -15,7 +15,7 @@ interface FormMessageProps {
15
15
  export function InternalFormMessage({
16
16
  data,
17
17
  onRequestClose,
18
- }: FormMessageProps): JSX.Element {
18
+ }: FormMessageProps) {
19
19
  const { t } = useAtlantisI18n();
20
20
  const styles = useStyles();
21
21
  const emptyStateData: EmptyStateProps = useMemo(() => {
@@ -8,9 +8,7 @@ interface FormMessageBannerProps {
8
8
  readonly bannerMessages?: FormBannerMessage[];
9
9
  }
10
10
 
11
- export function FormMessageBanner({
12
- bannerMessages,
13
- }: FormMessageBannerProps): JSX.Element {
11
+ export function FormMessageBanner({ bannerMessages }: FormMessageBannerProps) {
14
12
  return (
15
13
  <>
16
14
  {bannerMessages?.map((message, index) => (
@@ -13,7 +13,7 @@ export function FormSaveButton({
13
13
  setSecondaryActionLoading,
14
14
  onOpenBottomSheet,
15
15
  onCloseBottomSheet,
16
- }: FormSaveButtonProps): JSX.Element {
16
+ }: FormSaveButtonProps) {
17
17
  const { t } = useAtlantisI18n();
18
18
  const formContext = useFormContext();
19
19
  const buttonActions = useButtonGroupAction(secondaryActions);
@@ -1,4 +1,4 @@
1
- import type { MutableRefObject } from "react";
1
+ import type { RefObject } from "react";
2
2
  import type { DeepPartial, FieldValues, UseFormReturn } from "react-hook-form";
3
3
 
4
4
  export interface UseConfirmBeforeBackProps {
@@ -21,7 +21,7 @@ interface LocalCacheOptions {
21
21
  export interface AtlantisFormContextProps {
22
22
  useConfirmBeforeBack: (
23
23
  props: UseConfirmBeforeBackProps,
24
- ) => MutableRefObject<() => void>;
24
+ ) => RefObject<() => void>;
25
25
  useInternalFormLocalCache: <TData extends FieldValues>(
26
26
  formMethods: UseFormReturn<TData>,
27
27
  cacheKey?: string,
@@ -4,15 +4,14 @@ import type { View } from "react-native";
4
4
  import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
5
5
 
6
6
  interface UseFormViewRefsReturn {
7
- readonly scrollViewRef: RefObject<KeyboardAwareScrollView>;
8
- readonly bottomViewRef: RefObject<View>;
7
+ readonly scrollViewRef: RefObject<KeyboardAwareScrollView | null>;
8
+ readonly bottomViewRef: RefObject<View | null>;
9
9
  readonly scrollToTop: () => void;
10
10
  }
11
11
 
12
12
  export function useFormViewRefs(): UseFormViewRefsReturn {
13
- const scrollViewRef: RefObject<KeyboardAwareScrollView> =
14
- useRef<KeyboardAwareScrollView>(null);
15
- const bottomViewRef: RefObject<View> = useRef<View>(null);
13
+ const scrollViewRef = useRef<KeyboardAwareScrollView>(null);
14
+ const bottomViewRef = useRef<View>(null);
16
15
  const scrollToTop = useCallback(() => {
17
16
  scrollViewRef.current?.scrollToPosition(0, 0);
18
17
  }, [scrollViewRef]);
@@ -5,7 +5,7 @@ import type {
5
5
  UseFormReturn,
6
6
  } from "react-hook-form";
7
7
  import { useForm } from "react-hook-form";
8
- import type { MutableRefObject, RefObject } from "react";
8
+ import type { RefObject } from "react";
9
9
  import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
10
10
  import { useAtlantisContext } from "../../AtlantisContext";
11
11
  import { useAtlantisFormContext } from "../context/AtlantisFormContext";
@@ -20,8 +20,9 @@ type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<
20
20
  | "localCacheKey"
21
21
  | "localCacheExclude"
22
22
  | "localCacheId"
23
+ | "UNSAFE_allowDiscardLocalCacheWhenOffline"
23
24
  > & {
24
- scrollViewRef?: RefObject<KeyboardAwareScrollView>;
25
+ scrollViewRef?: RefObject<KeyboardAwareScrollView | null>;
25
26
  readonly saveButtonHeight: number;
26
27
  readonly messageBannerHeight: number;
27
28
  };
@@ -31,7 +32,7 @@ interface UseInternalForm<T extends FieldValues> {
31
32
  readonly handleSubmit: UseFormHandleSubmit<T>;
32
33
  readonly isSubmitting: boolean;
33
34
  readonly isDirty: boolean;
34
- readonly removeListenerRef: MutableRefObject<() => void>;
35
+ readonly removeListenerRef: RefObject<() => void>;
35
36
  readonly setLocalCache: (data: DeepPartial<T>) => void;
36
37
  }
37
38
 
@@ -45,6 +46,7 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
45
46
  scrollViewRef,
46
47
  saveButtonHeight,
47
48
  messageBannerHeight,
49
+ UNSAFE_allowDiscardLocalCacheWhenOffline = false,
48
50
  }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T> {
49
51
  const { useConfirmBeforeBack, useInternalFormLocalCache } =
50
52
  useAtlantisFormContext();
@@ -82,11 +84,16 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
82
84
  };
83
85
  }
84
86
 
87
+ const shouldRemoveCacheOnBack = UNSAFE_allowDiscardLocalCacheWhenOffline
88
+ ? true
89
+ : isOnline;
90
+
85
91
  const removeListenerRef = useConfirmBeforeBack({
86
92
  alwaysPreventBack: isSubmitting,
87
93
  shouldShowAlert: isDirty,
88
- onAcceptEvent: isOnline ? removeLocalCache : undefined,
89
- showLostProgressMessage: isOnline || !clientSideSaveOn ? true : false,
94
+ onAcceptEvent: shouldRemoveCacheOnBack ? removeLocalCache : undefined,
95
+ showLostProgressMessage:
96
+ shouldRemoveCacheOnBack || !clientSideSaveOn ? true : false,
90
97
  });
91
98
 
92
99
  return {
package/src/Form/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { MutableRefObject, RefObject } from "react";
1
+ import type { ReactElement, RefObject } from "react";
2
2
  import type {
3
3
  ControllerProps,
4
4
  DefaultValues,
@@ -54,7 +54,7 @@ interface FormNoticeMessage {
54
54
 
55
55
  export type FormRef<T extends FieldValues = FieldValues> =
56
56
  | (UseFormReturn<T> & {
57
- scrollViewRef?: RefObject<KeyboardAwareScrollView>;
57
+ scrollViewRef?: RefObject<KeyboardAwareScrollView | null>;
58
58
  saveButtonHeight?: number;
59
59
  messageBannerHeight?: number;
60
60
  })
@@ -127,7 +127,7 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
127
127
  /**
128
128
  * ref object to access react hook form methods and state
129
129
  */
130
- formRef?: MutableRefObject<FormRef<T> | undefined>;
130
+ formRef?: RefObject<FormRef<T> | undefined>;
131
131
 
132
132
  /**
133
133
  * Label to be displayed for the save button
@@ -142,7 +142,7 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
142
142
  onSubmit: () => void,
143
143
  label: string | undefined,
144
144
  isSubmitting: boolean,
145
- ) => JSX.Element;
145
+ ) => ReactElement;
146
146
 
147
147
  /**
148
148
  * Adding a key will save a local copy of the form data that will be used to
@@ -171,6 +171,13 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
171
171
  */
172
172
  localCacheId?: string | string[];
173
173
 
174
+ /**
175
+ * If true, the local cache will be removed when the user navigates away from
176
+ * the dirty form even when offline. By default, cache is only removed on back when online.
177
+ * Defaults to false.
178
+ */
179
+ UNSAFE_allowDiscardLocalCacheWhenOffline?: boolean;
180
+
174
181
  /**
175
182
  * Secondary Action for ButtonGroup
176
183
  */
@@ -39,7 +39,7 @@ export function FormField<T>({
39
39
  children,
40
40
  defaultValue: value,
41
41
  validations,
42
- }: FormFieldProps<T>): JSX.Element {
42
+ }: FormFieldProps<T>) {
43
43
  const { error, field } = useFormController({
44
44
  name,
45
45
  value,
@@ -88,7 +88,7 @@ function FormatFileContent({
88
88
  styleInGrid,
89
89
  onUploadComplete,
90
90
  isMedia,
91
- }: FormatFileContentProps): JSX.Element {
91
+ }: FormatFileContentProps) {
92
92
  const styles = useStyles();
93
93
 
94
94
  return (
@@ -204,7 +204,7 @@ export function FormatFile<T extends File | FileUpload>({
204
204
  showFileTypeIndicator = true,
205
205
  createThumbnail,
206
206
  onPreviewPress,
207
- }: FormatFileProps<T>): JSX.Element {
207
+ }: FormatFileProps<T>) {
208
208
  const onTapModified = onTap ? () => onTap(file) : () => undefined;
209
209
 
210
210
  const formattedFile = parseFile(file, showFileTypeIndicator);
@@ -236,7 +236,7 @@ function FormatFileInternal({
236
236
  onPreviewPress,
237
237
  testID,
238
238
  createThumbnail: createThumbnailProp,
239
- }: FormatFileInternalProps): JSX.Element {
239
+ }: FormatFileInternalProps) {
240
240
  const [showOverlay, setShowOverlay] = useState<boolean>(
241
241
  file.status !== StatusCode.Completed,
242
242
  );
@@ -3,7 +3,7 @@ import { View } from "react-native";
3
3
  import { useStyles } from "./ErrorIcon.style";
4
4
  import { Icon } from "../../../Icon";
5
5
 
6
- export function ErrorIcon(): JSX.Element {
6
+ export function ErrorIcon() {
7
7
  const styles = useStyles();
8
8
 
9
9
  return (
@@ -27,7 +27,7 @@ export function FileView({
27
27
  showOverlay,
28
28
  showError,
29
29
  onUploadComplete,
30
- }: FileViewProps): JSX.Element {
30
+ }: FileViewProps) {
31
31
  const { t } = useAtlantisI18n();
32
32
  const styles = useStyles();
33
33
 
@@ -9,7 +9,7 @@ import { useAtlantisI18n } from "../../../hooks/useAtlantisI18n";
9
9
  export type BottomSheetOptionsSuffix = "receipt" | "image" | "file" | "video";
10
10
 
11
11
  interface FormatFileBottomSheetProps {
12
- readonly bottomSheetRef: RefObject<BottomSheetRef>;
12
+ readonly bottomSheetRef: RefObject<BottomSheetRef | null>;
13
13
  readonly onPreviewPress?: () => void;
14
14
  readonly onRemovePress?: () => void;
15
15
  readonly bottomSheetOptionsSuffix?: BottomSheetOptionsSuffix;
@@ -20,7 +20,7 @@ export const FormatFileBottomSheet = ({
20
20
  onPreviewPress,
21
21
  onRemovePress,
22
22
  bottomSheetOptionsSuffix,
23
- }: FormatFileBottomSheetProps): JSX.Element => {
23
+ }: FormatFileBottomSheetProps) => {
24
24
  const { t } = useAtlantisI18n();
25
25
 
26
26
  const handlePress = (onPressAction: () => void) => {