@terreno/ui 0.9.3 → 0.11.0

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.
Files changed (94) hide show
  1. package/dist/Banner.js +2 -2
  2. package/dist/Banner.js.map +1 -1
  3. package/dist/Common.d.ts +6 -10
  4. package/dist/Common.js.map +1 -1
  5. package/dist/ConsentHistory.d.ts +2 -1
  6. package/dist/DataTable.js +4 -2
  7. package/dist/DataTable.js.map +1 -1
  8. package/dist/DateUtilities.js +16 -7
  9. package/dist/DateUtilities.js.map +1 -1
  10. package/dist/DraggableList.d.ts +5 -4
  11. package/dist/DraggableList.js.map +1 -1
  12. package/dist/Icon.js +0 -3
  13. package/dist/Icon.js.map +1 -1
  14. package/dist/PasswordField.d.ts +1 -1
  15. package/dist/PasswordField.js.map +1 -1
  16. package/dist/TextFieldNumberActionSheet.d.ts +1 -1
  17. package/dist/Toast.d.ts +1 -1
  18. package/dist/Toast.js +2 -2
  19. package/dist/Toast.js.map +1 -1
  20. package/dist/index.d.ts +2 -2
  21. package/package.json +2 -1
  22. package/src/ActionSheet.test.tsx +262 -3
  23. package/src/AddressField.test.tsx +50 -0
  24. package/src/Banner.test.tsx +22 -0
  25. package/src/Banner.tsx +2 -2
  26. package/src/Box.test.tsx +218 -0
  27. package/src/Button.test.tsx +71 -0
  28. package/src/Common.ts +11 -9
  29. package/src/ConsentFormScreen.test.tsx +167 -0
  30. package/src/ConsentHistory.tsx +1 -1
  31. package/src/ConsentNavigator.test.tsx +210 -4
  32. package/src/DataTable.tsx +15 -15
  33. package/src/DateUtilities.test.ts +34 -16
  34. package/src/DateUtilities.tsx +24 -13
  35. package/src/DecimalRangeActionSheet.test.tsx +53 -2
  36. package/src/DraggableList.tsx +5 -5
  37. package/src/EmailField.test.tsx +81 -0
  38. package/src/EmojiSelector.test.tsx +262 -1
  39. package/src/ErrorBoundary.test.tsx +52 -1
  40. package/src/HeightActionSheet.test.tsx +57 -2
  41. package/src/Icon.tsx +0 -3
  42. package/src/InfoModalIcon.test.tsx +16 -0
  43. package/src/InfoTooltipButton.test.tsx +53 -1
  44. package/src/MobileAddressAutoComplete.test.tsx +137 -7
  45. package/src/Modal.test.tsx +188 -0
  46. package/src/NumberPickerActionSheet.test.tsx +59 -2
  47. package/src/OpenAPIContext.test.tsx +184 -3
  48. package/src/Page.test.tsx +162 -1
  49. package/src/Pagination.test.tsx +16 -0
  50. package/src/PasswordField.tsx +1 -1
  51. package/src/PhoneNumberField.test.tsx +46 -9
  52. package/src/PickerSelect.test.tsx +230 -0
  53. package/src/SegmentedControl.test.tsx +38 -0
  54. package/src/SelectBadge.test.tsx +52 -1
  55. package/src/SideDrawer.test.tsx +69 -0
  56. package/src/Signature.test.tsx +42 -5
  57. package/src/SignatureField.test.tsx +35 -0
  58. package/src/Slider.test.tsx +59 -0
  59. package/src/Spinner.test.tsx +6 -0
  60. package/src/SplitPage.test.tsx +228 -2
  61. package/src/TapToEdit.test.tsx +171 -1
  62. package/src/TerrenoProvider.test.tsx +42 -2
  63. package/src/TextFieldNumberActionSheet.tsx +1 -1
  64. package/src/Theme.test.tsx +118 -28
  65. package/src/Toast.test.tsx +95 -2
  66. package/src/Toast.tsx +3 -3
  67. package/src/Tooltip.test.tsx +204 -1
  68. package/src/UnifiedAddressAutoComplete.test.tsx +38 -19
  69. package/src/UserInactivity.test.tsx +73 -1
  70. package/src/Utilities.test.tsx +190 -2
  71. package/src/WebAddressAutocomplete.test.tsx +148 -1
  72. package/src/__snapshots__/ActionSheet.test.tsx.snap +1736 -0
  73. package/src/__snapshots__/Button.test.tsx.snap +68 -0
  74. package/src/__snapshots__/EmojiSelector.test.tsx.snap +1363 -0
  75. package/src/__snapshots__/InfoTooltipButton.test.tsx.snap +72 -3
  76. package/src/__snapshots__/MobileAddressAutoComplete.test.tsx.snap +60 -9
  77. package/src/__snapshots__/Modal.test.tsx.snap +181 -0
  78. package/src/__snapshots__/Page.test.tsx.snap +48 -2
  79. package/src/__snapshots__/PhoneNumberField.test.tsx.snap +0 -93
  80. package/src/__snapshots__/PickerSelect.test.tsx.snap +706 -0
  81. package/src/__snapshots__/SideDrawer.test.tsx.snap +533 -1399
  82. package/src/__snapshots__/Signature.test.tsx.snap +0 -3
  83. package/src/__snapshots__/SplitPage.test.tsx.snap +970 -0
  84. package/src/__snapshots__/UnifiedAddressAutoComplete.test.tsx.snap +220 -4
  85. package/src/__snapshots__/WebAddressAutocomplete.test.tsx.snap +93 -0
  86. package/src/bunSetup.ts +204 -121
  87. package/src/index.tsx +2 -2
  88. package/src/table/TableHeaderCell.test.tsx +142 -0
  89. package/src/table/TableRow.test.tsx +33 -0
  90. package/src/table/__snapshots__/TableRow.test.tsx.snap +403 -0
  91. package/src/table/tableContext.test.tsx +96 -0
  92. package/src/test-utils.tsx +1 -1
  93. package/src/useConsentForms.test.ts +130 -0
  94. package/src/useSubmitConsent.test.ts +64 -0
