@jobber/components-native 0.39.0 → 0.40.1

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 (136) hide show
  1. package/dist/src/Form/Form.js +187 -0
  2. package/dist/src/Form/Form.style.js +33 -0
  3. package/dist/src/Form/components/FormActionBar/FormActionBar.js +21 -0
  4. package/dist/src/Form/components/FormActionBar/FormActionBar.style.js +5 -0
  5. package/dist/src/Form/components/FormActionBar/index.js +1 -0
  6. package/dist/src/Form/components/FormBody/FormBody.js +20 -0
  7. package/dist/src/Form/components/FormBody/FormBody.style.js +26 -0
  8. package/dist/src/Form/components/FormBody/index.js +1 -0
  9. package/dist/src/Form/components/FormCache/FormCache.js +34 -0
  10. package/dist/src/Form/components/FormErrorBanner/FormErrorBanner.js +21 -0
  11. package/dist/src/Form/components/FormErrorBanner/index.js +1 -0
  12. package/dist/src/Form/components/FormErrorBanner/messages.js +13 -0
  13. package/dist/src/Form/components/FormMask/FormMask.js +11 -0
  14. package/dist/src/Form/components/FormMask/FormMask.style.js +15 -0
  15. package/dist/src/Form/components/FormMask/index.js +1 -0
  16. package/dist/src/Form/components/FormMessage/FormMessage.js +48 -0
  17. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.js +28 -0
  18. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.style.js +17 -0
  19. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/index.js +1 -0
  20. package/dist/src/Form/components/FormMessage/components/InternalFormMessage/messages.js +8 -0
  21. package/dist/src/Form/components/FormMessage/index.js +1 -0
  22. package/dist/src/Form/components/FormMessageBanner/FormMessageBanner.js +15 -0
  23. package/dist/src/Form/components/FormMessageBanner/index.js +1 -0
  24. package/dist/src/Form/components/FormSaveButton/FormSaveButton.js +69 -0
  25. package/dist/src/Form/components/FormSaveButton/index.js +1 -0
  26. package/dist/src/Form/components/FormSaveButton/messages.js +8 -0
  27. package/dist/src/Form/constants.js +2 -0
  28. package/dist/src/Form/context/AtlantisFormContext.js +16 -0
  29. package/dist/src/Form/context/index.js +1 -0
  30. package/dist/src/Form/context/types.js +1 -0
  31. package/dist/src/Form/hooks/useFormViewRefs.js +14 -0
  32. package/dist/src/Form/hooks/useInternalForm.js +37 -0
  33. package/dist/src/Form/hooks/useOfflineHandler.js +24 -0
  34. package/dist/src/Form/hooks/useSaveButtonPosition.js +25 -0
  35. package/dist/src/Form/hooks/useScreenInformation.js +15 -0
  36. package/dist/src/Form/hooks/useScrollToError/index.js +1 -0
  37. package/dist/src/Form/hooks/useScrollToError/useScrollToError.js +63 -0
  38. package/dist/src/Form/index.js +4 -0
  39. package/dist/src/Form/messages.js +28 -0
  40. package/dist/src/Form/types.js +10 -0
  41. package/dist/src/InputText/InputText.js +8 -1
  42. package/dist/src/index.js +1 -0
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/dist/types/src/Form/Form.d.ts +4 -0
  45. package/dist/types/src/Form/Form.style.d.ts +31 -0
  46. package/dist/types/src/Form/components/FormActionBar/FormActionBar.d.ts +13 -0
  47. package/dist/types/src/Form/components/FormActionBar/FormActionBar.style.d.ts +15 -0
  48. package/dist/types/src/Form/components/FormActionBar/index.d.ts +2 -0
  49. package/dist/types/src/Form/components/FormBody/FormBody.d.ts +10 -0
  50. package/dist/types/src/Form/components/FormBody/FormBody.style.d.ts +24 -0
  51. package/dist/types/src/Form/components/FormBody/index.d.ts +1 -0
  52. package/dist/types/src/Form/components/FormCache/FormCache.d.ts +10 -0
  53. package/dist/types/src/Form/components/FormErrorBanner/FormErrorBanner.d.ts +3 -0
  54. package/dist/types/src/Form/components/FormErrorBanner/index.d.ts +1 -0
  55. package/dist/types/src/Form/components/FormErrorBanner/messages.d.ts +12 -0
  56. package/dist/types/src/Form/components/FormMask/FormMask.d.ts +2 -0
  57. package/dist/types/src/Form/components/FormMask/FormMask.style.d.ts +13 -0
  58. package/dist/types/src/Form/components/FormMask/index.d.ts +1 -0
  59. package/dist/types/src/Form/components/FormMessage/FormMessage.d.ts +19 -0
  60. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.d.ts +8 -0
  61. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.style.d.ts +20 -0
  62. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/index.d.ts +1 -0
  63. package/dist/types/src/Form/components/FormMessage/components/InternalFormMessage/messages.d.ts +7 -0
  64. package/dist/types/src/Form/components/FormMessage/index.d.ts +1 -0
  65. package/dist/types/src/Form/components/FormMessageBanner/FormMessageBanner.d.ts +7 -0
  66. package/dist/types/src/Form/components/FormMessageBanner/index.d.ts +1 -0
  67. package/dist/types/src/Form/components/FormSaveButton/FormSaveButton.d.ts +3 -0
  68. package/dist/types/src/Form/components/FormSaveButton/index.d.ts +1 -0
  69. package/dist/types/src/Form/components/FormSaveButton/messages.d.ts +7 -0
  70. package/dist/types/src/Form/constants.d.ts +2 -0
  71. package/dist/types/src/Form/context/AtlantisFormContext.d.ts +12 -0
  72. package/dist/types/src/Form/context/index.d.ts +2 -0
  73. package/dist/types/src/Form/context/types.d.ts +26 -0
  74. package/dist/types/src/Form/hooks/useFormViewRefs.d.ts +10 -0
  75. package/dist/types/src/Form/hooks/useInternalForm.d.ts +19 -0
  76. package/dist/types/src/Form/hooks/useOfflineHandler.d.ts +1 -0
  77. package/dist/types/src/Form/hooks/useSaveButtonPosition.d.ts +12 -0
  78. package/dist/types/src/Form/hooks/useScreenInformation.d.ts +8 -0
  79. package/dist/types/src/Form/hooks/useScrollToError/index.d.ts +1 -0
  80. package/dist/types/src/Form/hooks/useScrollToError/useScrollToError.d.ts +10 -0
  81. package/dist/types/src/Form/index.d.ts +5 -0
  82. package/dist/types/src/Form/messages.d.ts +27 -0
  83. package/dist/types/src/Form/types.d.ts +199 -0
  84. package/dist/types/src/InputNumber/InputNumber.d.ts +1 -1
  85. package/dist/types/src/index.d.ts +1 -0
  86. package/package.json +3 -2
  87. package/src/Form/Form.style.ts +34 -0
  88. package/src/Form/Form.test.tsx +588 -0
  89. package/src/Form/Form.tsx +296 -0
  90. package/src/Form/components/FormActionBar/FormActionBar.style.ts +11 -0
  91. package/src/Form/components/FormActionBar/FormActionBar.tsx +63 -0
  92. package/src/Form/components/FormActionBar/index.ts +2 -0
  93. package/src/Form/components/FormBody/FormBody.style.ts +27 -0
  94. package/src/Form/components/FormBody/FormBody.tsx +62 -0
  95. package/src/Form/components/FormBody/index.ts +1 -0
  96. package/src/Form/components/FormCache/FormCache.tsx +50 -0
  97. package/src/Form/components/FormErrorBanner/FormErrorBanner.test.tsx +124 -0
  98. package/src/Form/components/FormErrorBanner/FormErrorBanner.tsx +34 -0
  99. package/src/Form/components/FormErrorBanner/index.ts +1 -0
  100. package/src/Form/components/FormErrorBanner/messages.ts +14 -0
  101. package/src/Form/components/FormMask/FormMask.style.tsx +16 -0
  102. package/src/Form/components/FormMask/FormMask.tsx +19 -0
  103. package/src/Form/components/FormMask/index.ts +1 -0
  104. package/src/Form/components/FormMessage/FormMessage.test.tsx +72 -0
  105. package/src/Form/components/FormMessage/FormMessage.tsx +63 -0
  106. package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.style.ts +18 -0
  107. package/src/Form/components/FormMessage/components/InternalFormMessage/InternalFormMessage.tsx +55 -0
  108. package/src/Form/components/FormMessage/components/InternalFormMessage/index.ts +1 -0
  109. package/src/Form/components/FormMessage/components/InternalFormMessage/messages.ts +10 -0
  110. package/src/Form/components/FormMessage/index.ts +1 -0
  111. package/src/Form/components/FormMessageBanner/FormMessageBanner.test.tsx +27 -0
  112. package/src/Form/components/FormMessageBanner/FormMessageBanner.tsx +33 -0
  113. package/src/Form/components/FormMessageBanner/index.ts +1 -0
  114. package/src/Form/components/FormSaveButton/FormSaveButton.test.tsx +159 -0
  115. package/src/Form/components/FormSaveButton/FormSaveButton.tsx +103 -0
  116. package/src/Form/components/FormSaveButton/index.ts +1 -0
  117. package/src/Form/components/FormSaveButton/messages.ts +9 -0
  118. package/src/Form/constants.ts +2 -0
  119. package/src/Form/context/AtlantisFormContext.test.tsx +45 -0
  120. package/src/Form/context/AtlantisFormContext.tsx +21 -0
  121. package/src/Form/context/index.ts +5 -0
  122. package/src/Form/context/types.ts +34 -0
  123. package/src/Form/hooks/useFormViewRefs.ts +23 -0
  124. package/src/Form/hooks/useInternalForm.ts +99 -0
  125. package/src/Form/hooks/useOfflineHandler.ts +36 -0
  126. package/src/Form/hooks/useSaveButtonPosition.ts +52 -0
  127. package/src/Form/hooks/useScreenInformation.ts +25 -0
  128. package/src/Form/hooks/useScrollToError/index.ts +1 -0
  129. package/src/Form/hooks/useScrollToError/useScrollToError.test.tsx +103 -0
  130. package/src/Form/hooks/useScrollToError/useScrollToError.ts +102 -0
  131. package/src/Form/index.ts +13 -0
  132. package/src/Form/messages.ts +33 -0
  133. package/src/Form/types.ts +255 -0
  134. package/src/InputNumber/InputNumber.tsx +1 -1
  135. package/src/InputText/InputText.tsx +8 -1
  136. package/src/index.ts +1 -0
