@jobber/components-native 0.84.4-JOB-138679-b1552ab.68 → 0.84.4-match-mobi-accfa8a.9
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 +5 -3
- package/dist/src/BottomSheet/BottomSheet.js +58 -32
- package/dist/src/BottomSheet/BottomSheet.style.js +8 -9
- package/dist/src/BottomSheet/hooks/useBottomSheetBackHandler.js +26 -0
- package/dist/src/Chip/Chip.js +12 -1
- package/dist/src/Chip/Chip.style.js +1 -1
- package/dist/src/Content/ContentHorizontal.style.js +15 -0
- package/dist/src/Content/ContentSpaceAround.style.js +15 -0
- package/dist/src/Content/ContentVertical.style.js +15 -0
- package/dist/src/InputText/InputText.js +2 -2
- package/dist/src/ProgressBar/ProgressBar.js +10 -6
- package/dist/src/ProgressBar/ProgressBarInner.js +3 -12
- package/dist/src/ProgressBar/ProgressBarStepped.js +7 -2
- package/dist/src/StatusIndicator/StatusIndicator.js +11 -0
- package/dist/src/StatusIndicator/StatusIndicator.style.js +12 -0
- package/dist/src/StatusIndicator/StatusIndicator.type.js +1 -0
- package/dist/src/StatusIndicator/index.js +1 -0
- package/dist/src/StatusLabel/StatusLabel.js +6 -12
- package/dist/src/StatusLabel/StatusLabel.style.js +10 -13
- package/dist/src/hooks/useFormController.js +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/utils/meta/meta.json +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/src/BottomSheet/BottomSheet.d.ts +7 -3
- package/dist/types/src/BottomSheet/BottomSheet.style.d.ts +7 -14
- package/dist/types/src/BottomSheet/hooks/useBottomSheetBackHandler.d.ts +8 -0
- package/dist/types/src/Content/Content.d.ts +1 -1
- package/dist/types/src/Content/ContentHorizontal.style.d.ts +15 -0
- package/dist/types/src/Content/ContentSpaceAround.style.d.ts +15 -0
- package/dist/types/src/Content/ContentVertical.style.d.ts +15 -0
- package/dist/types/src/Form/context/types.d.ts +2 -2
- package/dist/types/src/Form/hooks/useInternalForm.d.ts +2 -2
- package/dist/types/src/InputText/InputText.d.ts +1 -1
- package/dist/types/src/Menu/types.d.ts +1 -1
- package/dist/types/src/ProgressBar/ProgressBar.d.ts +1 -1
- package/dist/types/src/ProgressBar/ProgressBarInner.d.ts +4 -1
- package/dist/types/src/ProgressBar/ProgressBarStepped.d.ts +3 -1
- package/dist/types/src/ProgressBar/types.d.ts +20 -0
- package/dist/types/src/StatusIndicator/StatusIndicator.d.ts +6 -0
- package/dist/types/src/StatusIndicator/StatusIndicator.style.d.ts +8 -0
- package/dist/types/src/StatusIndicator/StatusIndicator.type.d.ts +1 -0
- package/dist/types/src/StatusIndicator/index.d.ts +2 -0
- package/dist/types/src/StatusLabel/StatusLabel.d.ts +3 -3
- package/dist/types/src/StatusLabel/StatusLabel.style.d.ts +10 -10
- package/dist/types/src/index.d.ts +1 -0
- package/package.json +5 -3
- package/src/BottomSheet/BottomSheet.stories.tsx +128 -0
- package/src/BottomSheet/BottomSheet.style.ts +7 -14
- package/src/BottomSheet/BottomSheet.test.tsx +19 -24
- package/src/BottomSheet/BottomSheet.tsx +112 -93
- package/src/BottomSheet/hooks/useBottomSheetBackHandler.test.ts +90 -0
- package/src/BottomSheet/hooks/useBottomSheetBackHandler.ts +41 -0
- package/src/Checkbox/Checkbox.test.tsx +118 -1
- package/src/Chip/Chip.style.ts +1 -1
- package/src/Chip/Chip.tsx +19 -1
- package/src/Content/Content.tsx +6 -1
- package/src/Content/ContentHorizontal.style.ts +20 -0
- package/src/Content/ContentSpaceAround.style.ts +20 -0
- package/src/Content/ContentVertical.style.ts +20 -0
- package/src/Form/context/types.ts +2 -2
- package/src/Form/hooks/useInternalForm.ts +2 -2
- package/src/InputText/InputText.test.tsx +115 -1
- package/src/InputText/InputText.tsx +3 -3
- package/src/Menu/types.ts +1 -1
- package/src/ProgressBar/ProgressBar.test.tsx +109 -0
- package/src/ProgressBar/ProgressBar.tsx +17 -1
- package/src/ProgressBar/ProgressBarInner.tsx +7 -10
- package/src/ProgressBar/ProgressBarStepped.tsx +12 -1
- package/src/ProgressBar/__snapshots__/ProgressBar.test.tsx.snap +14 -0
- package/src/ProgressBar/types.ts +22 -0
- package/src/StatusIndicator/StatusIndicator.style.ts +14 -0
- package/src/StatusIndicator/StatusIndicator.test.tsx +42 -0
- package/src/StatusIndicator/StatusIndicator.tsx +23 -0
- package/src/StatusIndicator/StatusIndicator.type.ts +6 -0
- package/src/StatusIndicator/__snapshots__/StatusIndicator.test.tsx.snap +96 -0
- package/src/StatusIndicator/index.ts +2 -0
- package/src/StatusLabel/StatusLabel.style.ts +10 -16
- package/src/StatusLabel/StatusLabel.tsx +15 -28
- package/src/StatusLabel/__snapshots__/StatusLabel.test.tsx.snap +133 -105
- package/src/ThumbnailList/__snapshots__/ThumbnailList.test.tsx.snap +199 -1
- package/src/hooks/useFormController.ts +1 -1
- package/src/index.ts +1 -0
- package/src/utils/meta/meta.json +1 -0
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import type { ReactNode
|
|
2
|
-
import React, {
|
|
3
|
-
import type { Modalize } from "react-native-modalize";
|
|
4
|
-
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import React, { useCallback, useImperativeHandle, useRef } from "react";
|
|
5
3
|
import { Keyboard, View } from "react-native";
|
|
6
|
-
import {
|
|
4
|
+
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
5
|
+
import RNBottomSheet, {
|
|
6
|
+
BottomSheetBackdrop,
|
|
7
|
+
BottomSheetView,
|
|
8
|
+
} from "@gorhom/bottom-sheet";
|
|
9
|
+
import type { BottomSheetBackdropProps } from "@gorhom/bottom-sheet";
|
|
10
|
+
import { tokens } from "@jobber/design";
|
|
7
11
|
import { useStyles } from "./BottomSheet.style";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
12
|
+
import { BottomSheetOption } from "./components/BottomSheetOption";
|
|
13
|
+
import { useBottomSheetBackHandler } from "./hooks/useBottomSheetBackHandler";
|
|
10
14
|
import { Divider } from "../Divider";
|
|
11
15
|
import { Heading } from "../Heading";
|
|
16
|
+
import { useIsScreenReaderEnabled } from "../hooks";
|
|
12
17
|
import { useAtlantisI18n } from "../hooks/useAtlantisI18n";
|
|
13
18
|
|
|
14
19
|
export interface BottomSheetProps {
|
|
@@ -40,71 +45,89 @@ export interface BottomSheetProps {
|
|
|
40
45
|
readonly onClose?: () => void;
|
|
41
46
|
}
|
|
42
47
|
|
|
43
|
-
export
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
) {
|
|
58
|
-
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
59
|
-
const [open, setOpen] = useState<boolean>(false);
|
|
48
|
+
export interface BottomSheetRef {
|
|
49
|
+
open: () => void;
|
|
50
|
+
close: () => void;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function BottomSheet({
|
|
54
|
+
children,
|
|
55
|
+
showCancel,
|
|
56
|
+
loading = false,
|
|
57
|
+
heading,
|
|
58
|
+
onOpen,
|
|
59
|
+
onClose,
|
|
60
|
+
ref,
|
|
61
|
+
}: BottomSheetProps & { readonly ref?: React.Ref<BottomSheetRef> }) {
|
|
60
62
|
const styles = useStyles();
|
|
63
|
+
const isScreenReaderEnabled = useIsScreenReaderEnabled();
|
|
64
|
+
|
|
65
|
+
const cancellable = (showCancel && !loading) || isScreenReaderEnabled;
|
|
66
|
+
|
|
67
|
+
const { t } = useAtlantisI18n();
|
|
68
|
+
const insets = useSafeAreaInsets();
|
|
69
|
+
const previousIndexRef = useRef(-1);
|
|
70
|
+
const bottomSheetRef = useRef<RNBottomSheet>(null);
|
|
71
|
+
const { handleSheetPositionChange } =
|
|
72
|
+
useBottomSheetBackHandler(bottomSheetRef);
|
|
73
|
+
|
|
74
|
+
useImperativeHandle(ref, () => ({
|
|
75
|
+
open: () => {
|
|
76
|
+
bottomSheetRef.current?.expand();
|
|
77
|
+
},
|
|
78
|
+
close: () => {
|
|
79
|
+
close();
|
|
80
|
+
},
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
const close = useCallback(() => {
|
|
84
|
+
bottomSheetRef.current?.close();
|
|
85
|
+
}, []);
|
|
86
|
+
|
|
87
|
+
const handleChange = (index: number) => {
|
|
88
|
+
// Handle Android back button
|
|
89
|
+
handleSheetPositionChange(index);
|
|
90
|
+
|
|
91
|
+
const previousIndex = previousIndexRef.current;
|
|
92
|
+
|
|
93
|
+
if (previousIndex === -1 && index >= 0) {
|
|
94
|
+
// Transitioned from closed to open
|
|
95
|
+
dismissKeyboard();
|
|
96
|
+
onOpen?.();
|
|
97
|
+
} else if (previousIndex >= 0 && index === -1) {
|
|
98
|
+
// Transitioned from open to closed
|
|
99
|
+
dismissKeyboard();
|
|
100
|
+
onClose?.();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
previousIndexRef.current = index;
|
|
104
|
+
};
|
|
61
105
|
|
|
62
106
|
return (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}}
|
|
79
|
-
styles={styles}
|
|
80
|
-
/>
|
|
81
|
-
}
|
|
82
|
-
withHandle={false}
|
|
83
|
-
withReactModal={isScreenReaderEnabled}
|
|
84
|
-
onOpen={openModal}
|
|
85
|
-
onClose={closeModal}
|
|
107
|
+
<RNBottomSheet
|
|
108
|
+
ref={bottomSheetRef}
|
|
109
|
+
index={-1}
|
|
110
|
+
backdropComponent={Backdrop}
|
|
111
|
+
backgroundStyle={styles.background}
|
|
112
|
+
enablePanDownToClose={true}
|
|
113
|
+
onChange={handleChange}
|
|
114
|
+
keyboardBlurBehavior="restore"
|
|
115
|
+
handleStyle={styles.handle}
|
|
116
|
+
>
|
|
117
|
+
<BottomSheetView
|
|
118
|
+
style={{
|
|
119
|
+
paddingBottom: insets.bottom + tokens["space-small"],
|
|
120
|
+
paddingTop: tokens["space-small"],
|
|
121
|
+
}}
|
|
86
122
|
>
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
</UNSAFE_WrappedModalize>
|
|
95
|
-
</>
|
|
123
|
+
{heading && <Header heading={heading} styles={styles} />}
|
|
124
|
+
{children}
|
|
125
|
+
{cancellable && (
|
|
126
|
+
<Footer styles={styles} close={close} cancelLabel={t("cancel")} />
|
|
127
|
+
)}
|
|
128
|
+
</BottomSheetView>
|
|
129
|
+
</RNBottomSheet>
|
|
96
130
|
);
|
|
97
|
-
|
|
98
|
-
function openModal() {
|
|
99
|
-
onOpen?.();
|
|
100
|
-
setOpen(true);
|
|
101
|
-
dismissKeyboard();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function closeModal() {
|
|
105
|
-
onClose?.();
|
|
106
|
-
setOpen(false);
|
|
107
|
-
}
|
|
108
131
|
}
|
|
109
132
|
|
|
110
133
|
function Header({
|
|
@@ -122,31 +145,21 @@ function Header({
|
|
|
122
145
|
}
|
|
123
146
|
|
|
124
147
|
function Footer({
|
|
125
|
-
cancellable,
|
|
126
|
-
onCancel,
|
|
127
148
|
styles,
|
|
149
|
+
close,
|
|
150
|
+
cancelLabel,
|
|
128
151
|
}: {
|
|
129
|
-
readonly cancellable: boolean;
|
|
130
|
-
readonly onCancel: () => void;
|
|
131
152
|
readonly styles: ReturnType<typeof useStyles>;
|
|
153
|
+
readonly close: () => void;
|
|
154
|
+
readonly cancelLabel: string;
|
|
132
155
|
}) {
|
|
133
|
-
const insets = useSafeAreaInsets();
|
|
134
|
-
const { t } = useAtlantisI18n();
|
|
135
|
-
|
|
136
156
|
return (
|
|
137
|
-
<View
|
|
138
|
-
{
|
|
139
|
-
<
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
<BottomSheetOption
|
|
144
|
-
text={t("cancel")}
|
|
145
|
-
icon={"remove"}
|
|
146
|
-
onPress={onCancel}
|
|
147
|
-
/>
|
|
148
|
-
</View>
|
|
149
|
-
)}
|
|
157
|
+
<View>
|
|
158
|
+
<View style={styles.footerDivider}>
|
|
159
|
+
<Divider />
|
|
160
|
+
</View>
|
|
161
|
+
|
|
162
|
+
<BottomSheetOption text={cancelLabel} icon="remove" onPress={close} />
|
|
150
163
|
</View>
|
|
151
164
|
);
|
|
152
165
|
}
|
|
@@ -157,10 +170,16 @@ function dismissKeyboard() {
|
|
|
157
170
|
Keyboard.dismiss();
|
|
158
171
|
}
|
|
159
172
|
|
|
160
|
-
function
|
|
161
|
-
styles
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
173
|
+
function Backdrop(bottomSheetBackdropProps: BottomSheetBackdropProps) {
|
|
174
|
+
const styles = useStyles();
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<BottomSheetBackdrop
|
|
178
|
+
{...bottomSheetBackdropProps}
|
|
179
|
+
appearsOnIndex={0}
|
|
180
|
+
disappearsOnIndex={-1}
|
|
181
|
+
style={styles.backdrop}
|
|
182
|
+
opacity={1}
|
|
183
|
+
/>
|
|
184
|
+
);
|
|
166
185
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { createRef } from "react";
|
|
2
|
+
import type { RefObject } from "react";
|
|
3
|
+
import { act, renderHook } from "@testing-library/react-native";
|
|
4
|
+
import { BackHandler } from "react-native";
|
|
5
|
+
import type BottomSheet from "@gorhom/bottom-sheet";
|
|
6
|
+
import { useBottomSheetBackHandler } from "./useBottomSheetBackHandler";
|
|
7
|
+
|
|
8
|
+
describe("useBottomSheetBackHandler", () => {
|
|
9
|
+
let mockRemove: jest.Mock;
|
|
10
|
+
let mockAddEventListener: jest.SpyInstance;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
mockRemove = jest.fn();
|
|
14
|
+
mockAddEventListener = jest.spyOn(BackHandler, "addEventListener");
|
|
15
|
+
mockAddEventListener.mockReturnValue({ remove: mockRemove });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
mockAddEventListener.mockRestore();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should register BackHandler listener when sheet becomes visible", async () => {
|
|
23
|
+
const bottomSheetRef = createRef<BottomSheet | null>();
|
|
24
|
+
const { result } = renderHook(() =>
|
|
25
|
+
useBottomSheetBackHandler(bottomSheetRef),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
await act(async () => {
|
|
29
|
+
result.current.handleSheetPositionChange(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(mockAddEventListener).toHaveBeenCalledWith(
|
|
33
|
+
"hardwareBackPress",
|
|
34
|
+
expect.any(Function),
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should call close() when back button is pressed", async () => {
|
|
39
|
+
const mockClose = jest.fn();
|
|
40
|
+
const bottomSheetRef = {
|
|
41
|
+
current: {
|
|
42
|
+
close: mockClose,
|
|
43
|
+
} as unknown as BottomSheet,
|
|
44
|
+
} as RefObject<BottomSheet | null>;
|
|
45
|
+
|
|
46
|
+
const { result } = renderHook(() =>
|
|
47
|
+
useBottomSheetBackHandler(bottomSheetRef),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await act(async () => {
|
|
51
|
+
result.current.handleSheetPositionChange(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const registeredCallback = mockAddEventListener.mock.calls[0][1];
|
|
55
|
+
const returnValue = registeredCallback();
|
|
56
|
+
|
|
57
|
+
expect(mockClose).toHaveBeenCalled();
|
|
58
|
+
expect(returnValue).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("should remove listener when sheet is dismissed", async () => {
|
|
62
|
+
const bottomSheetRef = createRef<BottomSheet | null>();
|
|
63
|
+
const { result } = renderHook(() =>
|
|
64
|
+
useBottomSheetBackHandler(bottomSheetRef),
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
await act(async () => {
|
|
68
|
+
result.current.handleSheetPositionChange(0);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
await act(async () => {
|
|
72
|
+
result.current.handleSheetPositionChange(-1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
expect(mockRemove).toHaveBeenCalled();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should not register listener when index is negative", async () => {
|
|
79
|
+
const bottomSheetRef = createRef<BottomSheet | null>();
|
|
80
|
+
const { result } = renderHook(() =>
|
|
81
|
+
useBottomSheetBackHandler(bottomSheetRef),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
await act(async () => {
|
|
85
|
+
result.current.handleSheetPositionChange(-1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
expect(mockAddEventListener).not.toHaveBeenCalled();
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { useCallback, useRef } from "react";
|
|
2
|
+
import { BackHandler, type NativeEventSubscription } from "react-native";
|
|
3
|
+
import type BottomSheet from "@gorhom/bottom-sheet";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Hook that closes the bottom sheet on the hardware back button press if it is visible
|
|
7
|
+
* @param bottomSheetRef ref to the bottom sheet component
|
|
8
|
+
*/
|
|
9
|
+
export function useBottomSheetBackHandler(
|
|
10
|
+
bottomSheetRef: React.RefObject<BottomSheet | null>,
|
|
11
|
+
): {
|
|
12
|
+
handleSheetPositionChange: (index: number) => void;
|
|
13
|
+
} {
|
|
14
|
+
const backHandlerSubscriptionRef = useRef<NativeEventSubscription | null>(
|
|
15
|
+
null,
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const handleSheetPositionChange = useCallback(
|
|
19
|
+
(index: number) => {
|
|
20
|
+
const isBottomSheetVisible = index >= 0;
|
|
21
|
+
|
|
22
|
+
if (isBottomSheetVisible && !backHandlerSubscriptionRef.current) {
|
|
23
|
+
// Setup the back handler if the bottom sheet is right in front of the user
|
|
24
|
+
backHandlerSubscriptionRef.current = BackHandler.addEventListener(
|
|
25
|
+
"hardwareBackPress",
|
|
26
|
+
() => {
|
|
27
|
+
bottomSheetRef.current?.close();
|
|
28
|
+
|
|
29
|
+
return true;
|
|
30
|
+
},
|
|
31
|
+
);
|
|
32
|
+
} else if (!isBottomSheetVisible) {
|
|
33
|
+
backHandlerSubscriptionRef.current?.remove();
|
|
34
|
+
backHandlerSubscriptionRef.current = null;
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
[bottomSheetRef],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return { handleSheetPositionChange };
|
|
41
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { fireEvent, render } from "@testing-library/react-native";
|
|
2
|
+
import { fireEvent, render, waitFor } from "@testing-library/react-native";
|
|
3
|
+
import { FormProvider, useForm } from "react-hook-form";
|
|
4
|
+
import { Button } from "react-native";
|
|
3
5
|
import { Checkbox } from "./Checkbox";
|
|
4
6
|
|
|
5
7
|
const accessibilityLabel = "testA11y";
|
|
@@ -102,3 +104,118 @@ describe("Checkbox", () => {
|
|
|
102
104
|
expect(getByTestId("minus2")).toBeDefined();
|
|
103
105
|
});
|
|
104
106
|
});
|
|
107
|
+
|
|
108
|
+
describe("Checkbox with FormProvider", () => {
|
|
109
|
+
const mockOnSubmit = jest.fn();
|
|
110
|
+
const checkboxName = "testCheckbox";
|
|
111
|
+
const checkboxLabel = "Test Checkbox";
|
|
112
|
+
const saveButtonText = "Save";
|
|
113
|
+
|
|
114
|
+
function FormWithProvider({
|
|
115
|
+
defaultChecked,
|
|
116
|
+
}: {
|
|
117
|
+
readonly defaultChecked?: boolean;
|
|
118
|
+
}) {
|
|
119
|
+
const formMethods = useForm();
|
|
120
|
+
|
|
121
|
+
return (
|
|
122
|
+
<FormProvider {...formMethods}>
|
|
123
|
+
<Checkbox
|
|
124
|
+
name={checkboxName}
|
|
125
|
+
label={checkboxLabel}
|
|
126
|
+
defaultChecked={defaultChecked}
|
|
127
|
+
accessibilityLabel={accessibilityLabel}
|
|
128
|
+
/>
|
|
129
|
+
<Button
|
|
130
|
+
onPress={formMethods.handleSubmit(values => mockOnSubmit(values))}
|
|
131
|
+
title={saveButtonText}
|
|
132
|
+
accessibilityLabel={saveButtonText}
|
|
133
|
+
/>
|
|
134
|
+
</FormProvider>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
beforeEach(() => {
|
|
139
|
+
mockOnSubmit.mockClear();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe("defaultChecked prop sets form value", () => {
|
|
143
|
+
it("sets form value to true when defaultChecked is true", async () => {
|
|
144
|
+
const { getByLabelText } = render(
|
|
145
|
+
<FormWithProvider defaultChecked={true} />,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
149
|
+
await waitFor(() => {
|
|
150
|
+
fireEvent.press(saveButton);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
154
|
+
[checkboxName]: true,
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it("sets form value to false when defaultChecked is false", async () => {
|
|
159
|
+
const { getByLabelText } = render(
|
|
160
|
+
<FormWithProvider defaultChecked={false} />,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
164
|
+
await waitFor(() => {
|
|
165
|
+
fireEvent.press(saveButton);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
169
|
+
[checkboxName]: false,
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it("sets form value to undefined when defaultChecked is undefined", async () => {
|
|
174
|
+
const { getByLabelText } = render(<FormWithProvider />);
|
|
175
|
+
|
|
176
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
177
|
+
await waitFor(() => {
|
|
178
|
+
fireEvent.press(saveButton);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
182
|
+
[checkboxName]: undefined,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
describe("checkbox value updates form value", () => {
|
|
188
|
+
it("updates form value when checkbox is toggled", async () => {
|
|
189
|
+
const { getByLabelText } = render(
|
|
190
|
+
<FormWithProvider defaultChecked={false} />,
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const checkbox = getByLabelText(accessibilityLabel);
|
|
194
|
+
fireEvent.press(checkbox);
|
|
195
|
+
|
|
196
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
197
|
+
await waitFor(() => {
|
|
198
|
+
fireEvent.press(saveButton);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
202
|
+
[checkboxName]: true,
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("preserves defaultChecked value when checkbox is not interacted with", async () => {
|
|
207
|
+
const { getByLabelText } = render(
|
|
208
|
+
<FormWithProvider defaultChecked={true} />,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const saveButton = getByLabelText(saveButtonText);
|
|
212
|
+
await waitFor(() => {
|
|
213
|
+
fireEvent.press(saveButton);
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
expect(mockOnSubmit).toHaveBeenCalledWith({
|
|
217
|
+
[checkboxName]: true,
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
package/src/Chip/Chip.style.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const useStyles = buildThemedStyles(tokens => {
|
|
|
12
12
|
justifyContent: "center",
|
|
13
13
|
marginHorizontal: tokens["space-smaller"],
|
|
14
14
|
marginTop: tokens["space-small"],
|
|
15
|
-
paddingHorizontal: tokens["space-
|
|
15
|
+
paddingHorizontal: tokens["space-base"],
|
|
16
16
|
},
|
|
17
17
|
iconLeft: {
|
|
18
18
|
marginHorizontal: tokens["space-smallest"],
|
package/src/Chip/Chip.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import type { IconNames } from "@jobber/design";
|
|
|
5
5
|
import { useStyles } from "./Chip.style";
|
|
6
6
|
import { Icon } from "../Icon";
|
|
7
7
|
import { Typography } from "../Typography";
|
|
8
|
+
import type { AtlantisThemeContextValue } from "../AtlantisThemeContext";
|
|
8
9
|
import { useAtlantisTheme } from "../AtlantisThemeContext";
|
|
9
10
|
|
|
10
11
|
export type AccentType = "client" | "invoice" | "job" | "request" | "quote";
|
|
@@ -86,6 +87,7 @@ export function Chip({
|
|
|
86
87
|
: tokens["color-interactive--background"],
|
|
87
88
|
},
|
|
88
89
|
isActive && { backgroundColor: accentColor },
|
|
90
|
+
getBorderStyle(inactiveBackgroundColor, tokens),
|
|
89
91
|
];
|
|
90
92
|
const dismiss =
|
|
91
93
|
(isActive || inactiveBackgroundColor === "surface") &&
|
|
@@ -96,7 +98,7 @@ export function Chip({
|
|
|
96
98
|
iconCustomColor: iconColor,
|
|
97
99
|
dismissColor: dismiss,
|
|
98
100
|
};
|
|
99
|
-
}, [accent, isActive, inactiveBackgroundColor]);
|
|
101
|
+
}, [accent, isActive, inactiveBackgroundColor, getBorderStyle, styles]);
|
|
100
102
|
|
|
101
103
|
const accessibilityState = useMemo(() => {
|
|
102
104
|
const checkableRoles = ["radio", "switch", "togglebutton", "checkbox"];
|
|
@@ -145,3 +147,19 @@ export function Chip({
|
|
|
145
147
|
</Pressable>
|
|
146
148
|
);
|
|
147
149
|
}
|
|
150
|
+
|
|
151
|
+
function getBorderStyle(
|
|
152
|
+
inactiveBackgroundColor: "surface" | "background",
|
|
153
|
+
tokens: AtlantisThemeContextValue["tokens"],
|
|
154
|
+
) {
|
|
155
|
+
let borderColor = "transparent";
|
|
156
|
+
|
|
157
|
+
if (inactiveBackgroundColor === "surface") {
|
|
158
|
+
borderColor = tokens["color-border"];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
borderColor,
|
|
163
|
+
borderWidth: tokens["border-base"],
|
|
164
|
+
};
|
|
165
|
+
}
|
package/src/Content/Content.tsx
CHANGED
|
@@ -9,10 +9,15 @@ import { useSpaceAroundStyles } from "./ContentSpaceAround.style";
|
|
|
9
9
|
export type Spacing =
|
|
10
10
|
| "none"
|
|
11
11
|
| "base"
|
|
12
|
+
| "slim"
|
|
12
13
|
| "small"
|
|
13
14
|
| "smaller"
|
|
14
15
|
| "smallest"
|
|
15
|
-
| "
|
|
16
|
+
| "minuscule"
|
|
17
|
+
| "large"
|
|
18
|
+
| "larger"
|
|
19
|
+
| "largest"
|
|
20
|
+
| "extravagant";
|
|
16
21
|
|
|
17
22
|
export interface ContentUnsafeStyle {
|
|
18
23
|
container?: StyleProp<ViewStyle>;
|
|
@@ -16,6 +16,10 @@ export const useHorizontalStyles = buildThemedStyles(tokens => {
|
|
|
16
16
|
padding: 0,
|
|
17
17
|
},
|
|
18
18
|
|
|
19
|
+
minusculeChildSpace: {
|
|
20
|
+
paddingLeft: tokens["space-minuscule"],
|
|
21
|
+
},
|
|
22
|
+
|
|
19
23
|
smallestChildSpace: {
|
|
20
24
|
paddingLeft: tokens["space-smallest"],
|
|
21
25
|
},
|
|
@@ -28,6 +32,10 @@ export const useHorizontalStyles = buildThemedStyles(tokens => {
|
|
|
28
32
|
paddingLeft: tokens["space-small"],
|
|
29
33
|
},
|
|
30
34
|
|
|
35
|
+
slimChildSpace: {
|
|
36
|
+
paddingLeft: tokens["space-slim"],
|
|
37
|
+
},
|
|
38
|
+
|
|
31
39
|
baseChildSpace: {
|
|
32
40
|
paddingLeft: tokens["space-base"],
|
|
33
41
|
},
|
|
@@ -35,5 +43,17 @@ export const useHorizontalStyles = buildThemedStyles(tokens => {
|
|
|
35
43
|
largeChildSpace: {
|
|
36
44
|
paddingLeft: tokens["space-large"],
|
|
37
45
|
},
|
|
46
|
+
|
|
47
|
+
largerChildSpace: {
|
|
48
|
+
paddingLeft: tokens["space-larger"],
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
largestChildSpace: {
|
|
52
|
+
paddingLeft: tokens["space-largest"],
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
extravagantChildSpace: {
|
|
56
|
+
paddingLeft: tokens["space-extravagant"],
|
|
57
|
+
},
|
|
38
58
|
};
|
|
39
59
|
});
|
|
@@ -6,6 +6,10 @@ export const useSpaceAroundStyles = buildThemedStyles(tokens => {
|
|
|
6
6
|
padding: 0,
|
|
7
7
|
},
|
|
8
8
|
|
|
9
|
+
minusculeSpace: {
|
|
10
|
+
padding: tokens["space-minuscule"],
|
|
11
|
+
},
|
|
12
|
+
|
|
9
13
|
smallestSpace: {
|
|
10
14
|
padding: tokens["space-smallest"],
|
|
11
15
|
},
|
|
@@ -18,6 +22,10 @@ export const useSpaceAroundStyles = buildThemedStyles(tokens => {
|
|
|
18
22
|
padding: tokens["space-small"],
|
|
19
23
|
},
|
|
20
24
|
|
|
25
|
+
slimSpace: {
|
|
26
|
+
padding: tokens["space-slim"],
|
|
27
|
+
},
|
|
28
|
+
|
|
21
29
|
baseSpace: {
|
|
22
30
|
padding: tokens["space-base"],
|
|
23
31
|
},
|
|
@@ -25,5 +33,17 @@ export const useSpaceAroundStyles = buildThemedStyles(tokens => {
|
|
|
25
33
|
largeSpace: {
|
|
26
34
|
padding: tokens["space-large"],
|
|
27
35
|
},
|
|
36
|
+
|
|
37
|
+
largerSpace: {
|
|
38
|
+
padding: tokens["space-larger"],
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
largestSpace: {
|
|
42
|
+
padding: tokens["space-largest"],
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
extravagantSpace: {
|
|
46
|
+
padding: tokens["space-extravagant"],
|
|
47
|
+
},
|
|
28
48
|
};
|
|
29
49
|
});
|
|
@@ -16,6 +16,10 @@ export const useVerticalStyles = buildThemedStyles(tokens => {
|
|
|
16
16
|
padding: 0,
|
|
17
17
|
},
|
|
18
18
|
|
|
19
|
+
minusculeChildSpace: {
|
|
20
|
+
paddingTop: tokens["space-minuscule"],
|
|
21
|
+
},
|
|
22
|
+
|
|
19
23
|
smallestChildSpace: {
|
|
20
24
|
paddingTop: tokens["space-smallest"],
|
|
21
25
|
},
|
|
@@ -28,6 +32,10 @@ export const useVerticalStyles = buildThemedStyles(tokens => {
|
|
|
28
32
|
paddingTop: tokens["space-small"],
|
|
29
33
|
},
|
|
30
34
|
|
|
35
|
+
slimChildSpace: {
|
|
36
|
+
paddingTop: tokens["space-slim"],
|
|
37
|
+
},
|
|
38
|
+
|
|
31
39
|
baseChildSpace: {
|
|
32
40
|
paddingTop: tokens["space-base"],
|
|
33
41
|
},
|
|
@@ -35,5 +43,17 @@ export const useVerticalStyles = buildThemedStyles(tokens => {
|
|
|
35
43
|
largeChildSpace: {
|
|
36
44
|
paddingTop: tokens["space-large"],
|
|
37
45
|
},
|
|
46
|
+
|
|
47
|
+
largerChildSpace: {
|
|
48
|
+
paddingTop: tokens["space-larger"],
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
largestChildSpace: {
|
|
52
|
+
paddingTop: tokens["space-largest"],
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
extravagantChildSpace: {
|
|
56
|
+
paddingTop: tokens["space-extravagant"],
|
|
57
|
+
},
|
|
38
58
|
};
|
|
39
59
|
});
|