@jobber/components-native 0.95.2-JOB-141866-ccf6ba8.10 → 0.95.2-JOB-141866-a6ee36d.22

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.
@@ -157,6 +157,13 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
157
157
  * Renders a footer below the save button.
158
158
  */
159
159
  renderFooter?: React.ReactNode;
160
+ /**
161
+ * TODO: JOB-147156 This is a HACK for multiline inputs on iOS scrolling issue.
162
+ * Disables the keyboard aware scroll view.
163
+ * This is useful when you want to disable the keyboard aware scroll view for a specific form.
164
+ * For example, when you have a form with a lot of fields and you want to disable the keyboard aware scroll view.
165
+ */
166
+ disableKeyboardAwareScroll?: boolean;
160
167
  }
161
168
  export type InternalFormProps<T extends FieldValues, SubmitResponseType> = Omit<FormProps<T, SubmitResponseType>, "initialLoading">;
162
169
  export type ValidationRulesByFieldPath<T extends FieldValues> = {
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import type { LayoutChangeEvent, StyleProp, TextStyle, ViewStyle } from "react-native";
2
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
3
3
  import type { FieldError } from "react-hook-form";
4
4
  import type { IconNames } from "@jobber/design";
5
5
  export type Clearable = "never" | "while-editing" | "always";
@@ -82,8 +82,7 @@ export interface InputFieldWrapperProps {
82
82
  * Change the type of loading indicator to spinner or glimmer.
83
83
  */
84
84
  readonly loadingType?: "spinner" | "glimmer";
85
- readonly scrollViewHackOnLayout?: (event: LayoutChangeEvent) => void;
86
85
  }
87
86
  export declare const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID = "ATL-InputFieldWrapper-Glimmers";
88
87
  export declare const INPUT_FIELD_WRAPPER_SPINNER_TEST_ID = "ATL-InputFieldWrapper-Spinner";
89
- export declare function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode, hasValue, error, focused, children, onClear, showClearAction, styleOverride, toolbar, toolbarVisibility, loading, loadingType, scrollViewHackOnLayout, }: InputFieldWrapperProps): React.JSX.Element;
88
+ export declare function InputFieldWrapper({ invalid, disabled, placeholder, assistiveText, prefix, suffix, placeholderMode, hasValue, error, focused, children, onClear, showClearAction, styleOverride, toolbar, toolbarVisibility, loading, loadingType, }: InputFieldWrapperProps): React.JSX.Element;
@@ -1,3 +1,2 @@
1
1
  export * from "./useFormController";
2
2
  export * from "./useIsScreenReaderEnabled";
3
- export * from "./useKeyboardVisibility";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jobber/components-native",
3
- "version": "0.95.2-JOB-141866-ccf6ba8.10+ccf6ba8d6",
3
+ "version": "0.95.2-JOB-141866-a6ee36d.22+a6ee36d10",
4
4
  "license": "MIT",
5
5
  "description": "React Native implementation of Atlantis",
6
6
  "repository": {
@@ -96,5 +96,5 @@
96
96
  "react-native-safe-area-context": "^5.4.0",
97
97
  "react-native-svg": ">=12.0.0"
98
98
  },
99
- "gitHead": "ccf6ba8d6a687be9144debdfeab7c09525685fb0"
99
+ "gitHead": "a6ee36d10f2c1ba08f20ad6bb9019e274bcee16f"
100
100
  }
@@ -18,6 +18,7 @@ import {
18
18
  useWindowDimensions,
19
19
  } from "react-native";
20
20
  import { Portal } from "react-native-portalize";
21
+ import { useKeyboardVisibility } from "./hooks/useKeyboardVisibility";
21
22
  import { useStyles } from "./ContentOverlay.style";
22
23
  import { useViewLayoutHeight } from "./hooks/useViewLayoutHeight";
