@terreno/ui 0.11.1 → 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/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?: any;
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: any) => void;
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?: any;
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?: any;
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?: any;
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]: any;
689
+ [key: string]: unknown;
690
690
  };
691
691
  }
692
692
  export type ReactChild = ReactNode;
@@ -1624,11 +1624,11 @@ export interface PageProps {
1624
1624
  color?: SurfaceColor;
1625
1625
  maxWidth?: number | string;
1626
1626
  keyboardOffset?: number;
1627
- footer?: any;
1627
+ footer?: ReactNode;
1628
1628
  rightButton?: string;
1629
1629
  rightButtonOnClick?: () => void;
1630
- children?: any;
1631
- onError?: (error: Error, stack: any) => void;
1630
+ children?: ReactChildren;
1631
+ onError?: (error: Error, stack: string) => void;
1632
1632
  }
1633
1633
  export interface ProgressBarProps {
1634
1634
  color: SurfaceColor;
@@ -1647,7 +1647,7 @@ export interface RadioFieldProps {
1647
1647
  export interface SignatureFieldProps {
1648
1648
  disabled?: boolean;
1649
1649
  value?: string;
1650
- onChange: (value: any) => void;
1650
+ onChange: (value: string) => void;
1651
1651
  title?: string;
1652
1652
  onStart?: () => void;
1653
1653
  onEnd?: () => void;
@@ -2027,7 +2027,7 @@ export interface APIError {
2027
2027
  pointer?: string;
2028
2028
  parameter?: string;
2029
2029
  meta?: {
2030
- [id: string]: any;
2030
+ [id: string]: unknown;
2031
2031
  };
2032
2032
  };
2033
2033
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Common.js","sourceRoot":"","sources":["../src/Common.ts"],"names":[],"mappings":"AAqXA,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;AAqTF,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;AAwnED;;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"}
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
@@ -137,5 +137,5 @@
137
137
  "test:coverage": "TZ=America/New_York bun run ../scripts/check-coverage.ts",
138
138
  "types": "bun typedoc"
139
139
  },
140
- "version": "0.11.1"
140
+ "version": "0.11.2"
141
141
  }
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 {ListRenderItemInfo, StyleProp, TextStyle, ViewStyle} from "react-native";
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?: any;
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: any) => void;
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?: any;
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?: any;
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?: any;
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]: any;
910
+ [key: string]: unknown;
900
911
  };
901
912
  }
902
913
 
