@terreno/ui 0.12.2 → 0.13.3
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/ConsentFormScreen.js +8 -7
- package/dist/ConsentFormScreen.js.map +1 -1
- package/dist/Field.js.map +1 -1
- package/dist/PickerSelect.d.ts +22 -10
- package/dist/PickerSelect.js +11 -9
- package/dist/PickerSelect.js.map +1 -1
- package/dist/SelectBadge.js +11 -1
- package/dist/SelectBadge.js.map +1 -1
- package/dist/SelectField.js +2 -2
- package/dist/SelectField.js.map +1 -1
- package/dist/SidebarNavigation.native.js.map +1 -1
- package/dist/Signature.js +4 -0
- package/dist/Signature.js.map +1 -1
- package/dist/Signature.native.js +4 -0
- package/dist/Signature.native.js.map +1 -1
- package/dist/Theme.d.ts +1 -1
- package/dist/Theme.js.map +1 -1
- package/dist/useConsentForms.d.ts +4 -3
- package/dist/useConsentForms.js +26 -4
- package/dist/useConsentForms.js.map +1 -1
- package/dist/useConsentHistory.d.ts +4 -3
- package/dist/useConsentHistory.js +26 -4
- package/dist/useConsentHistory.js.map +1 -1
- package/dist/useSubmitConsent.d.ts +7 -6
- package/dist/useSubmitConsent.js +25 -3
- package/dist/useSubmitConsent.js.map +1 -1
- package/package.json +1 -1
- package/src/ConsentFormScreen.test.tsx +91 -23
- package/src/ConsentFormScreen.tsx +21 -0
- package/src/Field.tsx +1 -1
- package/src/HeightField.test.tsx +68 -0
- package/src/Modal.tsx +2 -2
- package/src/NumberField.test.tsx +13 -19
- package/src/PickerSelect.tsx +48 -34
- package/src/SelectBadge.tsx +7 -3
- package/src/SelectField.tsx +2 -2
- package/src/SidebarNavigation.native.tsx +6 -2
- package/src/Signature.native.tsx +4 -0
- package/src/Signature.test.tsx +10 -0
- package/src/Signature.tsx +4 -0
- package/src/Theme.tsx +17 -14
- package/src/Tooltip.test.tsx +41 -22
- package/src/__snapshots__/AddressField.test.tsx.snap +0 -1
- package/src/__snapshots__/CustomSelectField.test.tsx.snap +0 -7
- package/src/__snapshots__/Field.test.tsx.snap +0 -6
- package/src/__snapshots__/PickerSelect.test.tsx.snap +0 -7
- package/src/__snapshots__/SelectField.test.tsx.snap +0 -6
- package/src/__snapshots__/TerrenoProvider.test.tsx.snap +4 -18
- package/src/__snapshots__/TimezonePicker.test.tsx.snap +0 -6
- package/src/bunSetup.ts +22 -0
- package/src/polyfill.d.ts +1 -1
- package/src/table/__snapshots__/TableBadge.test.tsx.snap +0 -1
- package/src/useConsentForms.test.ts +25 -0
- package/src/useConsentForms.ts +32 -7
- package/src/useConsentHistory.test.ts +99 -0
- package/src/useConsentHistory.ts +31 -6
- package/src/useSubmitConsent.test.ts +24 -0
- package/src/useSubmitConsent.ts +35 -10
package/src/PickerSelect.tsx
CHANGED
|
@@ -25,15 +25,18 @@
|
|
|
25
25
|
|
|
26
26
|
import {Picker} from "@react-native-picker/picker";
|
|
27
27
|
import isEqual from "lodash/isEqual";
|
|
28
|
-
import {useCallback, useEffect, useMemo, useState} from "react";
|
|
28
|
+
import {type ComponentType, type ReactNode, useCallback, useEffect, useMemo, useState} from "react";
|
|
29
29
|
import {
|
|
30
30
|
Keyboard,
|
|
31
31
|
Modal,
|
|
32
|
+
type ModalProps,
|
|
32
33
|
Platform,
|
|
33
34
|
Pressable,
|
|
35
|
+
type PressableProps,
|
|
34
36
|
StyleSheet,
|
|
35
37
|
Text,
|
|
36
38
|
TextInput,
|
|
39
|
+
type TextInputProps,
|
|
37
40
|
TouchableOpacity,
|
|
38
41
|
View,
|
|
39
42
|
} from "react-native";
|
|
@@ -67,14 +70,23 @@ export const defaultStyles = StyleSheet.create({
|
|
|
67
70
|
},
|
|
68
71
|
});
|
|
69
72
|
|
|
73
|
+
/** A single option for the picker select component. */
|
|
74
|
+
export interface PickerSelectItem {
|
|
75
|
+
label: string;
|
|
76
|
+
value: string | number | null;
|
|
77
|
+
key?: string | number;
|
|
78
|
+
color?: string;
|
|
79
|
+
inputLabel?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
70
82
|
export interface RNPickerSelectProps {
|
|
71
|
-
onValueChange: (value:
|
|
72
|
-
items:
|
|
73
|
-
value?:
|
|
74
|
-
placeholder?:
|
|
83
|
+
onValueChange: (value: string | number | null, index: number) => void;
|
|
84
|
+
items: PickerSelectItem[];
|
|
85
|
+
value?: string | number | null;
|
|
86
|
+
placeholder?: Partial<PickerSelectItem>;
|
|
75
87
|
disabled?: boolean;
|
|
76
88
|
itemKey?: string | number;
|
|
77
|
-
children?:
|
|
89
|
+
children?: ReactNode;
|
|
78
90
|
onOpen?: () => void;
|
|
79
91
|
useNativeAndroidPickerStyle?: boolean;
|
|
80
92
|
fixAndroidTouchableBug?: boolean;
|
|
@@ -87,18 +99,18 @@ export interface RNPickerSelectProps {
|
|
|
87
99
|
onClose?: () => void;
|
|
88
100
|
|
|
89
101
|
// Modal props (iOS only)
|
|
90
|
-
modalProps?:
|
|
102
|
+
modalProps?: Partial<ModalProps>;
|
|
91
103
|
|
|
92
104
|
// TextInput props
|
|
93
|
-
textInputProps?:
|
|
105
|
+
textInputProps?: Partial<TextInputProps>;
|
|
94
106
|
|
|
95
107
|
// Touchable Done props (iOS only)
|
|
96
|
-
touchableDoneProps?:
|
|
108
|
+
touchableDoneProps?: Partial<PressableProps>;
|
|
97
109
|
|
|
98
110
|
// Touchable wrapper props
|
|
99
|
-
touchableWrapperProps?:
|
|
111
|
+
touchableWrapperProps?: Partial<PressableProps>;
|
|
100
112
|
|
|
101
|
-
InputAccessoryView?:
|
|
113
|
+
InputAccessoryView?: ComponentType<{testID?: string}>;
|
|
102
114
|
}
|
|
103
115
|
|
|
104
116
|
export function RNPickerSelect({
|
|
@@ -125,7 +137,9 @@ export function RNPickerSelect({
|
|
|
125
137
|
InputAccessoryView,
|
|
126
138
|
}: RNPickerSelectProps) {
|
|
127
139
|
const [showPicker, setShowPicker] = useState<boolean>(false);
|
|
128
|
-
const [animationType, setAnimationType] = useState(
|
|
140
|
+
const [animationType, setAnimationType] = useState<"none" | "slide" | "fade" | undefined>(
|
|
141
|
+
undefined
|
|
142
|
+
);
|
|
129
143
|
const [orientation, setOrientation] = useState<"portrait" | "landscape">("portrait");
|
|
130
144
|
const [doneDepressed, setDoneDepressed] = useState<boolean>(false);
|
|
131
145
|
const {theme} = useTheme();
|
|
@@ -159,12 +173,12 @@ export function RNPickerSelect({
|
|
|
159
173
|
}, [items, placeholder]);
|
|
160
174
|
|
|
161
175
|
const getSelectedItem = useCallback(
|
|
162
|
-
(key:
|
|
163
|
-
let idx = options.findIndex((item
|
|
164
|
-
if (item
|
|
176
|
+
(key: string | number | undefined, val: string | number | null | undefined) => {
|
|
177
|
+
let idx = options.findIndex((item) => {
|
|
178
|
+
if (item?.key && key) {
|
|
165
179
|
return isEqual(item.key, key);
|
|
166
180
|
}
|
|
167
|
-
return isEqual(item
|
|
181
|
+
return isEqual(item?.value, val);
|
|
168
182
|
});
|
|
169
183
|
if (idx === -1) {
|
|
170
184
|
idx = 0;
|
|
@@ -177,7 +191,7 @@ export function RNPickerSelect({
|
|
|
177
191
|
[options]
|
|
178
192
|
);
|
|
179
193
|
|
|
180
|
-
const [selectedItem, setSelectedItem] = useState<
|
|
194
|
+
const [selectedItem, setSelectedItem] = useState<Partial<PickerSelectItem>>(() => {
|
|
181
195
|
return getSelectedItem(itemKey, value).selectedItem;
|
|
182
196
|
});
|
|
183
197
|
|
|
@@ -195,13 +209,17 @@ export function RNPickerSelect({
|
|
|
195
209
|
togglePicker(false, onDownArrow);
|
|
196
210
|
};
|
|
197
211
|
|
|
198
|
-
const onValueChangeEvent = (val:
|
|
212
|
+
const onValueChangeEvent = (val: string | number | null, index: number) => {
|
|
199
213
|
const item = getSelectedItem(itemKey, val);
|
|
200
214
|
onValueChange(val, index);
|
|
201
215
|
setSelectedItem(item.selectedItem);
|
|
202
216
|
};
|
|
203
217
|
|
|
204
|
-
const onOrientationChange = ({
|
|
218
|
+
const onOrientationChange = ({
|
|
219
|
+
nativeEvent,
|
|
220
|
+
}: {
|
|
221
|
+
nativeEvent: {orientation: "portrait" | "landscape"};
|
|
222
|
+
}) => {
|
|
205
223
|
setOrientation(nativeEvent.orientation);
|
|
206
224
|
};
|
|
207
225
|
|
|
@@ -215,7 +233,7 @@ export function RNPickerSelect({
|
|
|
215
233
|
}
|
|
216
234
|
};
|
|
217
235
|
|
|
218
|
-
const togglePicker = (animate = false, postToggleCallback?:
|
|
236
|
+
const togglePicker = (animate = false, postToggleCallback?: () => void) => {
|
|
219
237
|
if (disabled) {
|
|
220
238
|
return;
|
|
221
239
|
}
|
|
@@ -237,13 +255,13 @@ export function RNPickerSelect({
|
|
|
237
255
|
};
|
|
238
256
|
|
|
239
257
|
const renderPickerItems = () => {
|
|
240
|
-
return options?.map((item
|
|
258
|
+
return options?.map((item) => {
|
|
241
259
|
return (
|
|
242
260
|
<Picker.Item
|
|
243
|
-
color={item
|
|
244
|
-
key={item
|
|
245
|
-
label={item
|
|
246
|
-
value={item
|
|
261
|
+
color={item?.color}
|
|
262
|
+
key={item?.key || item?.label}
|
|
263
|
+
label={item?.label}
|
|
264
|
+
value={item?.value}
|
|
247
265
|
/>
|
|
248
266
|
);
|
|
249
267
|
});
|
|
@@ -408,7 +426,6 @@ export function RNPickerSelect({
|
|
|
408
426
|
]}
|
|
409
427
|
>
|
|
410
428
|
<Pressable
|
|
411
|
-
activeOpacity={1}
|
|
412
429
|
onPress={() => {
|
|
413
430
|
togglePicker(true);
|
|
414
431
|
}}
|
|
@@ -467,14 +484,11 @@ export function RNPickerSelect({
|
|
|
467
484
|
};
|
|
468
485
|
|
|
469
486
|
const renderAndroidHeadless = () => {
|
|
470
|
-
|
|
487
|
+
// noExplicitAny: Component is View or Pressable depending on a bug workaround flag. View
|
|
488
|
+
// ignores Pressable-specific props (onPress) at runtime. A type-safe union cannot express this.
|
|
489
|
+
const Component = (fixAndroidTouchableBug ? View : Pressable) as unknown as typeof Pressable;
|
|
471
490
|
return (
|
|
472
|
-
<Component
|
|
473
|
-
activeOpacity={1}
|
|
474
|
-
onPress={onOpen}
|
|
475
|
-
testID="android_touchable_wrapper"
|
|
476
|
-
{...touchableWrapperProps}
|
|
477
|
-
>
|
|
491
|
+
<Component onPress={onOpen} testID="android_touchable_wrapper" {...touchableWrapperProps}>
|
|
478
492
|
<View>
|
|
479
493
|
{renderTextInputOrChildren()}
|
|
480
494
|
<Picker
|
|
@@ -636,7 +650,7 @@ export function RNPickerSelect({
|
|
|
636
650
|
// Pass the original (non-stringified) value through so lodash
|
|
637
651
|
// `isEqual` matching in `getSelectedItem` works for number /
|
|
638
652
|
// object values.
|
|
639
|
-
const originalValue = options[originalIndex]?.value;
|
|
653
|
+
const originalValue = options[originalIndex]?.value ?? null;
|
|
640
654
|
onValueChangeEvent(originalValue, originalIndex);
|
|
641
655
|
closeWebMenu();
|
|
642
656
|
}}
|
package/src/SelectBadge.tsx
CHANGED
|
@@ -3,7 +3,7 @@ import type React from "react";
|
|
|
3
3
|
import {useCallback, useMemo, useState} from "react";
|
|
4
4
|
import {Modal, Platform, Text, TouchableOpacity, View} from "react-native";
|
|
5
5
|
|
|
6
|
-
import type {FieldOption, SelectBadgeProps, SurfaceTheme, TextTheme} from "./Common";
|
|
6
|
+
import type {FieldOption, IconColor, SelectBadgeProps, SurfaceTheme, TextTheme} from "./Common";
|
|
7
7
|
import {Icon} from "./Icon";
|
|
8
8
|
import {useTheme} from "./Theme";
|
|
9
9
|
import {useWebDropdownAnchor, WebDropdownMenu, type WebDropdownMenuOption} from "./WebDropdownMenu";
|
|
@@ -98,7 +98,7 @@ export const SelectBadge = ({
|
|
|
98
98
|
);
|
|
99
99
|
|
|
100
100
|
const renderPickerItems = useCallback(() => {
|
|
101
|
-
return options?.map((item:
|
|
101
|
+
return options?.map((item: FieldOption) => (
|
|
102
102
|
<Picker.Item key={item.key || item.label} label={item.label} value={item.value} />
|
|
103
103
|
));
|
|
104
104
|
}, [options]);
|
|
@@ -321,7 +321,11 @@ export const SelectBadge = ({
|
|
|
321
321
|
}}
|
|
322
322
|
>
|
|
323
323
|
<Icon
|
|
324
|
-
|
|
324
|
+
// noExplicitAny: textColor is a resolved hex string from the theme, but Icon's
|
|
325
|
+
// color prop expects an IconColor key. Icon falls back to the raw string at runtime
|
|
326
|
+
// (theme.text[color] ?? color), but the type cannot be narrowed without changing
|
|
327
|
+
// Icon's type signature to accept arbitrary strings.
|
|
328
|
+
color={textColor as unknown as IconColor}
|
|
325
329
|
iconName={showPicker ? "chevron-up" : "chevron-down"}
|
|
326
330
|
size="sm"
|
|
327
331
|
/>
|
package/src/SelectField.tsx
CHANGED
|
@@ -26,10 +26,10 @@ export const SelectField: FC<SelectFieldProps> = ({
|
|
|
26
26
|
disabled={disabled}
|
|
27
27
|
items={options}
|
|
28
28
|
onValueChange={(v) => {
|
|
29
|
-
if (v === undefined || v === "") {
|
|
29
|
+
if (v === undefined || v === null || v === "") {
|
|
30
30
|
onChange("");
|
|
31
31
|
} else {
|
|
32
|
-
onChange(v);
|
|
32
|
+
onChange(String(v));
|
|
33
33
|
}
|
|
34
34
|
}}
|
|
35
35
|
placeholder={!requireValue ? clearOption : {}}
|
|
@@ -5,7 +5,7 @@ import {Navigator, Slot} from "expo-router";
|
|
|
5
5
|
// update the import path here — this is the only place in the codebase that references it.
|
|
6
6
|
// eslint-disable-next-line import/no-internal-modules
|
|
7
7
|
import {Screen} from "expo-router/build/views/Screen";
|
|
8
|
-
import {type FC, useCallback, useEffect, useMemo, useRef, useState} from "react";
|
|
8
|
+
import {type FC, type ReactNode, useCallback, useEffect, useMemo, useRef, useState} from "react";
|
|
9
9
|
import {
|
|
10
10
|
Animated,
|
|
11
11
|
Dimensions,
|
|
@@ -361,7 +361,11 @@ const SidebarHeader: FC<{onOpen: () => void}> = ({onOpen}) => {
|
|
|
361
361
|
const insets = useSafeAreaInsets();
|
|
362
362
|
const {state, descriptors} = Navigator.useContext();
|
|
363
363
|
const activeRoute = state.routes[state.index];
|
|
364
|
-
const {headerLeft, headerRight, title} = (descriptors[activeRoute?.key]?.options ?? {}) as
|
|
364
|
+
const {headerLeft, headerRight, title} = (descriptors[activeRoute?.key]?.options ?? {}) as {
|
|
365
|
+
headerLeft?: (props: object) => ReactNode;
|
|
366
|
+
headerRight?: (props: object) => ReactNode;
|
|
367
|
+
title?: string;
|
|
368
|
+
};
|
|
365
369
|
|
|
366
370
|
return (
|
|
367
371
|
<View
|
package/src/Signature.native.tsx
CHANGED
|
@@ -18,6 +18,10 @@ export const Signature: FC<Props> = ({onChange, onStart, onEnd}: Props) => {
|
|
|
18
18
|
|
|
19
19
|
const handleClear = () => {
|
|
20
20
|
ref.current?.clearSignature();
|
|
21
|
+
// `clearSignature` on the underlying canvas does not fire `onOK`, so the
|
|
22
|
+
// parent never learns the signature is gone. Push an empty value so any
|
|
23
|
+
// "signature required" gating reflects the cleared state immediately.
|
|
24
|
+
onChange("");
|
|
21
25
|
};
|
|
22
26
|
|
|
23
27
|
const onBegin = () => {
|
package/src/Signature.test.tsx
CHANGED
|
@@ -49,6 +49,16 @@ describe("Signature", () => {
|
|
|
49
49
|
expect(clearMock).toHaveBeenCalledTimes(1);
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
+
it("notifies the parent with an empty value when Clear is pressed", () => {
|
|
53
|
+
clearMock.mockClear();
|
|
54
|
+
const mockOnChange = mock(() => {});
|
|
55
|
+
const {getByText} = renderWithTheme(<Signature onChange={mockOnChange} />);
|
|
56
|
+
fireEvent.press(getByText("Clear"));
|
|
57
|
+
// Without this, "signature required" gating in parents would never reset
|
|
58
|
+
// because the underlying canvas clear() does not fire onEnd/onOK.
|
|
59
|
+
expect(mockOnChange).toHaveBeenCalledWith("");
|
|
60
|
+
});
|
|
61
|
+
|
|
52
62
|
it("calls onChange with the data URL when a stroke ends", () => {
|
|
53
63
|
toDataURLReturn = "data:image/png;base64,abc";
|
|
54
64
|
const mockOnChange = mock(() => {});
|
package/src/Signature.tsx
CHANGED
|
@@ -17,6 +17,10 @@ export const Signature = ({onChange}: SignatureProps): ReactElement | null => {
|
|
|
17
17
|
|
|
18
18
|
const onClear = () => {
|
|
19
19
|
ref.current?.clear();
|
|
20
|
+
// `clear()` on the underlying canvas does not fire `onEnd`, so the parent
|
|
21
|
+
// never learns the signature is gone. Push an empty value so any
|
|
22
|
+
// "signature required" gating reflects the cleared state immediately.
|
|
23
|
+
onChange("");
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
const onUpdatedSignature = () => {
|
package/src/Theme.tsx
CHANGED
|
@@ -172,18 +172,21 @@ const computeTheme = (
|
|
|
172
172
|
return acc;
|
|
173
173
|
}
|
|
174
174
|
const value = themeConfig[key as keyof TerrenoThemeConfig] ?? {};
|
|
175
|
-
acc
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
175
|
+
(acc as unknown as Record<string, unknown>)[key] = Object.keys(value).reduce(
|
|
176
|
+
(accKey, valueKey) => {
|
|
177
|
+
const primitiveKey = value[valueKey as keyof typeof value] as keyof ThemePrimitives;
|
|
178
|
+
if (key === "font") {
|
|
179
|
+
accKey[valueKey] = primitiveKey;
|
|
180
|
+
} else {
|
|
181
|
+
if (primitives[primitiveKey] === undefined) {
|
|
182
|
+
console.error(`Primitive ${primitiveKey} not found in theme.`);
|
|
183
|
+
}
|
|
184
|
+
accKey[valueKey] = primitives[primitiveKey];
|
|
182
185
|
}
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
186
|
+
return accKey;
|
|
187
|
+
},
|
|
188
|
+
{} as Record<string, string | number>
|
|
189
|
+
);
|
|
187
190
|
return acc;
|
|
188
191
|
}, {} as TerrenoTheme);
|
|
189
192
|
return {...theme, primitives};
|
|
@@ -199,7 +202,7 @@ export const ThemeContext = createContext({
|
|
|
199
202
|
});
|
|
200
203
|
|
|
201
204
|
interface ThemeProviderProps {
|
|
202
|
-
children:
|
|
205
|
+
children: React.ReactNode;
|
|
203
206
|
}
|
|
204
207
|
|
|
205
208
|
export const ThemeProvider = ({children}: ThemeProviderProps) => {
|
|
@@ -225,12 +228,12 @@ export const ThemeProvider = ({children}: ThemeProviderProps) => {
|
|
|
225
228
|
const prevSubTheme = prev[key as keyof TerrenoThemeConfig];
|
|
226
229
|
|
|
227
230
|
if (newSubTheme && typeof newSubTheme === "object") {
|
|
228
|
-
(mergedTheme as
|
|
231
|
+
(mergedTheme as Record<string, unknown>)[key] = {
|
|
229
232
|
...prevSubTheme,
|
|
230
233
|
...newSubTheme,
|
|
231
234
|
};
|
|
232
235
|
} else {
|
|
233
|
-
mergedTheme
|
|
236
|
+
(mergedTheme as Record<string, unknown>)[key] = newSubTheme;
|
|
234
237
|
}
|
|
235
238
|
}
|
|
236
239
|
}
|
package/src/Tooltip.test.tsx
CHANGED
|
@@ -6,6 +6,20 @@ import {Text} from "./Text";
|
|
|
6
6
|
import {Tooltip} from "./Tooltip";
|
|
7
7
|
import {renderWithTheme} from "./test-utils";
|
|
8
8
|
|
|
9
|
+
// Minimal shape of the tree returned by toJSON() that we rely on here.
|
|
10
|
+
interface TestNode {
|
|
11
|
+
type: string;
|
|
12
|
+
props: {
|
|
13
|
+
onPointerEnter?: () => void;
|
|
14
|
+
onPointerLeave?: () => void;
|
|
15
|
+
onTouchStart?: (event?: {nativeEvent: object}) => void;
|
|
16
|
+
onLayout?: (event: {
|
|
17
|
+
nativeEvent: {layout: {height: number; width: number; x: number; y: number}};
|
|
18
|
+
}) => void;
|
|
19
|
+
};
|
|
20
|
+
children: null | Array<TestNode | string>;
|
|
21
|
+
}
|
|
22
|
+
|
|
9
23
|
// Mock react-native-portalize so Portal renders inline in tests
|
|
10
24
|
mock.module("react-native-portalize", () => ({
|
|
11
25
|
Host: ({children}: {children: React.ReactNode}) => <View testID="portal-host">{children}</View>,
|
|
@@ -13,10 +27,10 @@ mock.module("react-native-portalize", () => ({
|
|
|
13
27
|
}));
|
|
14
28
|
|
|
15
29
|
beforeAll(() => {
|
|
16
|
-
|
|
30
|
+
globalThis.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
17
31
|
return setTimeout(() => callback(Date.now()), 0) as unknown as number;
|
|
18
32
|
};
|
|
19
|
-
|
|
33
|
+
globalThis.cancelAnimationFrame = (id: number) => {
|
|
20
34
|
clearTimeout(id);
|
|
21
35
|
};
|
|
22
36
|
});
|
|
@@ -111,14 +125,14 @@ describe("Tooltip", () => {
|
|
|
111
125
|
</Tooltip>
|
|
112
126
|
);
|
|
113
127
|
|
|
114
|
-
const wrapper = toJSON()
|
|
128
|
+
const wrapper = toJSON();
|
|
115
129
|
expect(wrapper).toBeTruthy();
|
|
116
130
|
expect(queryByTestId("tooltip-container")).toBeNull();
|
|
117
131
|
|
|
118
|
-
const tree = toJSON();
|
|
132
|
+
const tree = toJSON() as TestNode | null;
|
|
119
133
|
await act(async () => {
|
|
120
134
|
// Trigger pointer enter on the wrapper
|
|
121
|
-
const root =
|
|
135
|
+
const root = tree?.children?.[0] as TestNode | undefined;
|
|
122
136
|
if (root?.props?.onPointerEnter) {
|
|
123
137
|
root.props.onPointerEnter();
|
|
124
138
|
}
|
|
@@ -138,8 +152,8 @@ describe("Tooltip", () => {
|
|
|
138
152
|
</Tooltip>
|
|
139
153
|
);
|
|
140
154
|
|
|
141
|
-
const tree = toJSON() as
|
|
142
|
-
const root = tree.children?.[0];
|
|
155
|
+
const tree = toJSON() as TestNode;
|
|
156
|
+
const root = tree.children?.[0] as TestNode;
|
|
143
157
|
|
|
144
158
|
await act(async () => {
|
|
145
159
|
root.props.onTouchStart?.({nativeEvent: {}});
|
|
@@ -151,8 +165,10 @@ describe("Tooltip", () => {
|
|
|
151
165
|
expect(queryByTestId("tooltip-container")).toBeTruthy();
|
|
152
166
|
|
|
153
167
|
// Second touch should hide
|
|
154
|
-
const treeAfterShow = toJSON() as
|
|
155
|
-
const updatedRoot = treeAfterShow.children
|
|
168
|
+
const treeAfterShow = toJSON() as TestNode;
|
|
169
|
+
const updatedRoot = (treeAfterShow.children as Array<TestNode | string>)[
|
|
170
|
+
(treeAfterShow.children as Array<TestNode | string>).length - 1
|
|
171
|
+
] as TestNode;
|
|
156
172
|
await act(async () => {
|
|
157
173
|
updatedRoot.props.onTouchStart?.({nativeEvent: {}});
|
|
158
174
|
});
|
|
@@ -167,8 +183,8 @@ describe("Tooltip", () => {
|
|
|
167
183
|
</Tooltip>
|
|
168
184
|
);
|
|
169
185
|
|
|
170
|
-
const tree = toJSON() as
|
|
171
|
-
const root = tree.children?.[0];
|
|
186
|
+
const tree = toJSON() as TestNode;
|
|
187
|
+
const root = tree.children?.[0] as TestNode;
|
|
172
188
|
|
|
173
189
|
await act(async () => {
|
|
174
190
|
root.props.onPointerEnter?.();
|
|
@@ -179,8 +195,10 @@ describe("Tooltip", () => {
|
|
|
179
195
|
|
|
180
196
|
expect(queryByTestId("tooltip-container")).toBeTruthy();
|
|
181
197
|
|
|
182
|
-
const treeAfter = toJSON() as
|
|
183
|
-
const wrapper = treeAfter.children
|
|
198
|
+
const treeAfter = toJSON() as TestNode;
|
|
199
|
+
const wrapper = (treeAfter.children as Array<TestNode | string>)[
|
|
200
|
+
(treeAfter.children as Array<TestNode | string>).length - 1
|
|
201
|
+
] as TestNode;
|
|
184
202
|
await act(async () => {
|
|
185
203
|
wrapper.props.onPointerLeave?.();
|
|
186
204
|
});
|
|
@@ -206,8 +224,8 @@ describe("Tooltip", () => {
|
|
|
206
224
|
</Tooltip>
|
|
207
225
|
);
|
|
208
226
|
|
|
209
|
-
const tree = toJSON() as
|
|
210
|
-
const root = tree.children?.[0];
|
|
227
|
+
const tree = toJSON() as TestNode;
|
|
228
|
+
const root = tree.children?.[0] as TestNode;
|
|
211
229
|
|
|
212
230
|
await act(async () => {
|
|
213
231
|
root.props.onPointerEnter?.();
|
|
@@ -227,8 +245,8 @@ describe("Tooltip", () => {
|
|
|
227
245
|
</Tooltip>
|
|
228
246
|
);
|
|
229
247
|
|
|
230
|
-
const tree = toJSON() as
|
|
231
|
-
const root = tree.children?.[0];
|
|
248
|
+
const tree = toJSON() as TestNode;
|
|
249
|
+
const root = tree.children?.[0] as TestNode;
|
|
232
250
|
|
|
233
251
|
// Show the tooltip
|
|
234
252
|
await act(async () => {
|
|
@@ -241,11 +259,12 @@ describe("Tooltip", () => {
|
|
|
241
259
|
|
|
242
260
|
// Find any views with onLayout to simulate layout event
|
|
243
261
|
const {View: ViewComp} = await import("react-native");
|
|
244
|
-
const allViews = UNSAFE_getAllByType(ViewComp
|
|
262
|
+
const allViews = UNSAFE_getAllByType(ViewComp);
|
|
245
263
|
for (const v of allViews) {
|
|
246
|
-
|
|
264
|
+
const props = v.props as TestNode["props"];
|
|
265
|
+
if (props.onLayout) {
|
|
247
266
|
await act(async () => {
|
|
248
|
-
|
|
267
|
+
props.onLayout?.({
|
|
249
268
|
nativeEvent: {
|
|
250
269
|
layout: {height: 100, width: 200, x: 0, y: 0},
|
|
251
270
|
},
|
|
@@ -280,8 +299,8 @@ describe("Tooltip", () => {
|
|
|
280
299
|
</Tooltip>
|
|
281
300
|
);
|
|
282
301
|
|
|
283
|
-
const tree = toJSON() as
|
|
284
|
-
const root = tree.children?.[0];
|
|
302
|
+
const tree = toJSON() as TestNode;
|
|
303
|
+
const root = tree.children?.[0] as TestNode;
|
|
285
304
|
await act(async () => {
|
|
286
305
|
root.props.onPointerEnter?.();
|
|
287
306
|
});
|
|
@@ -57,7 +57,6 @@ exports[`CustomSelectField renders correctly with default props 1`] = `
|
|
|
57
57
|
},
|
|
58
58
|
],
|
|
59
59
|
"props": {
|
|
60
|
-
"activeOpacity": 1,
|
|
61
60
|
"onPress": [Function],
|
|
62
61
|
"style": {
|
|
63
62
|
"alignItems": "center",
|
|
@@ -385,7 +384,6 @@ exports[`CustomSelectField renders with title 1`] = `
|
|
|
385
384
|
},
|
|
386
385
|
],
|
|
387
386
|
"props": {
|
|
388
|
-
"activeOpacity": 1,
|
|
389
387
|
"onPress": [Function],
|
|
390
388
|
"style": {
|
|
391
389
|
"alignItems": "center",
|
|
@@ -698,7 +696,6 @@ exports[`CustomSelectField renders with placeholder 1`] = `
|
|
|
698
696
|
},
|
|
699
697
|
],
|
|
700
698
|
"props": {
|
|
701
|
-
"activeOpacity": 1,
|
|
702
699
|
"onPress": [Function],
|
|
703
700
|
"style": {
|
|
704
701
|
"alignItems": "center",
|
|
@@ -1011,7 +1008,6 @@ exports[`CustomSelectField renders with selected value 1`] = `
|
|
|
1011
1008
|
},
|
|
1012
1009
|
],
|
|
1013
1010
|
"props": {
|
|
1014
|
-
"activeOpacity": 1,
|
|
1015
1011
|
"onPress": [Function],
|
|
1016
1012
|
"style": {
|
|
1017
1013
|
"alignItems": "center",
|
|
@@ -1324,7 +1320,6 @@ exports[`CustomSelectField renders with custom value (not in options) 1`] = `
|
|
|
1324
1320
|
},
|
|
1325
1321
|
],
|
|
1326
1322
|
"props": {
|
|
1327
|
-
"activeOpacity": 1,
|
|
1328
1323
|
"onPress": [Function],
|
|
1329
1324
|
"style": {
|
|
1330
1325
|
"alignItems": "center",
|
|
@@ -1741,7 +1736,6 @@ exports[`CustomSelectField renders disabled state 1`] = `
|
|
|
1741
1736
|
},
|
|
1742
1737
|
],
|
|
1743
1738
|
"props": {
|
|
1744
|
-
"activeOpacity": 1,
|
|
1745
1739
|
"onPress": [Function],
|
|
1746
1740
|
"style": {
|
|
1747
1741
|
"alignItems": "center",
|
|
@@ -2056,7 +2050,6 @@ exports[`CustomSelectField includes custom option in dropdown 1`] = `
|
|
|
2056
2050
|
},
|
|
2057
2051
|
],
|
|
2058
2052
|
"props": {
|
|
2059
|
-
"activeOpacity": 1,
|
|
2060
2053
|
"onPress": [Function],
|
|
2061
2054
|
"style": {
|
|
2062
2055
|
"alignItems": "center",
|
|
@@ -1224,7 +1224,6 @@ exports[`Field renders time field 1`] = `
|
|
|
1224
1224
|
},
|
|
1225
1225
|
],
|
|
1226
1226
|
"props": {
|
|
1227
|
-
"activeOpacity": 1,
|
|
1228
1227
|
"onPress": [Function],
|
|
1229
1228
|
"style": {
|
|
1230
1229
|
"alignItems": "center",
|
|
@@ -1494,7 +1493,6 @@ exports[`Field renders time field 1`] = `
|
|
|
1494
1493
|
},
|
|
1495
1494
|
],
|
|
1496
1495
|
"props": {
|
|
1497
|
-
"activeOpacity": 1,
|
|
1498
1496
|
"onPress": [Function],
|
|
1499
1497
|
"style": {
|
|
1500
1498
|
"alignItems": "center",
|
|
@@ -2219,7 +2217,6 @@ exports[`Field renders datetime field 1`] = `
|
|
|
2219
2217
|
},
|
|
2220
2218
|
],
|
|
2221
2219
|
"props": {
|
|
2222
|
-
"activeOpacity": 1,
|
|
2223
2220
|
"onPress": [Function],
|
|
2224
2221
|
"style": {
|
|
2225
2222
|
"alignItems": "center",
|
|
@@ -2489,7 +2486,6 @@ exports[`Field renders datetime field 1`] = `
|
|
|
2489
2486
|
},
|
|
2490
2487
|
],
|
|
2491
2488
|
"props": {
|
|
2492
|
-
"activeOpacity": 1,
|
|
2493
2489
|
"onPress": [Function],
|
|
2494
2490
|
"style": {
|
|
2495
2491
|
"alignItems": "center",
|
|
@@ -2860,7 +2856,6 @@ exports[`Field renders select field 1`] = `
|
|
|
2860
2856
|
},
|
|
2861
2857
|
],
|
|
2862
2858
|
"props": {
|
|
2863
|
-
"activeOpacity": 1,
|
|
2864
2859
|
"onPress": [Function],
|
|
2865
2860
|
"style": {
|
|
2866
2861
|
"alignItems": "center",
|
|
@@ -3737,7 +3732,6 @@ exports[`Field renders address field 1`] = `
|
|
|
3737
3732
|
},
|
|
3738
3733
|
],
|
|
3739
3734
|
"props": {
|
|
3740
|
-
"activeOpacity": 1,
|
|
3741
3735
|
"onPress": [Function],
|
|
3742
3736
|
"style": {
|
|
3743
3737
|
"alignItems": "center",
|
|
@@ -48,7 +48,6 @@ exports[`PickerSelect renders correctly with default props 1`] = `
|
|
|
48
48
|
},
|
|
49
49
|
],
|
|
50
50
|
"props": {
|
|
51
|
-
"activeOpacity": 1,
|
|
52
51
|
"onPress": [Function],
|
|
53
52
|
"style": {
|
|
54
53
|
"alignItems": "center",
|
|
@@ -313,7 +312,6 @@ exports[`PickerSelect renders with selected value 1`] = `
|
|
|
313
312
|
},
|
|
314
313
|
],
|
|
315
314
|
"props": {
|
|
316
|
-
"activeOpacity": 1,
|
|
317
315
|
"onPress": [Function],
|
|
318
316
|
"style": {
|
|
319
317
|
"alignItems": "center",
|
|
@@ -578,7 +576,6 @@ exports[`PickerSelect renders disabled state 1`] = `
|
|
|
578
576
|
},
|
|
579
577
|
],
|
|
580
578
|
"props": {
|
|
581
|
-
"activeOpacity": 1,
|
|
582
579
|
"onPress": [Function],
|
|
583
580
|
"style": {
|
|
584
581
|
"alignItems": "center",
|
|
@@ -845,7 +842,6 @@ exports[`PickerSelect renders without placeholder when placeholder is empty obje
|
|
|
845
842
|
},
|
|
846
843
|
],
|
|
847
844
|
"props": {
|
|
848
|
-
"activeOpacity": 1,
|
|
849
845
|
"onPress": [Function],
|
|
850
846
|
"style": {
|
|
851
847
|
"alignItems": "center",
|
|
@@ -1100,7 +1096,6 @@ exports[`PickerSelect matches items by itemKey 1`] = `
|
|
|
1100
1096
|
},
|
|
1101
1097
|
],
|
|
1102
1098
|
"props": {
|
|
1103
|
-
"activeOpacity": 1,
|
|
1104
1099
|
"onPress": [Function],
|
|
1105
1100
|
"style": {
|
|
1106
1101
|
"alignItems": "center",
|
|
@@ -1355,7 +1350,6 @@ exports[`PickerSelect renders custom InputAccessoryView 1`] = `
|
|
|
1355
1350
|
},
|
|
1356
1351
|
],
|
|
1357
1352
|
"props": {
|
|
1358
|
-
"activeOpacity": 1,
|
|
1359
1353
|
"onPress": [Function],
|
|
1360
1354
|
"style": {
|
|
1361
1355
|
"alignItems": "center",
|
|
@@ -1541,7 +1535,6 @@ exports[`PickerSelect passes textInputProps to TextInput 1`] = `
|
|
|
1541
1535
|
},
|
|
1542
1536
|
],
|
|
1543
1537
|
"props": {
|
|
1544
|
-
"activeOpacity": 1,
|
|
1545
1538
|
"onPress": [Function],
|
|
1546
1539
|
"style": {
|
|
1547
1540
|
"alignItems": "center",
|