@jobber/components-native 0.89.5-JOB-139254-4e3c64d.7 → 0.89.5-JOB-140604-c92b387.42

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 (32) hide show
  1. package/dist/package.json +4 -2
  2. package/dist/src/BottomSheet/BottomSheet.js +55 -35
  3. package/dist/src/BottomSheet/BottomSheet.style.js +13 -8
  4. package/dist/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.js +45 -0
  5. package/dist/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.js +8 -0
  6. package/dist/src/ButtonGroup/ButtonGroup.js +1 -1
  7. package/dist/src/Form/Form.js +1 -2
  8. package/dist/src/Form/hooks/useInternalForm.js +3 -6
  9. package/dist/src/InputText/InputText.js +2 -2
  10. package/dist/src/utils/meta/meta.json +1 -0
  11. package/dist/tsconfig.build.tsbuildinfo +1 -1
  12. package/dist/types/src/BottomSheet/BottomSheet.d.ts +13 -4
  13. package/dist/types/src/BottomSheet/BottomSheet.style.d.ts +12 -7
  14. package/dist/types/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.d.ts +9 -0
  15. package/dist/types/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.d.ts +5 -0
  16. package/dist/types/src/Form/hooks/useInternalForm.d.ts +2 -2
  17. package/dist/types/src/Form/types.d.ts +0 -6
  18. package/dist/types/src/InputText/InputText.d.ts +1 -1
  19. package/package.json +4 -2
  20. package/src/BottomSheet/BottomSheet.stories.tsx +129 -0
  21. package/src/BottomSheet/BottomSheet.style.ts +13 -11
  22. package/src/BottomSheet/BottomSheet.test.tsx +19 -24
  23. package/src/BottomSheet/BottomSheet.tsx +126 -103
  24. package/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.styles.ts +9 -0
  25. package/src/BottomSheet/components/BottomSheetInputText/BottomSheetInputText.tsx +89 -0
  26. package/src/ButtonGroup/ButtonGroup.tsx +1 -1
  27. package/src/Form/Form.tsx +0 -2
  28. package/src/Form/hooks/useInternalForm.ts +2 -9
  29. package/src/Form/types.ts +0 -7
  30. package/src/InputText/InputText.tsx +3 -3
  31. package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +216 -1
  32. package/src/utils/meta/meta.json +1 -0