@@ -1745,6 +1756,7 @@ export interface HeightActionSheetProps {
1745
1756
 
1746
1757
  export interface HyperlinkProps {
1747
1758
  linkDefault?: boolean;
1759
+ // noExplicitAny: type comes from external linkify-it library which lacks an exported type
1748
1760
  linkify?: any;
1749
1761
  linkStyle?: StyleProp<any>;
1750
1762
  linkText?: string | ((url: string) => string);
@@ -1901,10 +1913,12 @@ export interface ModalProps {
1901
1913
  /**
1902
1914
  * The function to call when the primary button is clicked.
1903
1915
  */
1916
+ // noExplicitAny: callback value type varies by consumer context
1904
1917
  primaryButtonOnClick?: (value?: any) => void | Promise<void>;
1905
1918
  /**
1906
1919
  * The function to call when the secondary button is clicked.
1907
1920
  */
1921
+ // noExplicitAny: callback value type varies by consumer context
1908
1922
  secondaryButtonOnClick?: (value?: any) => void | Promise<void>;
1909
1923
  }
1910
1924
 
@@ -1917,6 +1931,7 @@ export interface NumberPickerActionSheetProps {
1917
1931
  }
1918
1932
 
1919
1933
  export interface PageProps {
1934
+ // noExplicitAny: React Navigation type varies by navigation stack configuration
1920
1935
  navigation?: any;
1921
1936
  scroll?: boolean;
1922
1937
  loading?: boolean;
@@ -1930,11 +1945,11 @@ export interface PageProps {
1930
1945
  color?: SurfaceColor;
1931
1946
  maxWidth?: number | string;
1932
1947
  keyboardOffset?: number;
1933
- footer?: any;
1948
+ footer?: ReactNode;
1934
1949
  rightButton?: string;
1935
1950
  rightButtonOnClick?: () => void;
1936
- children?: any;
1937
- onError?: (error: Error, stack: any) => void;
1951
+ children?: ReactChildren;
1952
+ onError?: (error: Error, stack: string) => void;
1938
1953
  }
1939
1954
 
1940
1955
  export interface ProgressBarProps {
@@ -1957,7 +1972,7 @@ export interface RadioFieldProps {
1957
1972
  export interface SignatureFieldProps {
1958
1973
  disabled?: boolean; // default "default"
1959
1974
  value?: string;
1960
- onChange: (value: any) => void;
1975
+ onChange: (value: string) => void;
1961
1976
  title?: string; // default "Signature"
1962
1977
  onStart?: () => void;
1963
1978
  onEnd?: () => void;
@@ -2356,16 +2371,19 @@ export type TapToEditProps =
2356
2371
 
2357
2372
  export interface BaseTapToEditProps extends Omit<FieldProps, "onChange" | "value"> {
2358
2373
  title: string;
2374
+ // noExplicitAny: value type varies across TapToEdit field types (text, number, date, etc.)
2359
2375
  value: any;
2360
2376
 
2361
2377
  /**
2362
2378
  * Not required if not editable.
2363
2379
  */
2380
+ // noExplicitAny: value type varies across TapToEdit field types
2364
2381
  setValue?: (value: any) => void;
2365
2382
 
2366
2383
  /**
2367
2384
  * Not required if not editable.
2368
2385
  */
2386
+ // noExplicitAny: value type varies across TapToEdit field types
2369
2387
  onSave?: (value: any) => void | Promise<void>;
2370
2388
 
2371
2389
  /**
@@ -2378,6 +2396,7 @@ export interface BaseTapToEditProps extends Omit<FieldProps, "onChange" | "value
2378
2396
  * Enable edit mode from outside the component.
2379
2397
  */
2380
2398
  isEditing?: boolean;
2399
+ // noExplicitAny: input value type varies across TapToEdit field types
2381
2400
  transform?: (value: any) => string;
2382
2401
  /**
2383
2402
  * Show a confirmation modal before saving the value.
@@ -2433,7 +2452,7 @@ export interface APIError {
2433
2452
  source?: string;
2434
2453
  pointer?: string;
2435
2454
  parameter?: string;
2436
- meta?: {[id: string]: any};
2455
+ meta?: {[id: string]: unknown};
2437
2456
  };
2438
2457
  }
2439
2458
 
@@ -2466,6 +2485,7 @@ export interface ModelFields {
2466
2485
 
2467
2486
  export interface OpenAPISpec {
2468
2487
  paths: {
2488
+ // noExplicitAny: OpenAPI path items are deeply accessed with chained property lookups
2469
2489
  [key: string]: any;
2470
2490
  };
2471
2491
  }
@@ -2498,7 +2518,8 @@ export interface ModelAdminFieldConfig {
2498
2518
 
2499
2519
  // The props for a custom column component for ModelAdmin.
2500
2520
  export interface ModelAdminCustomComponentProps extends Omit<FieldProps, "name"> {
2501
- doc: any; // The rest of the document.
2521
+ // noExplicitAny: document shape varies by model used with ModelAdmin
2522
+ doc: any;
2502
2523
  fieldKey: string; // Dot notation representation of the field.
2503
2524
  // user: User;
2504
2525
  editing: boolean; // Allow for inline editing of the field.
@@ -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 originalNavigator = (globalThis as any).navigator;
13
- (globalThis as any).navigator = undefined;
31
+ const g = globalThis as unknown as GlobalThisWithNavigator;
32
+ const originalNavigator = g.navigator;
33
+ g.navigator = undefined;
14
34
  const locale = detectLocale();
15
- (globalThis as any).navigator = originalNavigator;
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 originalNavigator = (globalThis as any).navigator;
22
- (globalThis as any).navigator = undefined;
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
- (globalThis as any).navigator = originalNavigator;
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 originalNavigator = (globalThis as any).navigator;
39
- (globalThis as any).navigator = {language: "fr-FR"};
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
- (globalThis as any).navigator = originalNavigator;
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: any) => {
79
+ injectEndpoints: mock((opts: MockInjectOpts) => {
58
80
  // Call endpoint builder to exercise provides/onQueryStarted
59
81
  const build = {
60
- query: mock((def: any) => {
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 any]});
82
- const {result} = renderHook(() => useConsentForms(api as any));
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 any]}});
89
- const {result} = renderHook(() => useConsentForms(api as any, "/api"));
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 any));
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: any) => {
126
+ injectEndpoints: mock((opts: MockInjectOpts) => {
105
127
  const build = {
106
- query: mock((def: any) => {
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 any));
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: any) => {
27
+ injectEndpoints: mock((opts: MockInjectOpts) => {
17
28
  const build = {
18
- mutation: mock((def: any) => {
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 any));
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 any, "/api"));
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 any));
72
+ const {result} = renderHook(() => useSubmitConsent(api as unknown as SubmitConsentApi));
62
73
  expect(result.current.submit).toBeDefined();
63
74
  });
64
75
  });