@terreno/ui 0.11.0 → 0.11.2
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/Banner.js +5 -3
- package/dist/Banner.js.map +1 -1
- package/dist/Common.d.ts +15 -13
- package/dist/Common.js.map +1 -1
- package/package.json +1 -1
- package/src/Banner.test.tsx +43 -0
- package/src/Banner.tsx +5 -3
- package/src/Common.ts +37 -14
- package/src/Modal.test.tsx +46 -1
- package/src/useConsentForms.test.ts +41 -19
- package/src/useSubmitConsent.test.ts +16 -5
package/dist/Banner.js
CHANGED
|
@@ -58,9 +58,9 @@ export const Banner = (props) => {
|
|
|
58
58
|
bgColor = "warning";
|
|
59
59
|
}
|
|
60
60
|
const [show, setShow] = useState(true);
|
|
61
|
-
// Load seen from async storage.
|
|
61
|
+
// Load seen from async storage (only when id is provided).
|
|
62
62
|
useEffect(() => {
|
|
63
|
-
if (dismissible) {
|
|
63
|
+
if (dismissible && id) {
|
|
64
64
|
void Unifier.storage.getItem(getKey(id)).then((isSeen) => {
|
|
65
65
|
console.debug(`[banner] ${getKey(id)} seen? ${isSeen}`);
|
|
66
66
|
setShow(!isSeen);
|
|
@@ -71,7 +71,9 @@ export const Banner = (props) => {
|
|
|
71
71
|
if (!dismissible) {
|
|
72
72
|
return;
|
|
73
73
|
}
|
|
74
|
-
|
|
74
|
+
if (id) {
|
|
75
|
+
await hideBanner(id);
|
|
76
|
+
}
|
|
75
77
|
setShow(false);
|
|
76
78
|
};
|
|
77
79
|
if (!show) {
|
package/dist/Banner.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Banner.js","sourceRoot":"","sources":["../src/Banner.tsx"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"Banner.js","sourceRoot":"","sources":["../src/Banner.tsx"],"names":[],"mappings":"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"}
|
package/dist/Common.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CountryCode } from "libphonenumber-js";
|
|
2
2
|
import type React from "react";
|
|
3
3
|
import type { ReactElement, ReactNode } from "react";
|
|
4
|
-
import type { ListRenderItemInfo, StyleProp, TextStyle, ViewStyle } from "react-native";
|
|
4
|
+
import type { ImageStyle, ListRenderItemInfo, StyleProp, TextInput, TextStyle, ViewStyle } from "react-native";
|
|
5
5
|
import type { DimensionValue } from "react-native/Libraries/StyleSheet/StyleSheetTypes";
|
|
6
6
|
import type { Styles } from "react-native-google-places-autocomplete";
|
|
7
7
|
import type { SvgProps } from "react-native-svg";
|
|
@@ -418,7 +418,7 @@ export interface BoxPropsBase {
|
|
|
418
418
|
zIndex?: number | "auto";
|
|
419
419
|
onClick?: () => void | Promise<void>;
|
|
420
420
|
className?: string;
|
|
421
|
-
style?:
|
|
421
|
+
style?: StyleProp<ViewStyle>;
|
|
422
422
|
onHoverStart?: () => void | Promise<void>;
|
|
423
423
|
onHoverEnd?: () => void | Promise<void>;
|
|
424
424
|
scroll?: boolean;
|
|
@@ -442,7 +442,7 @@ export type BoxProps = (BoxPropsBase & {
|
|
|
442
442
|
} & AccessibilityProps);
|
|
443
443
|
export type BoxColor = SurfaceColor | "transparent";
|
|
444
444
|
export interface ErrorBoundaryProps {
|
|
445
|
-
onError?: (error: Error, stack:
|
|
445
|
+
onError?: (error: Error, stack: string) => void;
|
|
446
446
|
children?: ReactNode;
|
|
447
447
|
}
|
|
448
448
|
export interface IconProps {
|
|
@@ -515,7 +515,7 @@ export interface TextFieldProps extends BaseFieldProps, HelperTextProps, ErrorTe
|
|
|
515
515
|
grow?: boolean;
|
|
516
516
|
multiline?: boolean;
|
|
517
517
|
rows?: number;
|
|
518
|
-
inputRef?:
|
|
518
|
+
inputRef?: (ref: TextInput | null) => void;
|
|
519
519
|
trimOnBlur?: boolean;
|
|
520
520
|
aiSuggestion?: AiSuggestionProps;
|
|
521
521
|
}
|
|
@@ -605,7 +605,7 @@ export interface ImageProps {
|
|
|
605
605
|
size?: string;
|
|
606
606
|
srcSet?: string;
|
|
607
607
|
fullWidth?: boolean;
|
|
608
|
-
style?:
|
|
608
|
+
style?: ImageStyle;
|
|
609
609
|
}
|
|
610
610
|
export interface BackButtonInterface {
|
|
611
611
|
onBack: () => void;
|
|
@@ -666,7 +666,7 @@ export interface SplitPageProps {
|
|
|
666
666
|
renderListViewHeader?: () => ReactElement | null;
|
|
667
667
|
renderContent?: (index?: number) => ReactElement | ReactElement[] | null;
|
|
668
668
|
listViewData: any[];
|
|
669
|
-
listViewExtraData?:
|
|
669
|
+
listViewExtraData?: unknown;
|
|
670
670
|
listViewWidth?: number;
|
|
671
671
|
listViewMaxWidth?: number;
|
|
672
672
|
renderChild?: () => ReactChild;
|
|
@@ -686,7 +686,7 @@ export interface AddressInterface {
|
|
|
686
686
|
export interface TransformValueOptions {
|
|
687
687
|
func?: (value: string) => string;
|
|
688
688
|
options?: {
|
|
689
|
-
[key: string]:
|
|
689
|
+
[key: string]: unknown;
|
|
690
690
|
};
|
|
691
691
|
}
|
|
692
692
|
export type ReactChild = ReactNode;
|
|
@@ -1183,8 +1183,10 @@ export interface BannerButtonProps {
|
|
|
1183
1183
|
export interface BannerPropsBase {
|
|
1184
1184
|
/**
|
|
1185
1185
|
* Used to identify if banner has been dismissed by the user.
|
|
1186
|
+
* When provided, dismissal state is persisted to AsyncStorage.
|
|
1187
|
+
* When omitted, dismissal is ephemeral (resets on remount).
|
|
1186
1188
|
*/
|
|
1187
|
-
id
|
|
1189
|
+
id?: string;
|
|
1188
1190
|
/**
|
|
1189
1191
|
* The text to display in the main body of the banner.
|
|
1190
1192
|
*/
|
|
@@ -1622,11 +1624,11 @@ export interface PageProps {
|
|
|
1622
1624
|
color?: SurfaceColor;
|
|
1623
1625
|
maxWidth?: number | string;
|
|
1624
1626
|
keyboardOffset?: number;
|
|
1625
|
-
footer?:
|
|
1627
|
+
footer?: ReactNode;
|
|
1626
1628
|
rightButton?: string;
|
|
1627
1629
|
rightButtonOnClick?: () => void;
|
|
1628
|
-
children?:
|
|
1629
|
-
onError?: (error: Error, stack:
|
|
1630
|
+
children?: ReactChildren;
|
|
1631
|
+
onError?: (error: Error, stack: string) => void;
|
|
1630
1632
|
}
|
|
1631
1633
|
export interface ProgressBarProps {
|
|
1632
1634
|
color: SurfaceColor;
|
|
@@ -1645,7 +1647,7 @@ export interface RadioFieldProps {
|
|
|
1645
1647
|
export interface SignatureFieldProps {
|
|
1646
1648
|
disabled?: boolean;
|
|
1647
1649
|
value?: string;
|
|
1648
|
-
onChange: (value:
|
|
1650
|
+
onChange: (value: string) => void;
|
|
1649
1651
|
title?: string;
|
|
1650
1652
|
onStart?: () => void;
|
|
1651
1653
|
onEnd?: () => void;
|
|
@@ -2025,7 +2027,7 @@ export interface APIError {
|
|
|
2025
2027
|
pointer?: string;
|
|
2026
2028
|
parameter?: string;
|
|
2027
2029
|
meta?: {
|
|
2028
|
-
[id: string]:
|
|
2030
|
+
[id: string]: unknown;
|
|
2029
2031
|
};
|
|
2030
2032
|
};
|
|
2031
2033
|
}
|
package/dist/Common.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"AA4XA,MAAM,CAAC,MAAM,WAAW,GAAG;IACzB,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,CAAC,EAAE,EAAE;IACL,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,EAAE;CACP,CAAC;AAEF,MAAM,UAAU,UAAU,CAAC,OAAqB;IAC9C,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAChB,OAAO,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAmB,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/D,CAAC;IACD,OAAO,WAAW,CAAC,OAAyB,CAAC,CAAC;AAChD,CAAC;AAqBD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAe,EAAE,EAAE;IAClD,OAAO;QACL,KAAK,EAAE,EAAE;QACT,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,EAAE;QACN,EAAE,EAAE,CAAC;KACN,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAClB,CAAC,CAAC;AAsTF,MAAM,YAAY,GAAG;IACnB,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,OAAO,EAAE,CAAC;IACV,IAAI,EAAE,EAAE;IACR,EAAE,EAAE,EAAE;IACN,EAAE,EAAE,CAAC;IACL,OAAO,EAAE,CAAC;IACV,OAAO,EAAE,GAAG;IACZ,EAAE,EAAE,CAAC;IACL,EAAE,EAAE,EAAE;CACP,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,QAAkB;IAC5C,OAAO,YAAY,CAAC,QAAQ,CAAC,CAAC;AAChC,CAAC;AAqoED;;GAEG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;CACV,CAAC"}
|
package/package.json
CHANGED
package/src/Banner.test.tsx
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import {describe, expect, it, mock} from "bun:test";
|
|
2
2
|
import {act, fireEvent, waitFor} from "@testing-library/react-native";
|
|
3
|
+
import React from "react";
|
|
3
4
|
|
|
4
5
|
import {Banner} from "./Banner";
|
|
5
6
|
import {renderWithTheme} from "./test-utils";
|
|
7
|
+
import {Unifier} from "./Unifier";
|
|
6
8
|
|
|
7
9
|
describe("Banner", () => {
|
|
8
10
|
it("renders correctly with default props", () => {
|
|
@@ -125,4 +127,45 @@ describe("Banner", () => {
|
|
|
125
127
|
expect(handleClick).toHaveBeenCalled();
|
|
126
128
|
});
|
|
127
129
|
});
|
|
130
|
+
|
|
131
|
+
// Tests for optional id prop
|
|
132
|
+
it("renders without id prop", () => {
|
|
133
|
+
const {getByText} = renderWithTheme(<Banner text="No id banner" />);
|
|
134
|
+
expect(getByText("No id banner")).toBeTruthy();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("renders without id and without dismissible", () => {
|
|
138
|
+
const {getByText, queryByLabelText} = renderWithTheme(<Banner text="Simple banner no id" />);
|
|
139
|
+
expect(getByText("Simple banner no id")).toBeTruthy();
|
|
140
|
+
expect(queryByLabelText("Dismiss")).toBeNull();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("hides dismissible banner without id when dismissed", async () => {
|
|
144
|
+
const {getByLabelText, queryByText} = renderWithTheme(
|
|
145
|
+
<Banner dismissible text="Ephemeral banner" />
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
await act(async () => {
|
|
149
|
+
fireEvent.press(getByLabelText("Dismiss"));
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await waitFor(() => {
|
|
153
|
+
expect(queryByText("Ephemeral banner")).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it("does not persist dismissal to storage when id is omitted", async () => {
|
|
158
|
+
const setItemMock = Unifier.storage.setItem as ReturnType<typeof mock>;
|
|
159
|
+
setItemMock.mockClear();
|
|
160
|
+
|
|
161
|
+
const {getByLabelText} = renderWithTheme(<Banner dismissible text="No persist banner" />);
|
|
162
|
+
|
|
163
|
+
await act(async () => {
|
|
164
|
+
fireEvent.press(getByLabelText("Dismiss"));
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
await waitFor(() => {
|
|
168
|
+
expect(setItemMock).not.toHaveBeenCalled();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
128
171
|
});
|
package/src/Banner.tsx
CHANGED
|
@@ -112,9 +112,9 @@ export const Banner = (props: BannerProps): React.ReactElement | null => {
|
|
|
112
112
|
|
|
113
113
|
const [show, setShow] = useState(true);
|
|
114
114
|
|
|
115
|
-
// Load seen from async storage.
|
|
115
|
+
// Load seen from async storage (only when id is provided).
|
|
116
116
|
useEffect(() => {
|
|
117
|
-
if (dismissible) {
|
|
117
|
+
if (dismissible && id) {
|
|
118
118
|
void Unifier.storage.getItem(getKey(id)).then((isSeen) => {
|
|
119
119
|
console.debug(`[banner] ${getKey(id)} seen? ${isSeen}`);
|
|
120
120
|
setShow(!isSeen);
|
|
@@ -126,7 +126,9 @@ export const Banner = (props: BannerProps): React.ReactElement | null => {
|
|
|
126
126
|
if (!dismissible) {
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
|
-
|
|
129
|
+
if (id) {
|
|
130
|
+
await hideBanner(id);
|
|
131
|
+
}
|
|
130
132
|
setShow(false);
|
|
131
133
|
};
|
|
132
134
|
|
package/src/Common.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type {CountryCode} from "libphonenumber-js";
|
|
2
2
|
import type React from "react";
|
|
3
3
|
import type {ReactElement, ReactNode} from "react";
|
|
4
|
-
import type {
|
|
4
|
+
import type {
|
|
5
|
+
ImageStyle,
|
|
6
|
+
ListRenderItemInfo,
|
|
7
|
+
StyleProp,
|
|
8
|
+
TextInput,
|
|
9
|
+
TextStyle,
|
|
10
|
+
ViewStyle,
|
|
11
|
+
} from "react-native";
|
|
5
12
|
import type {DimensionValue} from "react-native/Libraries/StyleSheet/StyleSheetTypes";
|
|
6
13
|
import type {Styles} from "react-native-google-places-autocomplete";
|
|
7
14
|
import type {SvgProps} from "react-native-svg";
|
|
@@ -456,6 +463,7 @@ export interface BoxPropsBase {
|
|
|
456
463
|
lgColumn?: UnsignedUpTo12;
|
|
457
464
|
dangerouslySetInlineStyle?: {
|
|
458
465
|
__style: {
|
|
466
|
+
// noExplicitAny: escape hatch for arbitrary inline style values
|
|
459
467
|
[key: string]: any;
|
|
460
468
|
};
|
|
461
469
|
};
|
|
@@ -528,7 +536,7 @@ export interface BoxPropsBase {
|
|
|
528
536
|
|
|
529
537
|
onClick?: () => void | Promise<void>;
|
|
530
538
|
className?: string;
|
|
531
|
-
style?:
|
|
539
|
+
style?: StyleProp<ViewStyle>;
|
|
532
540
|
onHoverStart?: () => void | Promise<void>;
|
|
533
541
|
onHoverEnd?: () => void | Promise<void>;
|
|
534
542
|
scroll?: boolean;
|
|
@@ -554,7 +562,7 @@ export type BoxProps =
|
|
|
554
562
|
export type BoxColor = SurfaceColor | "transparent";
|
|
555
563
|
|
|
556
564
|
export interface ErrorBoundaryProps {
|
|
557
|
-
onError?: (error: Error, stack:
|
|
565
|
+
onError?: (error: Error, stack: string) => void;
|
|
558
566
|
children?: ReactNode;
|
|
559
567
|
}
|
|
560
568
|
|
|
@@ -652,7 +660,7 @@ export interface TextFieldProps extends BaseFieldProps, HelperTextProps, ErrorTe
|
|
|
652
660
|
multiline?: boolean;
|
|
653
661
|
rows?: number;
|
|
654
662
|
|
|
655
|
-
inputRef?:
|
|
663
|
+
inputRef?: (ref: TextInput | null) => void;
|
|
656
664
|
trimOnBlur?: boolean;
|
|
657
665
|
|
|
658
666
|
aiSuggestion?: AiSuggestionProps;
|
|
@@ -782,7 +790,7 @@ export interface ImageProps {
|
|
|
782
790
|
size?: string;
|
|
783
791
|
srcSet?: string;
|
|
784
792
|
fullWidth?: boolean;
|
|
785
|
-
style?:
|
|
793
|
+
style?: ImageStyle;
|
|
786
794
|
}
|
|
787
795
|
|
|
788
796
|
export interface BackButtonInterface {
|
|
@@ -850,14 +858,17 @@ export interface SplitPageProps {
|
|
|
850
858
|
loading?: boolean;
|
|
851
859
|
color?: SurfaceColor;
|
|
852
860
|
keyboardOffset?: number;
|
|
861
|
+
// noExplicitAny: ListRenderItemInfo generic type depends on the consumer's data shape
|
|
853
862
|
renderListViewItem: (itemInfo: ListRenderItemInfo<any>) => ReactElement | null;
|
|
854
863
|
renderListViewHeader?: () => ReactElement | null;
|
|
855
864
|
renderContent?: (index?: number) => ReactElement | ReactElement[] | null;
|
|
865
|
+
// noExplicitAny: list data type varies by consumer's data model
|
|
856
866
|
listViewData: any[];
|
|
857
|
-
listViewExtraData?:
|
|
867
|
+
listViewExtraData?: unknown;
|
|
858
868
|
listViewWidth?: number;
|
|
859
869
|
listViewMaxWidth?: number;
|
|
860
870
|
renderChild?: () => ReactChild;
|
|
871
|
+
// noExplicitAny: callback value type varies by consumer's data model
|
|
861
872
|
onSelectionChange?: (value?: any) => void | Promise<void>;
|
|
862
873
|
}
|
|
863
874
|
|
|
@@ -896,7 +907,7 @@ export interface AddressInterface {
|
|
|
896
907
|
export interface TransformValueOptions {
|
|
897
908
|
func?: (value: string) => string;
|
|
898
909
|
options?: {
|
|
899
|
-
[key: string]:
|
|
910
|
+
[key: string]: unknown;
|
|
900
911
|
};
|
|
901
912
|
}
|
|
902
913
|
|
|
@@ -1447,8 +1458,10 @@ export interface BannerButtonProps {
|
|
|
1447
1458
|
export interface BannerPropsBase {
|
|
1448
1459
|
/**
|
|
1449
1460
|
* Used to identify if banner has been dismissed by the user.
|
|
1461
|
+
* When provided, dismissal state is persisted to AsyncStorage.
|
|
1462
|
+
* When omitted, dismissal is ephemeral (resets on remount).
|
|
1450
1463
|
*/
|
|
1451
|
-
id
|
|
1464
|
+
id?: string;
|
|
1452
1465
|
/**
|
|
1453
1466
|
* The text to display in the main body of the banner.
|
|
1454
1467
|
*/
|
|
@@ -1743,6 +1756,7 @@ export interface HeightActionSheetProps {
|
|
|
1743
1756
|
|
|
1744
1757
|
export interface HyperlinkProps {
|
|
1745
1758
|
linkDefault?: boolean;
|
|
1759
|
+
// noExplicitAny: type comes from external linkify-it library which lacks an exported type
|
|
1746
1760
|
linkify?: any;
|
|
1747
1761
|
linkStyle?: StyleProp<any>;
|
|
1748
1762
|
linkText?: string | ((url: string) => string);
|
|
@@ -1899,10 +1913,12 @@ export interface ModalProps {
|
|
|
1899
1913
|
/**
|
|
1900
1914
|
* The function to call when the primary button is clicked.
|
|
1901
1915
|
*/
|
|
1916
|
+
// noExplicitAny: callback value type varies by consumer context
|
|
1902
1917
|
primaryButtonOnClick?: (value?: any) => void | Promise<void>;
|
|
1903
1918
|
/**
|
|
1904
1919
|
* The function to call when the secondary button is clicked.
|
|
1905
1920
|
*/
|
|
1921
|
+
// noExplicitAny: callback value type varies by consumer context
|
|
1906
1922
|
secondaryButtonOnClick?: (value?: any) => void | Promise<void>;
|
|
1907
1923
|
}
|
|
1908
1924
|
|
|
@@ -1915,6 +1931,7 @@ export interface NumberPickerActionSheetProps {
|
|
|
1915
1931
|
}
|
|
1916
1932
|
|
|
1917
1933
|
export interface PageProps {
|
|
1934
|
+
// noExplicitAny: React Navigation type varies by navigation stack configuration
|
|
1918
1935
|
navigation?: any;
|
|
1919
1936
|
scroll?: boolean;
|
|
1920
1937
|
loading?: boolean;
|
|
@@ -1928,11 +1945,11 @@ export interface PageProps {
|
|
|
1928
1945
|
color?: SurfaceColor;
|
|
1929
1946
|
maxWidth?: number | string;
|
|
1930
1947
|
keyboardOffset?: number;
|
|
1931
|
-
footer?:
|
|
1948
|
+
footer?: ReactNode;
|
|
1932
1949
|
rightButton?: string;
|
|
1933
1950
|
rightButtonOnClick?: () => void;
|
|
1934
|
-
children?:
|
|
1935
|
-
onError?: (error: Error, stack:
|
|
1951
|
+
children?: ReactChildren;
|
|
1952
|
+
onError?: (error: Error, stack: string) => void;
|
|
1936
1953
|
}
|
|
1937
1954
|
|
|
1938
1955
|
export interface ProgressBarProps {
|
|
@@ -1955,7 +1972,7 @@ export interface RadioFieldProps {
|
|
|
1955
1972
|
export interface SignatureFieldProps {
|
|
1956
1973
|
disabled?: boolean; // default "default"
|
|
1957
1974
|
value?: string;
|
|
1958
|
-
onChange: (value:
|
|
1975
|
+
onChange: (value: string) => void;
|
|
1959
1976
|
title?: string; // default "Signature"
|
|
1960
1977
|
onStart?: () => void;
|
|
1961
1978
|
onEnd?: () => void;
|
|
@@ -2354,16 +2371,19 @@ export type TapToEditProps =
|
|
|
2354
2371
|
|
|
2355
2372
|
export interface BaseTapToEditProps extends Omit<FieldProps, "onChange" | "value"> {
|
|
2356
2373
|
title: string;
|
|
2374
|
+
// noExplicitAny: value type varies across TapToEdit field types (text, number, date, etc.)
|
|
2357
2375
|
value: any;
|
|
2358
2376
|
|
|
2359
2377
|
/**
|
|
2360
2378
|
* Not required if not editable.
|
|
2361
2379
|
*/
|
|
2380
|
+
// noExplicitAny: value type varies across TapToEdit field types
|
|
2362
2381
|
setValue?: (value: any) => void;
|
|
2363
2382
|
|
|
2364
2383
|
/**
|
|
2365
2384
|
* Not required if not editable.
|
|
2366
2385
|
*/
|
|
2386
|
+
// noExplicitAny: value type varies across TapToEdit field types
|
|
2367
2387
|
onSave?: (value: any) => void | Promise<void>;
|
|
2368
2388
|
|
|
2369
2389
|
/**
|
|
@@ -2376,6 +2396,7 @@ export interface BaseTapToEditProps extends Omit<FieldProps, "onChange" | "value
|
|
|
2376
2396
|
* Enable edit mode from outside the component.
|
|
2377
2397
|
*/
|
|
2378
2398
|
isEditing?: boolean;
|
|
2399
|
+
// noExplicitAny: input value type varies across TapToEdit field types
|
|
2379
2400
|
transform?: (value: any) => string;
|
|
2380
2401
|
/**
|
|
2381
2402
|
* Show a confirmation modal before saving the value.
|
|
@@ -2431,7 +2452,7 @@ export interface APIError {
|
|
|
2431
2452
|
source?: string;
|
|
2432
2453
|
pointer?: string;
|
|
2433
2454
|
parameter?: string;
|
|
2434
|
-
meta?: {[id: string]:
|
|
2455
|
+
meta?: {[id: string]: unknown};
|
|
2435
2456
|
};
|
|
2436
2457
|
}
|
|
2437
2458
|
|
|
@@ -2464,6 +2485,7 @@ export interface ModelFields {
|
|
|
2464
2485
|
|
|
2465
2486
|
export interface OpenAPISpec {
|
|
2466
2487
|
paths: {
|
|
2488
|
+
// noExplicitAny: OpenAPI path items are deeply accessed with chained property lookups
|
|
2467
2489
|
[key: string]: any;
|
|
2468
2490
|
};
|
|
2469
2491
|
}
|
|
@@ -2496,7 +2518,8 @@ export interface ModelAdminFieldConfig {
|
|
|
2496
2518
|
|
|
2497
2519
|
// The props for a custom column component for ModelAdmin.
|
|
2498
2520
|
export interface ModelAdminCustomComponentProps extends Omit<FieldProps, "name"> {
|
|
2499
|
-
|
|
2521
|
+
// noExplicitAny: document shape varies by model used with ModelAdmin
|
|
2522
|
+
doc: any;
|
|
2500
2523
|
fieldKey: string; // Dot notation representation of the field.
|
|
2501
2524
|
// user: User;
|
|
2502
2525
|
editing: boolean; // Allow for inline editing of the field.
|
package/src/Modal.test.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {describe, expect, it, mock} from "bun:test";
|
|
1
|
+
import {afterEach, describe, expect, it, mock} from "bun:test";
|
|
2
2
|
import {fireEvent} from "@testing-library/react-native";
|
|
3
3
|
|
|
4
|
+
import {isMobileDevice} from "./MediaQuery";
|
|
4
5
|
import {Modal} from "./Modal";
|
|
5
6
|
import {Text} from "./Text";
|
|
6
7
|
import {renderWithTheme} from "./test-utils";
|
|
@@ -355,3 +356,47 @@ describe("Modal", () => {
|
|
|
355
356
|
expect(stopPropagation).not.toHaveBeenCalled();
|
|
356
357
|
});
|
|
357
358
|
});
|
|
359
|
+
|
|
360
|
+
describe("Modal mobile branch", () => {
|
|
361
|
+
afterEach(() => {
|
|
362
|
+
(isMobileDevice as ReturnType<typeof mock>).mockImplementation(() => false);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it("renders ActionSheet when isMobileDevice is true", () => {
|
|
366
|
+
(isMobileDevice as ReturnType<typeof mock>).mockImplementation(() => true);
|
|
367
|
+
const {toJSON} = renderWithTheme(
|
|
368
|
+
<Modal onDismiss={() => {}} title="Mobile Modal" visible>
|
|
369
|
+
<Text>Mobile Content</Text>
|
|
370
|
+
</Modal>
|
|
371
|
+
);
|
|
372
|
+
expect(toJSON()).toBeTruthy();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it("renders ActionSheet with title and buttons on mobile", () => {
|
|
376
|
+
(isMobileDevice as ReturnType<typeof mock>).mockImplementation(() => true);
|
|
377
|
+
const {toJSON} = renderWithTheme(
|
|
378
|
+
<Modal
|
|
379
|
+
onDismiss={() => {}}
|
|
380
|
+
primaryButtonOnClick={() => {}}
|
|
381
|
+
primaryButtonText="Save"
|
|
382
|
+
secondaryButtonOnClick={() => {}}
|
|
383
|
+
secondaryButtonText="Cancel"
|
|
384
|
+
title="Mobile Actions"
|
|
385
|
+
visible
|
|
386
|
+
>
|
|
387
|
+
<Text>Content</Text>
|
|
388
|
+
</Modal>
|
|
389
|
+
);
|
|
390
|
+
expect(toJSON()).toBeTruthy();
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it("renders ActionSheet with persistOnBackgroundClick enabled", () => {
|
|
394
|
+
(isMobileDevice as ReturnType<typeof mock>).mockImplementation(() => true);
|
|
395
|
+
const {toJSON} = renderWithTheme(
|
|
396
|
+
<Modal onDismiss={() => {}} persistOnBackgroundClick title="Persistent Mobile" visible>
|
|
397
|
+
<Text>Content</Text>
|
|
398
|
+
</Modal>
|
|
399
|
+
);
|
|
400
|
+
expect(toJSON()).toBeTruthy();
|
|
401
|
+
});
|
|
402
|
+
});
|
|
@@ -1,32 +1,53 @@
|
|
|
1
1
|
import {describe, expect, it, mock} from "bun:test";
|
|
2
2
|
import {renderHook} from "@testing-library/react-native";
|
|
3
3
|
|
|
4
|
+
import type {ConsentFormPublic} from "./useConsentForms";
|
|
4
5
|
import {detectLocale, useConsentForms} from "./useConsentForms";
|
|
5
6
|
|
|
7
|
+
type ConsentFormsApi = Parameters<typeof useConsentForms>[0];
|
|
8
|
+
|
|
9
|
+
interface GlobalThisWithNavigator {
|
|
10
|
+
navigator: {language: string} | undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface MockQueryDef {
|
|
14
|
+
onQueryStarted?: (
|
|
15
|
+
_arg: unknown,
|
|
16
|
+
helpers: {queryFulfilled: Promise<{data: ConsentFormPublic[]}> | Promise<never>}
|
|
17
|
+
) => Promise<void>;
|
|
18
|
+
query: () => string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface MockInjectOpts {
|
|
22
|
+
endpoints: (build: {query: (def: MockQueryDef) => string}) => Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
|
|
6
25
|
mock.module("expo-localization", () => ({
|
|
7
26
|
getLocales: () => [{languageTag: "es-ES"}],
|
|
8
27
|
}));
|
|
9
28
|
|
|
10
29
|
describe("detectLocale", () => {
|
|
11
30
|
it("returns locale from expo-localization in native test env", () => {
|
|
12
|
-
const
|
|
13
|
-
|
|
31
|
+
const g = globalThis as unknown as GlobalThisWithNavigator;
|
|
32
|
+
const originalNavigator = g.navigator;
|
|
33
|
+
g.navigator = undefined;
|
|
14
34
|
const locale = detectLocale();
|
|
15
|
-
|
|
35
|
+
g.navigator = originalNavigator;
|
|
16
36
|
expect(typeof locale).toBe("string");
|
|
17
37
|
expect(locale.length).toBeGreaterThan(0);
|
|
18
38
|
});
|
|
19
39
|
|
|
20
40
|
it("falls back to en when expo-localization throws and no navigator", () => {
|
|
21
|
-
const
|
|
22
|
-
|
|
41
|
+
const g = globalThis as unknown as GlobalThisWithNavigator;
|
|
42
|
+
const originalNavigator = g.navigator;
|
|
43
|
+
g.navigator = undefined;
|
|
23
44
|
mock.module("expo-localization", () => ({
|
|
24
45
|
getLocales: () => {
|
|
25
46
|
throw new Error("not available");
|
|
26
47
|
},
|
|
27
48
|
}));
|
|
28
49
|
const locale = detectLocale();
|
|
29
|
-
|
|
50
|
+
g.navigator = originalNavigator;
|
|
30
51
|
// Reset mock
|
|
31
52
|
mock.module("expo-localization", () => ({
|
|
32
53
|
getLocales: () => [{languageTag: "es-ES"}],
|
|
@@ -35,10 +56,11 @@ describe("detectLocale", () => {
|
|
|
35
56
|
});
|
|
36
57
|
|
|
37
58
|
it("returns navigator.language when available", () => {
|
|
38
|
-
const
|
|
39
|
-
|
|
59
|
+
const g = globalThis as unknown as GlobalThisWithNavigator;
|
|
60
|
+
const originalNavigator = g.navigator;
|
|
61
|
+
g.navigator = {language: "fr-FR"};
|
|
40
62
|
const locale = detectLocale();
|
|
41
|
-
|
|
63
|
+
g.navigator = originalNavigator;
|
|
42
64
|
expect(locale).toBe("fr-FR");
|
|
43
65
|
});
|
|
44
66
|
});
|
|
@@ -54,10 +76,10 @@ describe("useConsentForms", () => {
|
|
|
54
76
|
}));
|
|
55
77
|
const api = {
|
|
56
78
|
enhanceEndpoints: mock(() => ({
|
|
57
|
-
injectEndpoints: mock((opts:
|
|
79
|
+
injectEndpoints: mock((opts: MockInjectOpts) => {
|
|
58
80
|
// Call endpoint builder to exercise provides/onQueryStarted
|
|
59
81
|
const build = {
|
|
60
|
-
query: mock((def:
|
|
82
|
+
query: mock((def: MockQueryDef) => {
|
|
61
83
|
// Call onQueryStarted with a successful result
|
|
62
84
|
if (def.onQueryStarted) {
|
|
63
85
|
void def.onQueryStarted(undefined, {
|
|
@@ -78,21 +100,21 @@ describe("useConsentForms", () => {
|
|
|
78
100
|
};
|
|
79
101
|
|
|
80
102
|
it("returns an array of forms when response is an array", () => {
|
|
81
|
-
const {api} = buildApi({data: [{id: "1", title: "Form 1"} as
|
|
82
|
-
const {result} = renderHook(() => useConsentForms(api as
|
|
103
|
+
const {api} = buildApi({data: [{id: "1", title: "Form 1"} as unknown as ConsentFormPublic]});
|
|
104
|
+
const {result} = renderHook(() => useConsentForms(api as unknown as ConsentFormsApi));
|
|
83
105
|
expect(result.current.forms).toBeDefined();
|
|
84
106
|
expect(Array.isArray(result.current.forms)).toBe(true);
|
|
85
107
|
});
|
|
86
108
|
|
|
87
109
|
it("unwraps .data property when response is object shape", () => {
|
|
88
|
-
const {api} = buildApi({data: {data: [{id: "2"} as
|
|
89
|
-
const {result} = renderHook(() => useConsentForms(api as
|
|
110
|
+
const {api} = buildApi({data: {data: [{id: "2"} as unknown as ConsentFormPublic]}});
|
|
111
|
+
const {result} = renderHook(() => useConsentForms(api as unknown as ConsentFormsApi, "/api"));
|
|
90
112
|
expect(Array.isArray(result.current.forms)).toBe(true);
|
|
91
113
|
});
|
|
92
114
|
|
|
93
115
|
it("returns empty array when no data is present", () => {
|
|
94
116
|
const {api} = buildApi({data: undefined, isLoading: true});
|
|
95
|
-
const {result} = renderHook(() => useConsentForms(api as
|
|
117
|
+
const {result} = renderHook(() => useConsentForms(api as unknown as ConsentFormsApi));
|
|
96
118
|
expect(result.current.forms).toEqual([]);
|
|
97
119
|
expect(result.current.isLoading).toBe(true);
|
|
98
120
|
});
|
|
@@ -101,9 +123,9 @@ describe("useConsentForms", () => {
|
|
|
101
123
|
const refetch = mock(() => {});
|
|
102
124
|
const api = {
|
|
103
125
|
enhanceEndpoints: mock(() => ({
|
|
104
|
-
injectEndpoints: mock((opts:
|
|
126
|
+
injectEndpoints: mock((opts: MockInjectOpts) => {
|
|
105
127
|
const build = {
|
|
106
|
-
query: mock((def:
|
|
128
|
+
query: mock((def: MockQueryDef) => {
|
|
107
129
|
if (def.onQueryStarted) {
|
|
108
130
|
void def.onQueryStarted(undefined, {
|
|
109
131
|
queryFulfilled: Promise.reject(new Error("failed")),
|
|
@@ -124,7 +146,7 @@ describe("useConsentForms", () => {
|
|
|
124
146
|
}),
|
|
125
147
|
})),
|
|
126
148
|
};
|
|
127
|
-
const {result} = renderHook(() => useConsentForms(api as
|
|
149
|
+
const {result} = renderHook(() => useConsentForms(api as unknown as ConsentFormsApi));
|
|
128
150
|
expect(result.current.error).toBe("error");
|
|
129
151
|
});
|
|
130
152
|
});
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import {describe, expect, it, mock} from "bun:test";
|
|
2
2
|
import {renderHook} from "@testing-library/react-native";
|
|
3
3
|
|
|
4
|
+
import type {SubmitConsentBody} from "./useSubmitConsent";
|
|
4
5
|
import {useSubmitConsent} from "./useSubmitConsent";
|
|
5
6
|
|
|
7
|
+
type SubmitConsentApi = Parameters<typeof useSubmitConsent>[0];
|
|
8
|
+
|
|
9
|
+
interface MockMutationDef {
|
|
10
|
+
query: (body: SubmitConsentBody) => {method: string; url: string};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface MockInjectOpts {
|
|
14
|
+
endpoints: (build: {mutation: (def: MockMutationDef) => string}) => Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
|
|
6
17
|
describe("useSubmitConsent", () => {
|
|
7
18
|
const buildApi = () => {
|
|
8
19
|
const unwrap = mock(async () => ({success: true}));
|
|
@@ -13,9 +24,9 @@ describe("useSubmitConsent", () => {
|
|
|
13
24
|
]);
|
|
14
25
|
const api = {
|
|
15
26
|
enhanceEndpoints: mock(() => ({
|
|
16
|
-
injectEndpoints: mock((opts:
|
|
27
|
+
injectEndpoints: mock((opts: MockInjectOpts) => {
|
|
17
28
|
const build = {
|
|
18
|
-
mutation: mock((def:
|
|
29
|
+
mutation: mock((def: MockMutationDef) => {
|
|
19
30
|
// Exercise the query builder
|
|
20
31
|
const result = def.query({
|
|
21
32
|
agreed: true,
|
|
@@ -37,7 +48,7 @@ describe("useSubmitConsent", () => {
|
|
|
37
48
|
|
|
38
49
|
it("returns submit function and state", () => {
|
|
39
50
|
const {api} = buildApi();
|
|
40
|
-
const {result} = renderHook(() => useSubmitConsent(api as
|
|
51
|
+
const {result} = renderHook(() => useSubmitConsent(api as unknown as SubmitConsentApi));
|
|
41
52
|
expect(typeof result.current.submit).toBe("function");
|
|
42
53
|
expect(result.current.isSubmitting).toBe(false);
|
|
43
54
|
expect(result.current.error).toBeUndefined();
|
|
@@ -45,7 +56,7 @@ describe("useSubmitConsent", () => {
|
|
|
45
56
|
|
|
46
57
|
it("calls submit mutation when submit is invoked", async () => {
|
|
47
58
|
const {api, submitMutation, unwrap} = buildApi();
|
|
48
|
-
const {result} = renderHook(() => useSubmitConsent(api as
|
|
59
|
+
const {result} = renderHook(() => useSubmitConsent(api as unknown as SubmitConsentApi, "/api"));
|
|
49
60
|
const response = await result.current.submit({
|
|
50
61
|
agreed: true,
|
|
51
62
|
consentFormId: "f1",
|
|
@@ -58,7 +69,7 @@ describe("useSubmitConsent", () => {
|
|
|
58
69
|
|
|
59
70
|
it("uses empty baseUrl when none provided", () => {
|
|
60
71
|
const {api} = buildApi();
|
|
61
|
-
const {result} = renderHook(() => useSubmitConsent(api as
|
|
72
|
+
const {result} = renderHook(() => useSubmitConsent(api as unknown as SubmitConsentApi));
|
|
62
73
|
expect(result.current.submit).toBeDefined();
|
|
63
74
|
});
|
|
64
75
|
});
|