@@ -0,0 +1,89 @@
1
+ import React, { forwardRef, useCallback } from "react";
2
+ import type { FocusEvent } from "react-native";
3
+ import { TextInput, View, findNodeHandle } from "react-native";
4
+ import { useBottomSheetInternal } from "@gorhom/bottom-sheet";
5
+ import { useStyles } from "./BottomSheetInputText.styles";
6
+ import type {
7
+ InputTextProps,
8
+ InputTextRef,
9
+ } from "../../../InputText/InputText";
10
+ import { InputText } from "../../../InputText/InputText";
11
+
12
+ /**
13
+ * BottomSheetInputText is a wrapper around InputText that provides
14
+ * bottom sheet keyboard handling. It implements the handleOnFocus and
15
+ * handleOnBlur logic from BottomSheetTextInput to ensure proper keyboard
16
+ * positioning within bottom sheets.
17
+ */
18
+ export const BottomSheetInputText = forwardRef<InputTextRef, InputTextProps>(
19
+ function BottomSheetInputText(props, ref) {
20
+ const styles = useStyles();
21
+ const { onFocus, onBlur } = props;
22
+ const { animatedKeyboardState, textInputNodesRef } =
23
+ useBottomSheetInternal();
24
+
25
+ const handleOnFocus = useCallback(
26
+ (event?: FocusEvent) => {
27
+ animatedKeyboardState.set(
28
+ (state: ReturnType<typeof animatedKeyboardState.get>) => ({
29
+ ...state,
30
+ target: event?.nativeEvent.target,
31
+ }),
32
+ );
33
+
34
+ onFocus?.(event);
35
+ },
36
+ [animatedKeyboardState, onFocus],
37
+ );
38
+
39
+ const handleOnBlur = useCallback(
40
+ (event?: FocusEvent) => {
41
+ const keyboardState = animatedKeyboardState.get();
42
+ const currentlyFocusedInput = TextInput.State.currentlyFocusedInput();
43
+ const currentFocusedInput =
44
+ currentlyFocusedInput !== null
45
+ ? findNodeHandle(
46
+ // @ts-expect-error - TextInput.State.currentlyFocusedInput() returns NativeMethods
47
+ // which is not directly assignable to findNodeHandle's expected type,
48
+ // but it works at runtime. This is a known type limitation in React Native.
49
+ currentlyFocusedInput,
50
+ )
51
+ : null;
52
+
53
+ /**
54
+ * we need to make sure that we only remove the target
55
+ * if the target belong to the current component and
56
+ * if the currently focused input is not in the targets set.
57
+ */
58
+ const shouldRemoveCurrentTarget =
59
+ keyboardState.target === event?.nativeEvent.target;
60
+ const shouldIgnoreBlurEvent =
61
+ currentFocusedInput &&
62
+ textInputNodesRef.current.has(currentFocusedInput);
63
+
64
+ if (shouldRemoveCurrentTarget && !shouldIgnoreBlurEvent) {
65
+ animatedKeyboardState.set(
66
+ (state: ReturnType<typeof animatedKeyboardState.get>) => ({
67
+ ...state,
68
+ target: undefined,
69
+ }),
70
+ );
71
+ }
72
+
73
+ onBlur?.(event);
74
+ },
75
+ [animatedKeyboardState, textInputNodesRef, onBlur],
76
+ );
77
+
78
+ return (
79
+ <View style={styles.inputText}>
80
+ <InputText
81
+ {...props}
82
+ ref={ref}
83
+ onFocus={handleOnFocus}
84
+ onBlur={handleOnBlur}
85
+ />
86
+ </View>
87
+ );
88
+ },
89
+ );
@@ -48,7 +48,7 @@ export function ButtonGroup({
48
48
  }: ButtonGroupProps): JSX.Element {
49
49
  const { t } = useAtlantisI18n();
50
50
  const { handlePress } = usePreventTapWhenOffline();
51
- const secondaryActionsRef = useRef<BottomSheetRef>();
51
+ const secondaryActionsRef = useRef<BottomSheetRef>(null);
52
52
  const { primaryActions, secondaryActions } = getActions(children);
53
53
  const styles = useStyles();
54
54
 
package/src/Form/Form.tsx CHANGED
@@ -66,7 +66,6 @@ function InternalForm<T extends FieldValues, S>({
66
66
  saveButtonOffset,
67
67
  showStickySaveButton = false,
68
68
  renderFooter,
69
- removeLocalCacheOnBackOffline,
70
69
  }: InternalFormProps<T, S>) {
71
70
  const { scrollViewRef, bottomViewRef, scrollToTop } = useFormViewRefs();
72
71
  const [saveButtonHeight, setSaveButtonHeight] = useState(0);
@@ -88,7 +87,6 @@ function InternalForm<T extends FieldValues, S>({
88
87
  scrollViewRef,
89
88
  saveButtonHeight,
90
89
  messageBannerHeight,
91
- removeLocalCacheOnBackOffline,
92
90
  });
93
91
  const { windowHeight, headerHeight } = useScreenInformation();
94
92
  const [keyboardHeight, setKeyboardHeight] = useState(0);
@@ -20,7 +20,6 @@ type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<
20
20
  | "localCacheKey"
21
21
  | "localCacheExclude"
22
22
  | "localCacheId"
23
- | "removeLocalCacheOnBackOffline"
24
23
  > & {
25
24
  scrollViewRef?: RefObject<KeyboardAwareScrollView>;
26
25
  readonly saveButtonHeight: number;
@@ -46,7 +45,6 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
46
45
  scrollViewRef,
47
46
  saveButtonHeight,
48
47
  messageBannerHeight,
49
- removeLocalCacheOnBackOffline = false,
50
48
  }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T> {
51
49
  const { useConfirmBeforeBack, useInternalFormLocalCache } =
52
50
  useAtlantisFormContext();
@@ -84,16 +82,11 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
84
82
  };
85
83
  }
86
84
 
87
- const shouldRemoveCacheOnBack = removeLocalCacheOnBackOffline
88
- ? true
89
- : isOnline;
90
-
91
85
  const removeListenerRef = useConfirmBeforeBack({
92
86
  alwaysPreventBack: isSubmitting,
93
87
  shouldShowAlert: isDirty,
94
- onAcceptEvent: shouldRemoveCacheOnBack ? removeLocalCache : undefined,
95
- showLostProgressMessage:
96
- shouldRemoveCacheOnBack || !clientSideSaveOn ? true : false,
88
+ onAcceptEvent: isOnline ? removeLocalCache : undefined,
89
+ showLostProgressMessage: isOnline || !clientSideSaveOn ? true : false,
97
90
  });
98
91
 
99
92
  return {
package/src/Form/types.ts CHANGED
@@ -171,13 +171,6 @@ 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
- removeLocalCacheOnBackOffline?: boolean;
180
-
181
174
  /**
182
175
  * Secondary Action for ButtonGroup
183
176
  */
@@ -117,7 +117,7 @@ export interface InputTextProps
117
117
  /**
118
118
  * Callback that is called when the text input is blurred
119
119
  */
120
- readonly onBlur?: () => void;
120
+ readonly onBlur?: (event?: FocusEvent) => void;
121
121
 
122
122
  /**
123
123
  * VoiceOver will read this string when a user selects the associated element
@@ -422,10 +422,10 @@ function InputTextInternal(
422
422
  setFocused(true);
423
423
  onFocus?.(event);
424
424
  }}
425
- onBlur={() => {
425
+ onBlur={event => {
426
426
  _name && setFocusedInput("");
427
427
  setFocused(false);
428
- onBlur?.();
428
+ onBlur?.(event);
429
429
  field.onBlur();
430
430
  trimWhitespace(inputTransform(field.value), updateFormAndState);
431
431
  }}
@@ -150,6 +150,221 @@ exports[`renders a thumbnail component with attachments 1`] = `
150
150
  "top": 0,
151
151
  }
152
152
  }
153
- />,
153
+ >
154
+ <View
155
+ backdropComponent={[Function]}
156
+ backgroundStyle={
157
+ {
158
+ "borderTopLeftRadius": 24,
159
+ "borderTopRightRadius": 24,
160
+ "paddingTop": 8,
161
+ }
162
+ }
163
+ enablePanDownToClose={true}
164
+ handleStyle={
165
+ {
166
+ "display": "none",
167
+ }
168
+ }
169
+ keyboardBlurBehavior="restore"
170
+ style={
171
+ {
172
+ "display": "none",
173
+ }
174
+ }
175
+ testID="bottom-sheet-mock"
176
+ >
177
+ <View
178
+ enableFooterMarginAdjustment={true}
179
+ style={
180
+ {
181
+ "paddingBottom": 8,
182
+ }
183
+ }
184
+ testID="bottom-sheet-view"
185
+ >
186
+ <View
187
+ accessibilityLabel="Preview "
188
+ accessibilityState={
189
+ {
190
+ "busy": undefined,
191
+ "checked": undefined,
192
+ "disabled": undefined,
193
+ "expanded": undefined,
194
+ "selected": undefined,
195
+ }
196
+ }
197
+ accessibilityValue={
198
+ {
199
+ "max": undefined,
200
+ "min": undefined,
201
+ "now": undefined,
202
+ "text": undefined,
203
+ }
204
+ }
205
+ accessible={true}
206
+ collapsable={false}
207
+ focusable={true}
208
+ onClick={[Function]}
209
+ onResponderGrant={[Function]}
210
+ onResponderMove={[Function]}
211
+ onResponderRelease={[Function]}
212
+ onResponderTerminate={[Function]}
213
+ onResponderTerminationRequest={[Function]}
214
+ onStartShouldSetResponder={[Function]}
215
+ style={
216
+ {
217
+ "alignContent": "center",
218
+ "alignItems": "center",
219
+ "display": "flex",
220
+ "flexDirection": "row",
221
+ "opacity": 1,
222
+ "padding": 8,
223
+ }
224
+ }
225
+ >
226
+ <View
227
+ style={
228
+ {
229
+ "paddingHorizontal": 8,
230
+ }
231
+ }
232
+ >
233
+ <RNSVGSvgView
234
+ align="xMidYMid"
235
+ bbHeight={24}
236
+ bbWidth={24}
237
+ focusable={false}
238
+ meetOrSlice={0}
239
+ minX={0}
240
+ minY={0}
241
+ style={
242
+ [
243
+ {
244
+ "backgroundColor": "transparent",
245
+ "borderWidth": 0,
246
+ },
247
+ {
248
+ "display": "flex",
249
+ "fill": "hsl(198, 35%, 21%)",
250
+ "height": 24,
251
+ "verticalAlign": "middle",
252
+ "width": 24,
253
+ },
254
+ {
255
+ "flex": 0,
256
+ "height": 24,
257
+ "width": 24,
258
+ },
259
+ ]
260
+ }
261
+ testID="eye"
262
+ vbHeight={24}
263
+ vbWidth={24}
264
+ >
265
+ <RNSVGGroup
266
+ fill={
267
+ {
268
+ "payload": 4280499528,
269
+ "type": 0,
270
+ }
271
+ }
272
+ propList={
273
+ [
274
+ "fill",
275
+ ]
276
+ }
277
+ >
278
+ <RNSVGPath
279
+ d="M16 12a4 4 0 1 1-8 0 4 4 0 0 1 8 0Zm-2 0a2 2 0 1 0-4 0 2 2 0 0 0 4 0Z"
280
+ fill={
281
+ {
282
+ "payload": 4280499528,
283
+ "type": 0,
284
+ }
285
+ }
286
+ propList={
287
+ [
288
+ "fill",
289
+ ]
290
+ }
291
+ />
292
+ <RNSVGPath
293
+ d="M21.863 12.477C20.794 14.2 16.703 20 12 20c-4.702 0-8.795-5.8-9.863-7.523a.903.903 0 0 1 0-.954C3.205 9.8 7.297 4 12 4c4.703 0 8.794 5.8 9.863 7.523a.903.903 0 0 1 0 .954ZM20 12s-3.582-6-8-6-8 6-8 6 3.582 6 8 6 8-6 8-6Z"
294
+ fill={
295
+ {
296
+ "payload": 4280499528,
297
+ "type": 0,
298
+ }
299
+ }
300
+ propList={
301
+ [
302
+ "fill",
303
+ ]
304
+ }
305
+ />
306
+ </RNSVGGroup>
307
+ </RNSVGSvgView>
308
+ </View>
309
+ <View
310
+ style={
311
+ {
312
+ "flexShrink": 1,
313
+ "paddingHorizontal": 8,
314
+ }
315
+ }
316
+ >
317
+ <Text
318
+ accessibilityRole="text"
319
+ adjustsFontSizeToFit={false}
320
+ allowFontScaling={true}
321
+ collapsable={false}
322
+ maxFontSizeMultiplier={3.125}
323
+ selectable={true}
324
+ selectionColor="hsl(86, 100%, 46%)"
325
+ style={
326
+ [
327
+ {
328
+ "fontFamily": "inter-semibold",
329
+ },
330
+ {
331
+ "color": "hsl(197, 15%, 43%)",
332
+ },
333
+ {
334
+ "textAlign": "left",
335
+ },
336
+ {
337
+ "fontSize": 16,
338
+ "lineHeight": 20,
339
+ },
340
+ {
341
+ "letterSpacing": 0,
342
+ },
343
+ ]
344
+ }
345
+ >
346
+ Preview
347
+ </Text>
348
+ </View>
349
+ </View>
350
+ </View>
351
+ <View
352
+ testID="bottom-sheet-footer"
353
+ >
354
+ <View
355
+ style={
356
+ [
357
+ {
358
+ "backgroundColor": "rgba(255, 255, 255, 1)",
359
+ },
360
+ {
361
+ "paddingBottom": 0,
362
+ },
363
+ ]
364
+ }
365
+ />
366
+ </View>
367
+ </View>
368
+ </View>,
154
369
  ]
155
370
  `;
@@ -12,6 +12,7 @@
12
12
  "AutoLink",
13
13
  "Banner",
14
14
  "BottomSheet",
15
+ "BottomSheet.InputText",
15
16
  "BottomSheetOption",
16
17
  "Button",
17
18
  "ButtonGroup",