package/src/Common.ts CHANGED
@@ -2066,7 +2066,7 @@ export interface PaginationProps {
2066
2066
  * Data Table
2067
2067
  */
2068
2068
  export interface DataTableCellData {
2069
- value: any;
2069
+ value: unknown;
2070
2070
  highlight?: SurfaceColor;
2071
2071
  textSize?: "sm" | "md" | "lg";
2072
2072
  }
@@ -2085,7 +2085,7 @@ export interface DataTableColumn {
2085
2085
  }
2086
2086
 
2087
2087
  export interface DataTableProps {
2088
- data: {value: any; highlight?: SurfaceColor; textSize?: "sm" | "md" | "lg"}[][];
2088
+ data: DataTableCellData[][];
2089
2089
  columns: DataTableColumn[];
2090
2090
  alternateRowBackground?: boolean;
2091
2091
  totalPages?: number;
@@ -2100,19 +2100,21 @@ export interface DataTableProps {
2100
2100
  /**
2101
2101
  * When tapping the eye icon, a modal is shown with more info about the row.
2102
2102
  */
2103
- moreContentComponent?: React.ComponentType<{
2104
- column: DataTableColumn;
2105
- rowData: any[];
2106
- rowIndex: number;
2107
- }>;
2103
+ moreContentComponent?: React.ComponentType<
2104
+ {
2105
+ column: DataTableColumn;
2106
+ rowData: DataTableCellData[];
2107
+ rowIndex: number;
2108
+ } & Record<string, unknown>
2109
+ >;
2108
2110
  // Extra data to pass to the more modal.
2109
- moreContentExtraData?: any[];
2111
+ moreContentExtraData?: Record<string, unknown>[];
2110
2112
  // Allows handling of custom column types.
2111
2113
  customColumnComponentMap?: DataTableCustomComponentMap;
2112
2114
  }
2113
2115
 
2114
2116
  export interface DataTableCellProps {
2115
- value: any;
2117
+ value: DataTableCellData;
2116
2118
  columnDef: DataTableColumn;
2117
2119
  colIndex: number;
2118
2120
  isPinnedHorizontal: boolean;
@@ -0,0 +1,167 @@
1
+ import {afterAll, describe, expect, it, mock} from "bun:test";
2
+ import {act, fireEvent} from "@testing-library/react-native";
3
+ import {Text as RNText} from "react-native";
4
+
5
+ // Capture the real MarkdownView export before mocking so we can restore it in
6
+ // afterAll and avoid leaking a test-only mock to sibling test files.
7
+ const RealMarkdownViewModule = require("./MarkdownView");
8
+
9
+ // Mock MarkdownView with a simple Text passthrough so we can assert on the
10
+ // rendered (variable-substituted) content without depending on
11
+ // react-native-markdown-display's tokenization.
12
+ mock.module("./MarkdownView", () => ({
13
+ MarkdownView: ({children}: {children: string}) => (
14
+ <RNText testID="markdown-view">{children}</RNText>
15
+ ),
16
+ }));
17
+
18
+ import {ConsentFormScreen} from "./ConsentFormScreen";
19
+ import {renderWithTheme} from "./test-utils";
20
+ import type {ConsentFormPublic} from "./useConsentForms";
21
+
22
+ // Restore the real MarkdownView so downstream test files (e.g.
23
+ // MarkdownView.test.tsx) see the un-mocked module regardless of test order.
24
+ afterAll(() => {
25
+ mock.module("./MarkdownView", () => RealMarkdownViewModule);
26
+ });
27
+
28
+ const baseForm: ConsentFormPublic = {
29
+ active: true,
30
+ agreeButtonText: "I agree",
31
+ allowDecline: true,
32
+ captureSignature: false,
33
+ checkboxes: [],
34
+ content: {en: "Consent body", es: "Cuerpo de consentimiento"},
35
+ declineButtonText: "Decline",
36
+ defaultLocale: "en",
37
+ id: "consent-1",
38
+ order: 0,
39
+ required: true,
40
+ requireScrollToBottom: false,
41
+ slug: "consent",
42
+ title: "Consent",
43
+ type: "tos",
44
+ version: 1,
45
+ };
46
+
47
+ describe("ConsentFormScreen", () => {
48
+ it("renders with default state and a locale fallback", () => {
49
+ const onAgree = mock(() => {});
50
+ const {getByTestId, getByText} = renderWithTheme(
51
+ <ConsentFormScreen
52
+ form={{...baseForm, content: {en: "Hello"}}}
53
+ locale="fr"
54
+ onAgree={onAgree}
55
+ />
56
+ );
57
+ expect(getByTestId("consent-form-scroll-view")).toBeTruthy();
58
+ expect(getByText("I agree")).toBeTruthy();
59
+ });
60
+
61
+ it("substitutes variables in content and preserves unknown placeholders", () => {
62
+ const {getByTestId} = renderWithTheme(
63
+ <ConsentFormScreen
64
+ form={{...baseForm, content: {en: "Hello {{name}}, {{missing}} stays"}}}
65
+ locale="en"
66
+ onAgree={() => {}}
67
+ variables={{name: "Ada"}}
68
+ />
69
+ );
70
+ expect(getByTestId("markdown-view").props.children).toBe("Hello Ada, {{missing}} stays");
71
+ });
72
+
73
+ it("invokes onAgree with signature and checkbox values", () => {
74
+ const onAgree = mock(() => {});
75
+ const form: ConsentFormPublic = {
76
+ ...baseForm,
77
+ captureSignature: false,
78
+ checkboxes: [
79
+ {label: "Required box", required: true},
80
+ {label: "Optional box", required: false},
81
+ ],
82
+ };
83
+ const {getByTestId} = renderWithTheme(
84
+ <ConsentFormScreen form={form} locale="en" onAgree={onAgree} />
85
+ );
86
+ // Toggle the required checkbox on and the optional one off/on
87
+ act(() => {
88
+ fireEvent.press(getByTestId("consent-form-checkbox-0"));
89
+ fireEvent.press(getByTestId("consent-form-checkbox-1"));
90
+ fireEvent.press(getByTestId("consent-form-checkbox-1"));
91
+ });
92
+ act(() => {
93
+ fireEvent.press(getByTestId("consent-form-agree-button"));
94
+ });
95
+ // Button has a debounce; wait briefly.
96
+ return new Promise<void>((resolve) => {
97
+ setTimeout(() => {
98
+ expect(onAgree).toHaveBeenCalledTimes(1);
99
+ const call = onAgree.mock.calls[0]?.[0] as {
100
+ checkboxValues: Record<string, boolean>;
101
+ signature?: string;
102
+ };
103
+ expect(call.checkboxValues["0"]).toBe(true);
104
+ expect(call.signature).toBeUndefined();
105
+ resolve();
106
+ }, 600);
107
+ });
108
+ });
109
+
110
+ it("opens and confirms the confirmation modal before toggling checkbox on", () => {
111
+ const form: ConsentFormPublic = {
112
+ ...baseForm,
113
+ checkboxes: [{confirmationPrompt: "Really?", label: "Tricky", required: true}],
114
+ };
115
+ const {getByTestId, queryByText} = renderWithTheme(
116
+ <ConsentFormScreen form={form} locale="en" onAgree={() => {}} />
117
+ );
118
+ act(() => {
119
+ fireEvent.press(getByTestId("consent-form-checkbox-0"));
120
+ });
121
+ expect(queryByText("Really?")).toBeTruthy();
122
+ });
123
+
124
+ it("fires onDecline when decline is pressed", () => {
125
+ const onDecline = mock(() => {});
126
+ const {getByTestId} = renderWithTheme(
127
+ <ConsentFormScreen form={baseForm} locale="en" onAgree={() => {}} onDecline={onDecline} />
128
+ );
129
+ act(() => {
130
+ fireEvent.press(getByTestId("consent-form-decline-button"));
131
+ });
132
+ return new Promise<void>((resolve) => {
133
+ setTimeout(() => {
134
+ expect(onDecline).toHaveBeenCalled();
135
+ resolve();
136
+ }, 600);
137
+ });
138
+ });
139
+
140
+ it("fires scroll handlers without crashing", () => {
141
+ const form = {...baseForm, requireScrollToBottom: true};
142
+ const {getByTestId} = renderWithTheme(
143
+ <ConsentFormScreen form={form} locale="en" onAgree={() => {}} />
144
+ );
145
+ const scroll = getByTestId("consent-form-scroll-view");
146
+ act(() => {
147
+ fireEvent(scroll, "contentSizeChange", 0, 300);
148
+ fireEvent(scroll, "layout", {nativeEvent: {layout: {height: 400}}});
149
+ fireEvent(scroll, "scroll", {
150
+ nativeEvent: {
151
+ contentOffset: {y: 100},
152
+ contentSize: {height: 300},
153
+ layoutMeasurement: {height: 210},
154
+ },
155
+ });
156
+ });
157
+ expect(scroll).toBeTruthy();
158
+ });
159
+
160
+ it("shows the scroll hint when content is still scrollable", () => {
161
+ const form = {...baseForm, requireScrollToBottom: true};
162
+ const {getByTestId} = renderWithTheme(
163
+ <ConsentFormScreen form={form} locale="en" onAgree={() => {}} />
164
+ );
165
+ expect(getByTestId("consent-form-scroll-hint")).toBeTruthy();
166
+ });
167
+ });
@@ -17,7 +17,7 @@ import type {ConsentHistoryEntry} from "./useConsentHistory";
17
17
  import {useConsentHistory} from "./useConsentHistory";
18
18
 
19
19
  interface ConsentHistoryProps {
20
- api: any;
20
+ api: Parameters<typeof useConsentHistory>[0];
21
21
  baseUrl?: string;
22
22
  title?: string;
23
23
  }
@@ -33,7 +33,7 @@ const createMockApi = (forms: ConsentFormPublic[]) => {
33
33
  mockSubmitMutation.mockReturnValue({unwrap: mockUnwrap});
34
34
 
35
35
  const innerApi = {
36
- injectEndpoints: mock((_config: any) => ({
36
+ injectEndpoints: mock((_config: unknown) => ({
37
37
  useGetPendingConsentsQuery: mock(() => ({
38
38
  data: {data: forms},
39
39
  error: undefined,
@@ -48,13 +48,13 @@ const createMockApi = (forms: ConsentFormPublic[]) => {
48
48
  };
49
49
 
50
50
  return {
51
- enhanceEndpoints: mock((_config: any) => innerApi),
51
+ enhanceEndpoints: mock((_config: unknown) => innerApi),
52
52
  };
53
53
  };
54
54
 
55
55
  const createLoadingMockApi = () => {
56
56
  const innerApi = {
57
- injectEndpoints: mock((_config: any) => ({
57
+ injectEndpoints: mock((_config: unknown) => ({
58
58
  useGetPendingConsentsQuery: mock(() => ({
59
59
  data: undefined,
60
60
  error: undefined,
@@ -69,7 +69,7 @@ const createLoadingMockApi = () => {
69
69
  };
70
70
 
71
71
  return {
72
- enhanceEndpoints: mock((_config: any) => innerApi),
72
+ enhanceEndpoints: mock((_config: unknown) => innerApi),
73
73
  };
74
74
  };
75
75
 
@@ -164,4 +164,210 @@ describe("ConsentNavigator", () => {
164
164
  // The extra screen should have a working next button (onNext was injected)
165
165
  expect(getByTestId("extra-screen-next")).toBeTruthy();
166
166
  });
167
+
168
+ it("renders children when error is auth (401)", () => {
169
+ const api = {
170
+ enhanceEndpoints: mock(() => ({
171
+ injectEndpoints: mock(() => ({
172
+ useGetPendingConsentsQuery: mock(() => ({
173
+ data: undefined,
174
+ error: {status: 401},
175
+ isLoading: false,
176
+ refetch: mock(() => {}),
177
+ })),
178
+ useSubmitConsentResponseMutation: mock(() => [
179
+ mock(() => ({unwrap: mock(() => Promise.resolve({}))})),
180
+ {error: undefined, isLoading: false},
181
+ ]),
182
+ })),
183
+ })),
184
+ };
185
+
186
+ const {getByText} = renderWithTheme(
187
+ <ConsentNavigator api={api as any}>
188
+ <Text>App Content</Text>
189
+ </ConsentNavigator>
190
+ );
191
+ expect(getByText("App Content")).toBeTruthy();
192
+ });
193
+
194
+ it("renders children when error is auth (403)", () => {
195
+ const api = {
196
+ enhanceEndpoints: mock(() => ({
197
+ injectEndpoints: mock(() => ({
198
+ useGetPendingConsentsQuery: mock(() => ({
199
+ data: undefined,
200
+ error: {originalStatus: 403},
201
+ isLoading: false,
202
+ refetch: mock(() => {}),
203
+ })),
204
+ useSubmitConsentResponseMutation: mock(() => [
205
+ mock(() => ({unwrap: mock(() => Promise.resolve({}))})),
206
+ {error: undefined, isLoading: false},
207
+ ]),
208
+ })),
209
+ })),
210
+ };
211
+
212
+ const {getByText} = renderWithTheme(
213
+ <ConsentNavigator api={api as any}>
214
+ <Text>App Content</Text>
215
+ </ConsentNavigator>
216
+ );
217
+ expect(getByText("App Content")).toBeTruthy();
218
+ });
219
+
220
+ it("renders error screen with retry button for non-auth errors", () => {
221
+ const onError = mock(() => {});
222
+ const api = {
223
+ enhanceEndpoints: mock(() => ({
224
+ injectEndpoints: mock(() => ({
225
+ useGetPendingConsentsQuery: mock(() => ({
226
+ data: undefined,
227
+ error: {message: "Server error", status: 500},
228
+ isLoading: false,
229
+ refetch: mock(() => {}),
230
+ })),
231
+ useSubmitConsentResponseMutation: mock(() => [
232
+ mock(() => ({unwrap: mock(() => Promise.resolve({}))})),
233
+ {error: undefined, isLoading: false},
234
+ ]),
235
+ })),
236
+ })),
237
+ };
238
+
239
+ const {getByTestId, getByText} = renderWithTheme(
240
+ <ConsentNavigator api={api as any} onError={onError}>
241
+ <Text>App Content</Text>
242
+ </ConsentNavigator>
243
+ );
244
+ expect(getByTestId("consent-navigator-error")).toBeTruthy();
245
+ expect(getByText("Failed to load consent forms")).toBeTruthy();
246
+ expect(onError).toHaveBeenCalled();
247
+ });
248
+
249
+ it("renders without error when no extra screens provided", () => {
250
+ const api = createMockApi([]);
251
+
252
+ const {getByText} = renderWithTheme(
253
+ <ConsentNavigator api={api}>
254
+ <Text>App Content</Text>
255
+ </ConsentNavigator>
256
+ );
257
+
258
+ expect(getByText("App Content")).toBeTruthy();
259
+ });
260
+
261
+ it("calls submit and refetch when user agrees to a consent form", async () => {
262
+ const {act, fireEvent} = await import("@testing-library/react-native");
263
+ const form = makeForm();
264
+ const unwrap = mock(() => Promise.resolve({agreed: true, id: "r-1"}));
265
+ const submitMutation = mock(() => ({unwrap}));
266
+ const refetch = mock(() => Promise.resolve());
267
+ const api = {
268
+ enhanceEndpoints: mock(() => ({
269
+ injectEndpoints: mock(() => ({
270
+ useGetPendingConsentsQuery: mock(() => ({
271
+ data: {data: [form]},
272
+ error: undefined,
273
+ isLoading: false,
274
+ refetch,
275
+ })),
276
+ useSubmitConsentResponseMutation: mock(() => [
277
+ submitMutation,
278
+ {error: undefined, isLoading: false},
279
+ ]),
280
+ })),
281
+ })),
282
+ };
283
+
284
+ const {getByTestId} = renderWithTheme(
285
+ <ConsentNavigator api={api as any}>
286
+ <Text>App Content</Text>
287
+ </ConsentNavigator>
288
+ );
289
+
290
+ await act(async () => {
291
+ fireEvent.press(getByTestId("consent-form-agree-button"));
292
+ });
293
+
294
+ expect(submitMutation).toHaveBeenCalled();
295
+ });
296
+
297
+ it("invokes onError when consent submission fails on agree", async () => {
298
+ const {act, fireEvent} = await import("@testing-library/react-native");
299
+ const form = makeForm();
300
+ const onError = mock(() => {});
301
+ const unwrap = mock(() => Promise.reject(new Error("submit failed")));
302
+ const submitMutation = mock(() => ({unwrap}));
303
+ const refetch = mock(() => Promise.resolve());
304
+ const api = {
305
+ enhanceEndpoints: mock(() => ({
306
+ injectEndpoints: mock(() => ({
307
+ useGetPendingConsentsQuery: mock(() => ({
308
+ data: {data: [form]},
309
+ error: undefined,
310
+ isLoading: false,
311
+ refetch,
312
+ })),
313
+ useSubmitConsentResponseMutation: mock(() => [
314
+ submitMutation,
315
+ {error: undefined, isLoading: false},
316
+ ]),
317
+ })),
318
+ })),
319
+ };
320
+
321
+ const {getByTestId} = renderWithTheme(
322
+ <ConsentNavigator api={api as any} onError={onError}>
323
+ <Text>App Content</Text>
324
+ </ConsentNavigator>
325
+ );
326
+
327
+ await act(async () => {
328
+ fireEvent.press(getByTestId("consent-form-agree-button"));
329
+ });
330
+
331
+ await act(async () => {
332
+ await new Promise((r) => setTimeout(r, 0));
333
+ });
334
+
335
+ expect(onError).toHaveBeenCalled();
336
+ });
337
+
338
+ it("calls submit with agreed=false when user declines an optional consent form", async () => {
339
+ const {act, fireEvent} = await import("@testing-library/react-native");
340
+ const form = makeForm({allowDecline: true, required: false});
341
+ const unwrap = mock(() => Promise.resolve({agreed: false, id: "r-2"}));
342
+ const submitMutation = mock(() => ({unwrap}));
343
+ const refetch = mock(() => Promise.resolve());
344
+ const api = {
345
+ enhanceEndpoints: mock(() => ({
346
+ injectEndpoints: mock(() => ({
347
+ useGetPendingConsentsQuery: mock(() => ({
348
+ data: {data: [form]},
349
+ error: undefined,
350
+ isLoading: false,
351
+ refetch,
352
+ })),
353
+ useSubmitConsentResponseMutation: mock(() => [
354
+ submitMutation,
355
+ {error: undefined, isLoading: false},
356
+ ]),
357
+ })),
358
+ })),
359
+ };
360
+
361
+ const {getByTestId} = renderWithTheme(
362
+ <ConsentNavigator api={api as any}>
363
+ <Text>App Content</Text>
364
+ </ConsentNavigator>
365
+ );
366
+
367
+ const declineBtn = getByTestId("consent-form-decline-button");
368
+ await act(async () => {
369
+ fireEvent.press(declineBtn);
370
+ });
371
+ expect(submitMutation).toHaveBeenCalled();
372
+ });
167
373
  });
package/src/DataTable.tsx CHANGED
@@ -32,23 +32,21 @@ import {TableTitle} from "./table/TableTitle";
32
32
  // easily.
33
33
 
34
34
  const TextCell: FC<{
35
- cellData: {value: string; textSize?: "sm" | "md" | "lg"};
35
+ cellData: DataTableCellData;
36
36
  column: DataTableColumn;
37
37
  }> = ({cellData}) => {
38
38
  return (
39
39
  <Box flex="grow" justifyContent="center">
40
- <Text size={cellData.textSize || "md"}>{cellData.value}</Text>
40
+ <Text size={cellData.textSize || "md"}>{String(cellData.value ?? "")}</Text>
41
41
  </Box>
42
42
  );
43
43
  };
44
44
 
45
- const CheckedCell: FC<{cellData: {value: boolean}; column: DataTableColumn}> = ({cellData}) => {
45
+ const CheckedCell: FC<{cellData: DataTableCellData; column: DataTableColumn}> = ({cellData}) => {
46
+ const isChecked = Boolean(cellData.value);
46
47
  return (
47
48
  <Box flex="grow" justifyContent="center" width="100%">
48
- <Icon
49
- color={cellData.value ? "success" : "secondaryDark"}
50
- iconName={cellData.value ? "check" : "x"}
51
- />
49
+ <Icon color={isChecked ? "success" : "secondaryDark"} iconName={isChecked ? "check" : "x"} />
52
50
  </Box>
53
51
  );
54
52
  };
@@ -71,7 +69,7 @@ const DataTableCell: FC<DataTableCellProps> = ({
71
69
  // Default to TextCell
72
70
  let Component: React.ComponentType<{
73
71
  column: DataTableColumn;
74
- cellData: {value: any; highlight?: SurfaceColor};
72
+ cellData: DataTableCellData;
75
73
  }> = TextCell;
76
74
  if (customColumnComponentMap?.[columnDef.columnType]) {
77
75
  Component = customColumnComponentMap[columnDef.columnType];
@@ -401,20 +399,22 @@ const DataTableHeader: FC<DataTableHeaderProps> = ({
401
399
  };
402
400
 
403
401
  interface DataTableContentProps {
404
- data: any[][];
402
+ data: DataTableCellData[][];
405
403
  columns: DataTableColumn[];
406
404
  pinnedColumns: number;
407
405
  alternateRowBackground: boolean;
408
406
  columnWidths: number[];
409
407
  bodyScrollRef: React.RefObject<ScrollView | null>;
410
408
  onScroll: (event: NativeSyntheticEvent<NativeScrollEvent>, isHeader: boolean) => void;
411
- moreContentComponent?: React.ComponentType<{
412
- column: DataTableColumn;
413
- rowData: any[];
414
- rowIndex: number;
415
- }>;
409
+ moreContentComponent?: React.ComponentType<
410
+ {
411
+ column: DataTableColumn;
412
+ rowData: DataTableCellData[];
413
+ rowIndex: number;
414
+ } & Record<string, unknown>
415
+ >;
416
416
  // Extra props to pass to the more modal, one per row.
417
- moreContentExtraData?: any[];
417
+ moreContentExtraData?: Record<string, unknown>[];
418
418
  moreContentSize?: "sm" | "md" | "lg";
419
419
  customColumnComponentMap?: DataTableCustomComponentMap;
420
420
  rowHeight: number;
@@ -25,9 +25,11 @@ describe("DateUtilities", () => {
25
25
 
26
26
  describe("humanDate", () => {
27
27
  it("should return invalid date if date is undefined", () => {
28
- expect(() => humanDate(undefined as any, {timezone: "America/New_York"})).toThrow(
29
- "humanDate: Passed undefined"
30
- );
28
+ expect(() =>
29
+ humanDate(undefined as unknown as Parameters<typeof humanDate>[0], {
30
+ timezone: "America/New_York",
31
+ })
32
+ ).toThrow("humanDate: Passed undefined");
31
33
  });
32
34
 
33
35
  it("should throw an error if date is invalid", () => {
@@ -95,9 +97,11 @@ describe("DateUtilities", () => {
95
97
 
96
98
  describe("humanDateTime", () => {
97
99
  it("should return invalid date if date is undefined", () => {
98
- expect(() => humanDateAndTime(undefined as any, {timezone: "America/New_York"})).toThrow(
99
- "humanDateAndTime: Passed undefined"
100
- );
100
+ expect(() =>
101
+ humanDateAndTime(undefined as unknown as Parameters<typeof humanDateAndTime>[0], {
102
+ timezone: "America/New_York",
103
+ })
104
+ ).toThrow("humanDateAndTime: Passed undefined");
101
105
  });
102
106
 
103
107
  it("should throw an error if date is invalid", () => {
@@ -232,7 +236,9 @@ describe("DateUtilities", () => {
232
236
 
233
237
  describe("printDate", () => {
234
238
  it("should throw an error if date is undefined", () => {
235
- expect(printDate(undefined as any)).toBe("Invalid Date");
239
+ expect(printDate(undefined as unknown as Parameters<typeof printDate>[0])).toBe(
240
+ "Invalid Date"
241
+ );
236
242
  });
237
243
 
238
244
  it("should throw an error if date is invalid", () => {
@@ -270,11 +276,17 @@ describe("DateUtilities", () => {
270
276
 
271
277
  describe("printOnlyDate", () => {
272
278
  it("should print invalid if no date and no default", () => {
273
- expect(printOnlyDate(undefined as any)).toBe("Invalid Date");
279
+ expect(printOnlyDate(undefined as unknown as Parameters<typeof printOnlyDate>[0])).toBe(
280
+ "Invalid Date"
281
+ );
274
282
  });
275
283
 
276
284
  it("should print default if no date and default", () => {
277
- expect(printOnlyDate(undefined as any, {defaultValue: "default"})).toBe("default");
285
+ expect(
286
+ printOnlyDate(undefined as unknown as Parameters<typeof printOnlyDate>[0], {
287
+ defaultValue: "default",
288
+ })
289
+ ).toBe("default");
278
290
  });
279
291
 
280
292
  it("should print the date in the default format", () => {
@@ -287,9 +299,11 @@ describe("DateUtilities", () => {
287
299
 
288
300
  describe("printDateTime", () => {
289
301
  it("should return invalid date if date is undefined", () => {
290
- expect(printDateAndTime(undefined as any, {timezone: "America/New_York"})).toBe(
291
- "Invalid Datetime"
292
- );
302
+ expect(
303
+ printDateAndTime(undefined as unknown as Parameters<typeof printDateAndTime>[0], {
304
+ timezone: "America/New_York",
305
+ })
306
+ ).toBe("Invalid Datetime");
293
307
  });
294
308
 
295
309
  it("should throw an error if date is invalid", () => {
@@ -339,7 +353,11 @@ describe("DateUtilities", () => {
339
353
 
340
354
  describe("printTime", () => {
341
355
  it("should return invalid date if date is undefined", () => {
342
- expect(printTime(undefined as any, {timezone: "America/New_York"})).toBe("Invalid Date");
356
+ expect(
357
+ printTime(undefined as unknown as Parameters<typeof printTime>[0], {
358
+ timezone: "America/New_York",
359
+ })
360
+ ).toBe("Invalid Date");
343
361
  });
344
362
 
345
363
  it("should throw an error if date is invalid", () => {
@@ -349,9 +367,9 @@ describe("DateUtilities", () => {
349
367
  });
350
368
 
351
369
  it("should throw an error with no timezone", () => {
352
- expect(() => printTime("2022-12-24T12:00:00.000Z", {} as any)).toThrow(
353
- "printTime: timezone is required"
354
- );
370
+ expect(() =>
371
+ printTime("2022-12-24T12:00:00.000Z", {} as unknown as Parameters<typeof printTime>[1])
372
+ ).toThrow("printTime: timezone is required");
355
373
  });
356
374
 
357
375
  it("should return the time in the default format", () => {