@jobber/components-native 0.89.3 → 0.89.5-JOB-139254-4e3c64d.7
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.
- package/dist/package.json +2 -2
- package/dist/src/Form/Form.js +2 -1
- package/dist/src/Form/hooks/useInternalForm.js +6 -3
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/Form/hooks/useInternalForm.d.ts +2 -2
- package/dist/types/src/Form/types.d.ts +6 -0
- package/package.json +2 -2
- package/src/AtlantisThemeContext/AtlantisThemeContext.test.tsx +5 -5
- package/src/ContentOverlay/ContentOverlay.test.tsx +4 -4
- package/src/ContentOverlay/hooks/useKeyboardVisibility.test.ts +6 -6
- package/src/Form/Form.tsx +2 -0
- package/src/Form/hooks/useInternalForm.ts +9 -2
- package/src/Form/types.ts +7 -0
- package/src/FormatFile/components/FormatFileBottomSheet/FormatFileBottomSheet.test.tsx +2 -2
- package/src/Glimmer/Glimmer.test.tsx +2 -2
|
@@ -2,7 +2,7 @@ import type { DeepPartial, FieldValues, UseFormHandleSubmit, UseFormReturn } fro
|
|
|
2
2
|
import type { MutableRefObject, RefObject } from "react";
|
|
3
3
|
import type { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view";
|
|
4
4
|
import type { InternalFormProps } from "../types";
|
|
5
|
-
type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<InternalFormProps<T, SubmitResponseType>, "mode" | "reValidateMode" | "initialValues" | "formRef" | "localCacheKey" | "localCacheExclude" | "localCacheId"> & {
|
|
5
|
+
type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<InternalFormProps<T, SubmitResponseType>, "mode" | "reValidateMode" | "initialValues" | "formRef" | "localCacheKey" | "localCacheExclude" | "localCacheId" | "removeLocalCacheOnBackOffline"> & {
|
|
6
6
|
scrollViewRef?: RefObject<KeyboardAwareScrollView>;
|
|
7
7
|
readonly saveButtonHeight: number;
|
|
8
8
|
readonly messageBannerHeight: number;
|
|
@@ -15,5 +15,5 @@ interface UseInternalForm<T extends FieldValues> {
|
|
|
15
15
|
readonly removeListenerRef: MutableRefObject<() => void>;
|
|
16
16
|
readonly setLocalCache: (data: DeepPartial<T>) => void;
|
|
17
17
|
}
|
|
18
|
-
export declare function useInternalForm<T extends FieldValues, SubmitResponseType>({ mode, reValidateMode, initialValues, formRef, localCacheKey, localCacheId, scrollViewRef, saveButtonHeight, messageBannerHeight, }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T>;
|
|
18
|
+
export declare function useInternalForm<T extends FieldValues, SubmitResponseType>({ mode, reValidateMode, initialValues, formRef, localCacheKey, localCacheId, scrollViewRef, saveButtonHeight, messageBannerHeight, removeLocalCacheOnBackOffline, }: UseInternalFormProps<T, SubmitResponseType>): UseInternalForm<T>;
|
|
19
19
|
export {};
|
|
@@ -131,6 +131,12 @@ export interface FormProps<T extends FieldValues, SubmitResponseType> {
|
|
|
131
131
|
* If a user opens the same form the data will only be loaded if the `localCacheId` matches
|
|
132
132
|
*/
|
|
133
133
|
localCacheId?: string | string[];
|
|
134
|
+
/**
|
|
135
|
+
* If true, the local cache will be removed when the user navigates away from
|
|
136
|
+
* the dirty form even when offline. By default, cache is only removed on back when online.
|
|
137
|
+
* Defaults to false.
|
|
138
|
+
*/
|
|
139
|
+
removeLocalCacheOnBackOffline?: boolean;
|
|
134
140
|
/**
|
|
135
141
|
* Secondary Action for ButtonGroup
|
|
136
142
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jobber/components-native",
|
|
3
|
-
"version": "0.89.
|
|
3
|
+
"version": "0.89.5-JOB-139254-4e3c64d.7+4e3c64d19",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "React Native implementation of Atlantis",
|
|
6
6
|
"repository": {
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"react-native-safe-area-context": "^5.4.0",
|
|
95
95
|
"react-native-svg": ">=12.0.0"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "4e3c64d1982cb68580827ad583b4812b3a240e7c"
|
|
98
98
|
}
|
|
@@ -51,24 +51,24 @@ describe("ThemeContext", () => {
|
|
|
51
51
|
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
it("updates the theme and tokens", () => {
|
|
54
|
+
it("updates the theme and tokens", async () => {
|
|
55
55
|
const { result } = renderHook(useAtlantisTheme, {
|
|
56
56
|
wrapper: (props: AtlantisThemeContextProviderProps) => (
|
|
57
57
|
<Wrapper {...props} />
|
|
58
58
|
),
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
-
act(() => result.current.setTheme("dark"));
|
|
61
|
+
await act(async () => result.current.setTheme("dark"));
|
|
62
62
|
expect(result.current.theme).toBe("dark");
|
|
63
63
|
expect(result.current.tokens).toEqual(expectedDarkTokens);
|
|
64
64
|
|
|
65
|
-
act(() => result.current.setTheme("light"));
|
|
65
|
+
await act(async () => result.current.setTheme("light"));
|
|
66
66
|
expect(result.current.theme).toBe("light");
|
|
67
67
|
expect(result.current.tokens).toEqual(expectedLightTokens);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
describe("when theme is forced for provider", () => {
|
|
71
|
-
it("ignores updates to the global theme", () => {
|
|
71
|
+
it("ignores updates to the global theme", async () => {
|
|
72
72
|
const { result } = renderHook(useAtlantisTheme, {
|
|
73
73
|
wrapper: (props: AtlantisThemeContextProviderProps) => (
|
|
74
74
|
<WrapperWithOverride {...props} dangerouslyOverrideTheme="light" />
|
|
@@ -76,7 +76,7 @@ describe("ThemeContext", () => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
// Update the global theme
|
|
79
|
-
act(() => result.current.setTheme("dark"));
|
|
79
|
+
await act(async () => result.current.setTheme("dark"));
|
|
80
80
|
|
|
81
81
|
// This hook shouldn't be affected by it because it's set to the light theme
|
|
82
82
|
expect(result.current.theme).toBe("light");
|
|
@@ -111,7 +111,7 @@ async function renderAndOpenContentOverlay(
|
|
|
111
111
|
) {
|
|
112
112
|
const rendered = renderContentOverlay(defaultOptions);
|
|
113
113
|
|
|
114
|
-
act(() => {
|
|
114
|
+
await act(async () => {
|
|
115
115
|
fireEvent.press(rendered.getByLabelText(defaultOptions.buttonLabel));
|
|
116
116
|
});
|
|
117
117
|
|
|
@@ -144,7 +144,7 @@ describe("when the close button is clicked on an open content overlay", () => {
|
|
|
144
144
|
};
|
|
145
145
|
const contentOverlayScreen = await renderAndOpenContentOverlay(options);
|
|
146
146
|
|
|
147
|
-
act(() => {
|
|
147
|
+
await act(async () => {
|
|
148
148
|
fireEvent.press(
|
|
149
149
|
contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
|
|
150
150
|
);
|
|
@@ -165,7 +165,7 @@ describe("when the close button is clicked on an open content overlay with a def
|
|
|
165
165
|
};
|
|
166
166
|
const contentOverlayScreen = await renderAndOpenContentOverlay(options);
|
|
167
167
|
|
|
168
|
-
act(() => {
|
|
168
|
+
await act(async () => {
|
|
169
169
|
fireEvent.press(
|
|
170
170
|
contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
|
|
171
171
|
);
|
|
@@ -301,7 +301,7 @@ describe("when the close button is clicked on an open content overlay with a def
|
|
|
301
301
|
};
|
|
302
302
|
const contentOverlayScreen = await renderAndOpenContentOverlay(options);
|
|
303
303
|
|
|
304
|
-
act(() => {
|
|
304
|
+
await act(async () => {
|
|
305
305
|
fireEvent.press(
|
|
306
306
|
contentOverlayScreen.getByTestId("ATL-Overlay-CloseButton"),
|
|
307
307
|
);
|
|
@@ -8,19 +8,19 @@ const keyboardEvent: Partial<KeyboardEvent> = {
|
|
|
8
8
|
};
|
|
9
9
|
|
|
10
10
|
describe("when the user is typing", () => {
|
|
11
|
-
it("sets the isKeyboardVisible to true", () => {
|
|
11
|
+
it("sets the isKeyboardVisible to true", async () => {
|
|
12
12
|
const { result } = renderHook(() => useKeyboardVisibility());
|
|
13
13
|
|
|
14
|
-
act(() => {
|
|
14
|
+
await act(async () => {
|
|
15
15
|
DeviceEventEmitter.emit("keyboardDidShow", keyboardEvent);
|
|
16
16
|
});
|
|
17
17
|
|
|
18
18
|
expect(result.current.isKeyboardVisible).toBe(true);
|
|
19
19
|
});
|
|
20
|
-
it("the keyboardDidShow event emits the keyboard height", () => {
|
|
20
|
+
it("the keyboardDidShow event emits the keyboard height", async () => {
|
|
21
21
|
const { result } = renderHook(() => useKeyboardVisibility());
|
|
22
22
|
|
|
23
|
-
act(() => {
|
|
23
|
+
await act(async () => {
|
|
24
24
|
DeviceEventEmitter.emit("keyboardDidShow", keyboardEvent);
|
|
25
25
|
});
|
|
26
26
|
|
|
@@ -31,10 +31,10 @@ describe("when the user is typing", () => {
|
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
describe("when the user not typing", () => {
|
|
34
|
-
it("sets the isKeyboardVisible to false", () => {
|
|
34
|
+
it("sets the isKeyboardVisible to false", async () => {
|
|
35
35
|
const { result } = renderHook(() => useKeyboardVisibility());
|
|
36
36
|
|
|
37
|
-
act(() => {
|
|
37
|
+
await act(async () => {
|
|
38
38
|
DeviceEventEmitter.emit("keyboardDidHide");
|
|
39
39
|
});
|
|
40
40
|
|
package/src/Form/Form.tsx
CHANGED
|
@@ -66,6 +66,7 @@ function InternalForm<T extends FieldValues, S>({
|
|
|
66
66
|
saveButtonOffset,
|
|
67
67
|
showStickySaveButton = false,
|
|
68
68
|
renderFooter,
|
|
69
|
+
removeLocalCacheOnBackOffline,
|
|
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
|
+
removeLocalCacheOnBackOffline,
|
|
90
92
|
});
|
|
91
93
|
const { windowHeight, headerHeight } = useScreenInformation();
|
|
92
94
|
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
@@ -20,6 +20,7 @@ type UseInternalFormProps<T extends FieldValues, SubmitResponseType> = Pick<
|
|
|
20
20
|
| "localCacheKey"
|
|
21
21
|
| "localCacheExclude"
|
|
22
22
|
| "localCacheId"
|
|
23
|
+
| "removeLocalCacheOnBackOffline"
|
|
23
24
|
> & {
|
|
24
25
|
scrollViewRef?: RefObject<KeyboardAwareScrollView>;
|
|
25
26
|
readonly saveButtonHeight: number;
|
|
@@ -45,6 +46,7 @@ export function useInternalForm<T extends FieldValues, SubmitResponseType>({
|
|
|
45
46
|
scrollViewRef,
|
|
46
47
|
saveButtonHeight,
|
|
47
48
|
messageBannerHeight,
|
|
49
|
+
removeLocalCacheOnBackOffline = 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 = removeLocalCacheOnBackOffline
|
|
88
|
+
? true
|
|
89
|
+
: isOnline;
|
|
90
|
+
|
|
85
91
|
const removeListenerRef = useConfirmBeforeBack({
|
|
86
92
|
alwaysPreventBack: isSubmitting,
|
|
87
93
|
shouldShowAlert: isDirty,
|
|
88
|
-
onAcceptEvent:
|
|
89
|
-
showLostProgressMessage:
|
|
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
|
@@ -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
|
+
removeLocalCacheOnBackOffline?: boolean;
|
|
180
|
+
|
|
174
181
|
/**
|
|
175
182
|
* Secondary Action for ButtonGroup
|
|
176
183
|
*/
|
|
@@ -39,11 +39,11 @@ const basicRenderTestWithValue = () => {
|
|
|
39
39
|
const removeLabel = `Remove ${bottomSheetOptionsSuffix}`;
|
|
40
40
|
let tree: RenderAPI;
|
|
41
41
|
|
|
42
|
-
beforeEach(() => {
|
|
42
|
+
beforeEach(async () => {
|
|
43
43
|
tree = renderBottomSheet(
|
|
44
44
|
bottomSheetOptionsSuffix as BottomSheetOptionsSuffix,
|
|
45
45
|
);
|
|
46
|
-
act(() => {
|
|
46
|
+
await act(async () => {
|
|
47
47
|
bottomSheetRef.current?.open();
|
|
48
48
|
});
|
|
49
49
|
});
|
|
@@ -46,7 +46,7 @@ describe("Glimmer", () => {
|
|
|
46
46
|
);
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
it("renders sets the correct width", () => {
|
|
49
|
+
it("renders sets the correct width", async () => {
|
|
50
50
|
jest.useFakeTimers();
|
|
51
51
|
|
|
52
52
|
// Spy on Animated.timing to verify the animation configuration
|
|
@@ -54,7 +54,7 @@ describe("Glimmer", () => {
|
|
|
54
54
|
|
|
55
55
|
render(<Glimmer />);
|
|
56
56
|
|
|
57
|
-
act(() => {
|
|
57
|
+
await act(async () => {
|
|
58
58
|
fireEvent(screen.getByTestId(GLIMMER_TEST_ID), "onLayout", {
|
|
59
59
|
nativeEvent: { layout: { width: 300 } },
|
|
60
60
|
});
|