23
24
  import type {
@@ -26,7 +27,7 @@ import type {
26
27
  ModalBackgroundColor,
27
28
  } from "./types";
28
29
  import { UNSAFE_WrappedModalize } from "./UNSAFE_WrappedModalize";
29
- import { useIsScreenReaderEnabled, useKeyboardVisibility } from "../hooks";
30
+ import { useIsScreenReaderEnabled } from "../hooks";
30
31
  import { IconButton } from "../IconButton";
31
32
  import { Heading } from "../Heading";
32
33
  import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
package/src/Form/Form.tsx CHANGED
@@ -1,7 +1,8 @@
1
- import React, { useMemo, useState } from "react";
1
+ import React, { useState } from "react";
2
2
  import type { FieldValues } from "react-hook-form";
3
3
  import { FormProvider } from "react-hook-form";
4
4
  import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
5
+ import type { LayoutChangeEvent } from "react-native";
5
6
  import { Keyboard, Platform, View, findNodeHandle } from "react-native";
6
7
  import { useStyles } from "./Form.style";
7
8
  import { FormErrorBanner } from "./components/FormErrorBanner";
@@ -64,6 +65,7 @@ function InternalForm<T extends FieldValues, S>({
64
65
  secondaryActions,
65
66
  saveButtonOffset,
66
67
  showStickySaveButton = false,
68
+ disableKeyboardAwareScroll = false,
67
69
  renderFooter,
68
70
  UNSAFE_allowDiscardLocalCacheWhenOffline,
69
71
  }: InternalFormProps<T, S>) {
@@ -107,12 +109,8 @@ function InternalForm<T extends FieldValues, S>({
107
109
  const [isSecondaryActionLoading, setIsSecondaryActionLoading] =
108
110
  useState<boolean>(false);
109
111
 
110
- const { calculatedKeyboardHeight } = useMemo(() => {
111
- return {
112
- calculatedKeyboardHeight:
113
- keyboardHeight - (paddingBottom + KEYBOARD_SAVE_BUTTON_DISTANCE),
114
- };
115
- }, [paddingBottom, keyboardHeight]);
112
+ const extraViewHeight = paddingBottom + KEYBOARD_SAVE_BUTTON_DISTANCE;
113
+ const calculatedKeyboardHeight = keyboardHeight - extraViewHeight;
116
114
 
117
115
  useScrollToError({
118
116
  formState: formMethods.formState,
@@ -134,6 +132,10 @@ function InternalForm<T extends FieldValues, S>({
134
132
  },
135
133
  });
136
134
 
135
+ const onLayout = (event: LayoutChangeEvent) => {
136
+ setMessageBannerHeight(event.nativeEvent.layout.height);
137
+ };
138
+
137
139
  const styles = useStyles();
138
140
 
139
141
  const { edgeToEdgeEnabled } = useAtlantisFormContext();
@@ -164,7 +166,7 @@ function InternalForm<T extends FieldValues, S>({
164
166
  >
165
167
  <KeyboardAwareScrollView
166
168
  enableResetScrollToCoords={false}
167
- enableAutomaticScroll={true}
169
+ enableAutomaticScroll={!disableKeyboardAwareScroll}
168
170
  enableOnAndroid={edgeToEdgeEnabled}
169
171
  keyboardOpeningTime={
170
172
  Platform.OS === "ios" ? tokens["timing-slowest"] : 0
@@ -183,11 +185,7 @@ function InternalForm<T extends FieldValues, S>({
183
185
  setFormContentHeight(nativeEvent.layout.height);
184
186
  }}
185
187
  >
186
- <View
187
- onLayout={({ nativeEvent }) => {
188
- setMessageBannerHeight(nativeEvent.layout.height);
189
- }}
190
- >
188
+ <View onLayout={onLayout}>
191
189
  <FormMessageBanner bannerMessages={bannerMessages} />
192
190
  <FormErrorBanner
193
191
  networkError={bannerErrors?.networkError}
package/src/Form/types.ts CHANGED
@@ -201,6 +201,14 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
201
201
  * Renders a footer below the save button.
202
202
  */
203
203
  renderFooter?: React.ReactNode;
204
+
205
+ /**
206
+ * TODO: JOB-147156 This is a HACK for multiline inputs on iOS scrolling issue.
207
+ * Disables the keyboard aware scroll view.
208
+ * This is useful when you want to disable the keyboard aware scroll view for a specific form.
209
+ * For example, when you have a form with a lot of fields and you want to disable the keyboard aware scroll view.
210
+ */
211
+ disableKeyboardAwareScroll?: boolean;
204
212
  }
205
213
 
206
214
  export type InternalFormProps<T extends FieldValues, SubmitResponseType> = Omit<
@@ -1,10 +1,5 @@
1
1
  import React from "react";
2
- import type {
3
- LayoutChangeEvent,
4
- StyleProp,
5
- TextStyle,
6
- ViewStyle,
7
- } from "react-native";
2
+ import type { StyleProp, TextStyle, ViewStyle } from "react-native";
8
3
  import { Text as RNText, View } from "react-native";
9
4
  import type { FieldError } from "react-hook-form";
10
5
  import type { IconNames } from "@jobber/design";
@@ -117,8 +112,6 @@ export interface InputFieldWrapperProps {
117
112
  * Change the type of loading indicator to spinner or glimmer.
118
113
  */
119
114
  readonly loadingType?: "spinner" | "glimmer";
120
-
121
- readonly scrollViewHackOnLayout?: (event: LayoutChangeEvent) => void;
122
115
  }
123
116
 
124
117
  export const INPUT_FIELD_WRAPPER_GLIMMERS_TEST_ID =
@@ -145,7 +138,6 @@ export function InputFieldWrapper({
145
138
  toolbarVisibility = "while-editing",
146
139
  loading = false,
147
140
  loadingType = "spinner",
148
- scrollViewHackOnLayout,
149
141
  }: InputFieldWrapperProps) {
150
142
  fieldAffixRequiredPropsCheck([prefix, suffix]);
151
143
  const handleClear = onClear ?? noopClear;
@@ -172,7 +164,6 @@ export function InputFieldWrapper({
172
164
  disabled && styles.disabled,
173
165
  styleOverride?.container,
174
166
  ]}
175
- onLayout={scrollViewHackOnLayout}
176
167
  >
177
168
  <View style={styles.field}>
178
169
  {prefix?.icon && (
@@ -1,7 +1,6 @@
1
1
  import type { Ref, SyntheticEvent } from "react";
2
2
  import React, {
3
3
  forwardRef,
4
- useDeferredValue,
5
4
  useEffect,
6
5
  useImperativeHandle,
7
6
  useMemo,
@@ -23,23 +22,13 @@ import type { Clearable } from "@jobber/hooks";
23
22
  import { useShowClear } from "@jobber/hooks";
24
23
  import { useStyles } from "./InputText.style";
25
24
  import { useInputAccessoriesContext } from "./context";
26
- import { useFormController, useKeyboardVisibility } from "../hooks";
25
+ import { useFormController } from "../hooks";
27
26
  import type {
28
27
  InputFieldStyleOverride,
29
28
  InputFieldWrapperProps,
30
29
  } from "../InputFieldWrapper/InputFieldWrapper";
31
30
  import { InputFieldWrapper } from "../InputFieldWrapper";
32
31
  import { useCommonInputStyles } from "../InputFieldWrapper/CommonInputStyles.style";
33
- import { useScreenInformation } from "../Form/hooks/useScreenInformation";
34
- // import { useScreenInformation } from "../Form/hooks/useScreenInformation";
35
-
36
- /**
37
- * Buffer zone in pixels for offscreen detection.
38
- * This makes the detection more sensitive by marking the component as offscreen
39
- * even if it's technically still visible but within this buffer distance from the edge.
40
- */
41
- // 44 (accessory bar height) + 20 (buffer)
42
- const KEYBOARD_AWARE_DETECTION_BUFFER = 40;
43
32
 
44
33
  export interface InputTextProps
45
34
  extends Pick<
@@ -383,25 +372,9 @@ function InputTextInternal(
383
372
 
384
373
  const styles = useStyles();
385
374
  const commonInputStyles = useCommonInputStyles();
386
- const { headerHeight, windowHeight } = useScreenInformation();
387
- const { keyboardHeight } = useKeyboardVisibility();
388
- const maxHeight = useDeferredValue(
389
- windowHeight -
390
- headerHeight -
391
- keyboardHeight -
392
- KEYBOARD_AWARE_DETECTION_BUFFER,
393
- );
394
- const [inputHeight, setInputHeight] = useState(0);
395
-
396
- const enableScroll = useDeferredValue(inputHeight > maxHeight);
397
375
 
398
376
  return (
399
377
  <InputFieldWrapper
400
- scrollViewHackOnLayout={event => {
401
- event.target?.measureInWindow((_, y, __, height) => {
402
- setInputHeight(height);
403
- });
404
- }}
405
378
  prefix={prefix}
406
379
  suffix={suffix}
407
380
  hasValue={hasValue}
@@ -447,11 +420,7 @@ function InputTextInternal(
447
420
  autoFocus={autoFocus}
448
421
  autoComplete={autoComplete}
449
422
  multiline={multiline}
450
- // Makes sure it doesn't jump to the top of the screen when the keyboard is shown and a new line is added.
451
- // State for tracking if the input should be scrollable.
452
- // This is tech debt related to an issue where keyboard aware scrollview doesn't work if `scrollEnabled` is true. However,
453
- // when `scrollEnabled` is false it causes an issue where super long text inputs will jump to the top when a new line is added to the bottom of the input.
454
- scrollEnabled={Platform.OS === "ios" && enableScroll}
423
+ scrollEnabled={false}
455
424
  textContentType={textContentType}
456
425
  onChangeText={handleChangeText}
457
426
  onSubmitEditing={handleOnSubmitEditing}
@@ -8,7 +8,6 @@ import { InputText } from "../InputText";
8
8
  const mockUseFormController = jest.fn();
9
9
  jest.mock("../../hooks", () => {
10
10
  return {
11
- ...jest.requireActual("../../hooks"),
12
11
  useFormController: (
13
12
  ...args: [{ name: string; value: string; validations: unknown }]
14
13
  ) => mockUseFormController(...args),
@@ -1,3 +1,2 @@
1
1
  export * from "./useFormController";
2
2
  export * from "./useIsScreenReaderEnabled";
3
- export * from "./useKeyboardVisibility";