@@ -0,0 +1 @@
1
+ export { AtlantisFormContext, useAtlantisFormContext, } from "./AtlantisFormContext";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { useCallback, useRef } from "react";
2
+ export function useFormViewRefs() {
3
+ const scrollViewRef = useRef(null);
4
+ const bottomViewRef = useRef(null);
5
+ const scrollToTop = useCallback(() => {
6
+ var _a;
7
+ (_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollToPosition(0, 0);
8
+ }, [scrollViewRef]);
9
+ return {
10
+ scrollViewRef: scrollViewRef,
11
+ bottomViewRef,
12
+ scrollToTop,
13
+ };
14
+ }
@@ -0,0 +1,37 @@
1
+ import { useForm, } from "react-hook-form";
2
+ import { useAtlantisContext } from "../../AtlantisContext";
3
+ import { useAtlantisFormContext } from "../context/AtlantisFormContext";
4
+ export function useInternalForm({ mode, reValidateMode, initialValues, formRef, localCacheKey, localCacheId, scrollViewRef, saveButtonHeight, messageBannerHeight, }) {
5
+ const { useConfirmBeforeBack, useInternalFormLocalCache } = useAtlantisFormContext();
6
+ const { isOnline } = useAtlantisContext();
7
+ const formMethods = useForm({
8
+ mode,
9
+ reValidateMode,
10
+ defaultValues: initialValues,
11
+ shouldFocusError: false,
12
+ });
13
+ const clientSideSaveOn = localCacheKey && localCacheKey !== "INVALID";
14
+ const { setLocalCache, removeLocalCache } = useInternalFormLocalCache(formMethods, localCacheKey, {
15
+ id: localCacheId,
16
+ });
17
+ const { handleSubmit, formState: { isSubmitting, isDirty }, } = formMethods;
18
+ if (formRef) {
19
+ formRef.current = Object.assign(Object.assign({}, formMethods), { saveButtonHeight,
20
+ messageBannerHeight,
21
+ scrollViewRef });
22
+ }
23
+ const removeListenerRef = useConfirmBeforeBack({
24
+ alwaysPreventBack: isSubmitting,
25
+ shouldShowAlert: isDirty,
26
+ onAcceptEvent: isOnline ? removeLocalCache : undefined,
27
+ showLostProgressMessage: isOnline || !clientSideSaveOn ? true : false,
28
+ });
29
+ return {
30
+ setLocalCache,
31
+ formMethods,
32
+ handleSubmit,
33
+ isSubmitting,
34
+ isDirty,
35
+ removeListenerRef,
36
+ };
37
+ }
@@ -0,0 +1,24 @@
1
+ import { useCallback } from "react";
2
+ import { useIntl } from "react-intl";
3
+ import { Alert } from "react-native";
4
+ import { messages } from "../messages";
5
+ export function useOfflineHandler() {
6
+ const { formatMessage } = useIntl();
7
+ const handleOfflineSubmit = useCallback((callback, dismiss) => {
8
+ return () => {
9
+ Alert.alert(formatMessage(messages.unavailableNetworkTitle), formatMessage(messages.unavailableNetworkMessage), [
10
+ {
11
+ text: formatMessage(messages.dismissAlertButton),
12
+ style: "cancel",
13
+ onPress: dismiss,
14
+ },
15
+ {
16
+ text: formatMessage(messages.retryAlertButton),
17
+ style: "default",
18
+ onPress: callback,
19
+ },
20
+ ]);
21
+ };
22
+ }, [formatMessage]);
23
+ return handleOfflineSubmit;
24
+ }
@@ -0,0 +1,25 @@
1
+ import { Platform } from "react-native";
2
+ import { useScreenInformation } from "./useScreenInformation";
3
+ import { tokens } from "../../utils/design";
4
+ export function useSaveButtonPosition({ formContentHeight, isBottomSheetOpen, showStickySaveButton, keyboardHeight, keyboardScreenY, }) {
5
+ const { headerHeight } = useScreenInformation();
6
+ if (showStickySaveButton) {
7
+ return {
8
+ saveButtonPosition: "sticky",
9
+ };
10
+ }
11
+ if (Platform.OS === "android" || isBottomSheetOpen) {
12
+ return {
13
+ saveButtonPosition: "inline",
14
+ };
15
+ }
16
+ const isKeyboardOpen = Boolean(keyboardHeight);
17
+ // tokens["space-large"] acts like a safe space below the form to avoid
18
+ // rendering the sticky button above the form elements.
19
+ const calculatedFormContentHeight = formContentHeight + headerHeight + tokens["space-large"];
20
+ const isKeyboardOverForm = calculatedFormContentHeight > keyboardScreenY;
21
+ const showInlineSaveButton = isKeyboardOpen && isKeyboardOverForm;
22
+ return {
23
+ saveButtonPosition: showInlineSaveButton ? "inline" : "sticky",
24
+ };
25
+ }
@@ -0,0 +1,15 @@
1
+ import { useWindowDimensions } from "react-native";
2
+ import { useSafeAreaInsets } from "react-native-safe-area-context";
3
+ import { KEYBOARD_TOP_PADDING_AUTO_SCROLL } from "../constants";
4
+ import { useAtlantisFormContext } from "../context";
5
+ export function useScreenInformation() {
6
+ const { headerHeight } = useAtlantisFormContext();
7
+ const windowHeight = useWindowDimensions().height;
8
+ const headerHeightWithPadding = headerHeight + KEYBOARD_TOP_PADDING_AUTO_SCROLL;
9
+ const insets = useSafeAreaInsets();
10
+ return {
11
+ windowHeight,
12
+ headerHeight: headerHeightWithPadding,
13
+ insets,
14
+ };
15
+ }
@@ -0,0 +1 @@
1
+ export * from "./useScrollToError";
@@ -0,0 +1,63 @@
1
+ import { useCallback, useState } from "react";
2
+ import { Keyboard, Platform, } from "react-native";
3
+ import { useIsScreenReaderEnabled } from "../../../hooks/useIsScreenReaderEnabled";
4
+ import { useErrorMessageContext } from "../../../ErrorMessageWrapper";
5
+ export function useScrollToError({ formState: { errors, isValid, submitCount }, refNode, setFocus, scrollToPosition, }) {
6
+ const [submitCounter, setSubmitCounter] = useState(submitCount);
7
+ const isScreenReaderEnabled = useIsScreenReaderEnabled();
8
+ const manuallyScrollToElement = useManuallyScrollToElement(handleScroll, refNode);
9
+ // Determine if the form has been submitted by checking if the submit count
10
+ // went up.
11
+ const hasBeenSubmitted = submitCounter < submitCount;
12
+ if (!hasBeenSubmitted)
13
+ return;
14
+ if (isScreenReaderEnabled) {
15
+ manuallyScrollToElement();
16
+ Keyboard.dismiss();
17
+ }
18
+ else {
19
+ defaultAutoScroll();
20
+ }
21
+ setSubmitCounter(submitCount);
22
+ function defaultAutoScroll() {
23
+ if (isValid)
24
+ return;
25
+ try {
26
+ focusInputWithRHF(errors, setFocus);
27
+ }
28
+ catch (_a) {
29
+ manuallyScrollToElement();
30
+ }
31
+ }
32
+ function handleScroll(...[x, y]) {
33
+ /**
34
+ * Disable scroll animation on android when screen reader is active since it
35
+ * can't accessibility-focus on an offscreen component.
36
+ */
37
+ const isAndroidWithScreenReader = isScreenReaderEnabled && Platform.OS === "android";
38
+ const shouldAnimateScroll = !isAndroidWithScreenReader;
39
+ scrollToPosition === null || scrollToPosition === void 0 ? void 0 : scrollToPosition(x, y, shouldAnimateScroll);
40
+ }
41
+ }
42
+ function focusInputWithRHF(errors, setFocus) {
43
+ const errorMessages = Object.keys(errors);
44
+ setFocus(errorMessages[0]);
45
+ }
46
+ /**
47
+ * Manually scroll to the element by checking which elements has an error from
48
+ * the Error Message Context
49
+ */
50
+ function useManuallyScrollToElement(callback, refNode) {
51
+ const { elements } = useErrorMessageContext();
52
+ return useCallback(() => {
53
+ const elementWithError = Object.keys(elements).find(el => elements[el].hasErrorMessage);
54
+ if (elementWithError) {
55
+ const element = elements[elementWithError];
56
+ refNode && element.measure(refNode, callback, handleError);
57
+ element.accessibilityFocus();
58
+ }
59
+ }, [callback, elements, refNode]);
60
+ }
61
+ function handleError() {
62
+ return Error("Couldn't scroll to error");
63
+ }
@@ -0,0 +1,4 @@
1
+ export { Form } from "./Form";
2
+ export { FormMessage } from "./components/FormMessage";
3
+ export { FormSubmitErrorType, FormBannerMessageType } from "./types";
4
+ export * from "./context";
@@ -0,0 +1,28 @@
1
+ import { defineMessages } from "react-intl";
2
+ export const messages = defineMessages({
3
+ loadingA11YLabel: {
4
+ id: "loadingA11yLabel",
5
+ defaultMessage: "Loading",
6
+ description: "Accessibility label for the loading indicator",
7
+ },
8
+ dismissAlertButton: {
9
+ id: "dismiss",
10
+ defaultMessage: "Dismiss",
11
+ description: "The label for the button to dismiss the alert ",
12
+ },
13
+ retryAlertButton: {
14
+ id: "retry",
15
+ defaultMessage: "Try Again",
16
+ description: "The label for the alert button to try action again",
17
+ },
18
+ unavailableNetworkTitle: {
19
+ id: "unavailableNetworkTitle",
20
+ defaultMessage: "Network unavailable",
21
+ description: "The title for alert about network unavailable",
22
+ },
23
+ unavailableNetworkMessage: {
24
+ id: "unavailableNetworkMessage",
25
+ defaultMessage: "Check your internet connection and try again later.",
26
+ description: "The message for alert about network unavailable",
27
+ },
28
+ });
@@ -0,0 +1,10 @@
1
+ export var FormSubmitErrorType;
2
+ (function (FormSubmitErrorType) {
3
+ FormSubmitErrorType["NetworkError"] = "NetworkError";
4
+ FormSubmitErrorType["UserError"] = "UserErrors";
5
+ })(FormSubmitErrorType || (FormSubmitErrorType = {}));
6
+ export var FormBannerMessageType;
7
+ (function (FormBannerMessageType) {
8
+ FormBannerMessageType["WarningMessage"] = "WarningMessage";
9
+ FormBannerMessageType["NoticeMessage"] = "NoticeMessage";
10
+ })(FormBannerMessageType || (FormBannerMessageType = {}));
@@ -11,6 +11,7 @@ export const InputText = forwardRef(InputTextInternal);
11
11
  function InputTextInternal({ invalid, disabled, name, placeholder, assistiveText, keyboard, value: controlledValue, defaultValue, autoFocus, autoComplete = "off", spellCheck, textContentType = "none", validations, onChangeText, onSubmitEditing, onFocus, accessibilityLabel, accessibilityHint, autoCorrect, autoCapitalize, onBlur, multiline = false, prefix, suffix, transform = {}, clearable = multiline ? "never" : "while-editing", testID, secureTextEntry, styleOverride, }, ref) {
12
12
  var _a;
13
13
  const isAndroid = Platform.OS === "android";
14
+ const isIOS = Platform.OS === "ios";
14
15
  const { input: inputTransform = identity, output: outputTransform = identity, } = transform;
15
16
  const { error, field } = useFormController({
16
17
  name,
@@ -97,7 +98,13 @@ function InputTextInternal({ invalid, disabled, name, placeholder, assistiveText
97
98
  field.ref(instance);
98
99
  } }))));
99
100
  function handleChangeText(value) {
100
- const newValue = outputTransform(value);
101
+ /**
102
+ * Replacing the U+FFFC character because it's duplicating text
103
+ * when dictating on iOS 16.
104
+ * https://github.com/facebook/react-native/issues/36521#issuecomment-1555421134
105
+ */
106
+ const removedIOSCharValue = isIOS ? value.replace(/\uFFFC/g, "") : value;
107
+ const newValue = outputTransform(removedIOSCharValue);
101
108
  setHasMiniLabel(Boolean(newValue));
102
109
  onChangeText === null || onChangeText === void 0 ? void 0 : onChangeText(newValue);
103
110
  field.onChange(newValue);
package/dist/src/index.js CHANGED
@@ -16,6 +16,7 @@ export * from "./Divider";
16
16
  export * from "./EmptyState";
17
17
  export * from "./ErrorMessageWrapper";
18
18
  export * from "./Flex";
19
+ export * from "./Form";
19
20
  export * from "./FormField";
20
21
  export * from "./Heading";
21
22
  export * from "./Icon";