@terreno/ui 0.13.3 → 0.14.1

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 (179) hide show
  1. package/dist/ActionSheet.d.ts +5 -5
  2. package/dist/ActionSheet.js +2 -2
  3. package/dist/ActionSheet.js.map +1 -1
  4. package/dist/Avatar.js +1 -1
  5. package/dist/Avatar.js.map +1 -1
  6. package/dist/Banner.js.map +1 -1
  7. package/dist/Box.js +2 -0
  8. package/dist/Box.js.map +1 -1
  9. package/dist/Button.d.ts +2 -2
  10. package/dist/Button.js +35 -23
  11. package/dist/Button.js.map +1 -1
  12. package/dist/Common.d.ts +16 -4
  13. package/dist/Common.js +4 -4
  14. package/dist/Common.js.map +1 -1
  15. package/dist/ConsentFormScreen.js +3 -3
  16. package/dist/ConsentFormScreen.js.map +1 -1
  17. package/dist/ConsentNavigator.d.ts +1 -1
  18. package/dist/ConsentNavigator.js +2 -1
  19. package/dist/ConsentNavigator.js.map +1 -1
  20. package/dist/CustomSelectField.js +3 -1
  21. package/dist/CustomSelectField.js.map +1 -1
  22. package/dist/DataTable.js +1 -1
  23. package/dist/DataTable.js.map +1 -1
  24. package/dist/DateTimeActionSheet.js +2 -1
  25. package/dist/DateTimeActionSheet.js.map +1 -1
  26. package/dist/DateTimeField.js +3 -2
  27. package/dist/DateTimeField.js.map +1 -1
  28. package/dist/DateUtilities.d.ts +25 -25
  29. package/dist/DateUtilities.js +31 -32
  30. package/dist/DateUtilities.js.map +1 -1
  31. package/dist/HeightField.js.map +1 -1
  32. package/dist/Hyperlink.js +19 -9
  33. package/dist/Hyperlink.js.map +1 -1
  34. package/dist/IconButton.js.map +1 -1
  35. package/dist/ImageBackground.d.ts +2 -5
  36. package/dist/ImageBackground.js +1 -1
  37. package/dist/ImageBackground.js.map +1 -1
  38. package/dist/MediaQuery.d.ts +4 -4
  39. package/dist/MediaQuery.js +8 -8
  40. package/dist/MediaQuery.js.map +1 -1
  41. package/dist/ModalSheet.d.ts +3 -2
  42. package/dist/ModalSheet.js +1 -1
  43. package/dist/ModalSheet.js.map +1 -1
  44. package/dist/OfflineBanner.d.ts +21 -0
  45. package/dist/OfflineBanner.js +25 -0
  46. package/dist/OfflineBanner.js.map +1 -0
  47. package/dist/OpenAPIContext.js +1 -1
  48. package/dist/OpenAPIContext.js.map +1 -1
  49. package/dist/Page.d.ts +1 -0
  50. package/dist/Page.js +7 -2
  51. package/dist/Page.js.map +1 -1
  52. package/dist/Pagination.js.map +1 -1
  53. package/dist/Permissions.js +3 -0
  54. package/dist/Permissions.js.map +1 -1
  55. package/dist/PickerSelect.d.ts +1 -1
  56. package/dist/PickerSelect.js +9 -6
  57. package/dist/PickerSelect.js.map +1 -1
  58. package/dist/SelectField.js +1 -1
  59. package/dist/SelectField.js.map +1 -1
  60. package/dist/SplitPage.js +7 -2
  61. package/dist/SplitPage.js.map +1 -1
  62. package/dist/SplitPage.native.js +4 -1
  63. package/dist/SplitPage.native.js.map +1 -1
  64. package/dist/TapToEdit.d.ts +1 -1
  65. package/dist/TapToEdit.js +12 -14
  66. package/dist/TapToEdit.js.map +1 -1
  67. package/dist/Toast.js.map +1 -1
  68. package/dist/ToastNotifications.js +2 -2
  69. package/dist/ToastNotifications.js.map +1 -1
  70. package/dist/Tooltip.d.ts +24 -1
  71. package/dist/Tooltip.js +2 -2
  72. package/dist/Tooltip.js.map +1 -1
  73. package/dist/Unifier.d.ts +3 -3
  74. package/dist/Unifier.js +15 -12
  75. package/dist/Unifier.js.map +1 -1
  76. package/dist/Utilities.d.ts +12 -8
  77. package/dist/Utilities.js +13 -15
  78. package/dist/Utilities.js.map +1 -1
  79. package/dist/index.d.ts +2 -1
  80. package/dist/index.js +2 -1
  81. package/dist/index.js.map +1 -1
  82. package/dist/signUp/PasswordRequirements.js +3 -3
  83. package/dist/signUp/PasswordRequirements.js.map +1 -1
  84. package/dist/table/TableHeaderCell.js +1 -9
  85. package/dist/table/TableHeaderCell.js.map +1 -1
  86. package/dist/table/tableContext.d.ts +1 -1
  87. package/dist/table/tableContext.js +2 -2
  88. package/dist/table/tableContext.js.map +1 -1
  89. package/package.json +2 -1
  90. package/src/ActionSheet.test.tsx +1 -0
  91. package/src/ActionSheet.tsx +8 -6
  92. package/src/Avatar.tsx +9 -2
  93. package/src/Badge.test.tsx +1 -0
  94. package/src/Banner.test.tsx +71 -0
  95. package/src/Banner.tsx +1 -1
  96. package/src/Box.test.tsx +1 -0
  97. package/src/Box.tsx +10 -6
  98. package/src/Button.test.tsx +35 -0
  99. package/src/Button.tsx +65 -34
  100. package/src/Common.ts +42 -19
  101. package/src/ConsentFormScreen.test.tsx +124 -0
  102. package/src/ConsentFormScreen.tsx +18 -6
  103. package/src/ConsentNavigator.test.tsx +1 -0
  104. package/src/ConsentNavigator.tsx +5 -3
  105. package/src/CustomSelectField.tsx +3 -1
  106. package/src/DataTable.test.tsx +218 -0
  107. package/src/DataTable.tsx +1 -1
  108. package/src/DateTimeActionSheet.tsx +7 -3
  109. package/src/DateTimeField.test.tsx +1 -0
  110. package/src/DateTimeField.tsx +3 -2
  111. package/src/DateUtilities.test.ts +111 -0
  112. package/src/DateUtilities.tsx +43 -44
  113. package/src/DecimalRangeActionSheet.test.tsx +28 -0
  114. package/src/ErrorBoundary.test.tsx +1 -0
  115. package/src/HeightActionSheet.test.tsx +16 -0
  116. package/src/HeightField.test.tsx +106 -1
  117. package/src/HeightField.tsx +2 -1
  118. package/src/Hyperlink.tsx +83 -52
  119. package/src/IconButton.tsx +1 -1
  120. package/src/ImageBackground.tsx +5 -6
  121. package/src/MediaQuery.ts +8 -8
  122. package/src/MobileAddressAutoComplete.test.tsx +20 -1
  123. package/src/ModalSheet.test.tsx +1 -5
  124. package/src/ModalSheet.tsx +15 -6
  125. package/src/NumberField.test.tsx +14 -0
  126. package/src/OfflineBanner.test.tsx +70 -0
  127. package/src/OfflineBanner.tsx +54 -0
  128. package/src/OpenAPIContext.tsx +3 -2
  129. package/src/Page.test.tsx +28 -0
  130. package/src/Page.tsx +18 -2
  131. package/src/Pagination.tsx +1 -1
  132. package/src/Permissions.ts +3 -0
  133. package/src/PickerSelect.tsx +20 -17
  134. package/src/SelectBadge.test.tsx +1 -0
  135. package/src/SelectField.tsx +1 -1
  136. package/src/Signature.test.tsx +1 -0
  137. package/src/SplitPage.native.tsx +2 -0
  138. package/src/SplitPage.tsx +6 -1
  139. package/src/TapToEdit.test.tsx +48 -0
  140. package/src/TapToEdit.tsx +13 -14
  141. package/src/Toast.tsx +1 -1
  142. package/src/ToastNotifications.test.tsx +738 -0
  143. package/src/ToastNotifications.tsx +3 -6
  144. package/src/Tooltip.test.tsx +586 -8
  145. package/src/Tooltip.tsx +2 -2
  146. package/src/Unifier.ts +20 -16
  147. package/src/Utilities.tsx +20 -19
  148. package/src/WebAddressAutocomplete.test.tsx +138 -0
  149. package/src/WebDropdownMenu.test.tsx +23 -0
  150. package/src/__snapshots__/AddressField.test.tsx.snap +3 -1
  151. package/src/__snapshots__/Button.test.tsx.snap +92 -50
  152. package/src/__snapshots__/CustomSelectField.test.tsx.snap +21 -7
  153. package/src/__snapshots__/DecimalRangeActionSheet.test.tsx.snap +14 -8
  154. package/src/__snapshots__/ErrorPage.test.tsx.snap +7 -4
  155. package/src/__snapshots__/Field.test.tsx.snap +18 -6
  156. package/src/__snapshots__/HeightActionSheet.test.tsx.snap +14 -8
  157. package/src/__snapshots__/HeightField.test.tsx.snap +35 -20
  158. package/src/__snapshots__/InfoModalIcon.test.tsx.snap +28 -16
  159. package/src/__snapshots__/Modal.test.tsx.snap +19 -10
  160. package/src/__snapshots__/ModalSheet.test.tsx.snap +0 -1
  161. package/src/__snapshots__/NumberPickerActionSheet.test.tsx.snap +14 -8
  162. package/src/__snapshots__/Page.test.tsx.snap +7 -4
  163. package/src/__snapshots__/SelectField.test.tsx.snap +18 -6
  164. package/src/__snapshots__/TerrenoProvider.test.tsx.snap +0 -2
  165. package/src/__snapshots__/TimezonePicker.test.tsx.snap +18 -6
  166. package/src/bunSetup.ts +25 -2
  167. package/src/index.tsx +2 -1
  168. package/src/login/LoginScreen.test.tsx +23 -1
  169. package/src/login/__snapshots__/LoginScreen.test.tsx.snap +15 -6
  170. package/src/signUp/PasswordRequirements.tsx +9 -6
  171. package/src/signUp/__snapshots__/PasswordRequirements.test.tsx.snap +50 -2
  172. package/src/signUp/__snapshots__/SignUpScreen.test.tsx.snap +35 -5
  173. package/src/table/TableHeaderCell.tsx +8 -11
  174. package/src/table/TableRow.test.tsx +31 -1
  175. package/src/table/__snapshots__/TableBadge.test.tsx.snap +3 -1
  176. package/src/table/__snapshots__/TableHeaderCell.test.tsx.snap +2 -0
  177. package/src/table/tableContext.tsx +2 -2
  178. package/src/types/react-native-swiper-flatlist.d.ts +1 -0
  179. package/src/useStoredState.test.tsx +47 -0
package/src/Unifier.ts CHANGED
@@ -8,6 +8,7 @@ import {requestPermissions} from "./Permissions";
8
8
 
9
9
  declare global {
10
10
  interface Window {
11
+ // biome-ignore lint/suspicious/noExplicitAny: Google Maps JS SDK global type is loaded dynamically and not bundled as a typed dependency
11
12
  google: any;
12
13
  }
13
14
  }
@@ -17,15 +18,20 @@ export type PlatformOS = "ios" | "android" | "web";
17
18
  type Luminance = "light" | "lighter" | "dark" | "darker";
18
19
 
19
20
  // Changes a color luminance
20
- export function changeColorLuminance(hex: string, luminanceChange: Luminance) {
21
- // Validate hex string, strip "#" if present.
22
- hex = String(hex).replace(/[^0-9a-f]/gi, "");
23
- if (hex.length === 3) {
24
- hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
25
- } else if (hex.length !== 6) {
26
- throw new Error(`Invalid color hex: ${hex}`);
21
+ export const changeColorLuminance = (hex: string, luminanceChange: Luminance) => {
22
+ let normalizedHex = String(hex).replace(/[^0-9a-f]/gi, "");
23
+ if (normalizedHex.length === 3) {
24
+ normalizedHex =
25
+ normalizedHex[0] +
26
+ normalizedHex[0] +
27
+ normalizedHex[1] +
28
+ normalizedHex[1] +
29
+ normalizedHex[2] +
30
+ normalizedHex[2];
31
+ } else if (normalizedHex.length !== 6) {
32
+ throw new Error(`Invalid color hex: ${normalizedHex}`);
27
33
  }
28
- let luminance;
34
+ let luminance: number;
29
35
  switch (luminanceChange) {
30
36
  case "light":
31
37
  luminance = -0.2;
@@ -43,19 +49,17 @@ export function changeColorLuminance(hex: string, luminanceChange: Luminance) {
43
49
  throw new Error(`Cannot change luminance to ${luminanceChange}`);
44
50
  }
45
51
 
46
- // Convert to decimal and change luminosity
47
52
  let rgb = "#";
48
53
  for (let i = 0; i < 3; i++) {
49
- const decimal = parseInt(hex.substr(i * 2, 2), 16);
54
+ const decimal = parseInt(normalizedHex.substr(i * 2, 2), 16);
50
55
  const appliedLuminance = Math.round(
51
56
  Math.min(Math.max(0, decimal + decimal * luminance), 255)
52
57
  ).toString(16);
53
- // 0 pad, if necessary.
54
58
  rgb += `00${appliedLuminance}`.substr(appliedLuminance.length);
55
59
  }
56
60
 
57
61
  return rgb;
58
- }
62
+ };
59
63
 
60
64
  class UnifierClass {
61
65
  private _web = false;
@@ -127,7 +131,7 @@ class UnifierClass {
127
131
  };
128
132
 
129
133
  storage = {
130
- getItem: async (key: string, defaultValue?: any) => {
134
+ getItem: async (key: string, defaultValue?: unknown) => {
131
135
  try {
132
136
  const jsonValue = await AsyncStorage.getItem(key);
133
137
  if (jsonValue) {
@@ -147,13 +151,13 @@ class UnifierClass {
147
151
  return defaultValue || null;
148
152
  }
149
153
  },
150
- setItem: async (key: string, item: any) => {
154
+ setItem: async (key: string, item: unknown) => {
151
155
  try {
152
156
  const jsonValue = JSON.stringify(item);
153
157
  await AsyncStorage.setItem(key, jsonValue);
154
- } catch (error: any) {
158
+ } catch (error: unknown) {
155
159
  console.error(`[storage] Error storing ${key}`, item, error);
156
- throw new Error(error);
160
+ throw new Error(error instanceof Error ? error.message : String(error));
157
161
  }
158
162
  },
159
163
  };
package/src/Utilities.tsx CHANGED
@@ -6,7 +6,10 @@ import {Platform} from "react-native";
6
6
  import type {APIError, BaseProfile, IconSize} from "./Common";
7
7
  import {COUNTY_AND_COUNTY_EQUIVALENT_ENTITIES} from "./Constants";
8
8
 
9
- export function mergeInlineStyles(inlineStyle?: any, newStyle?: any) {
9
+ export const mergeInlineStyles = (
10
+ inlineStyle?: {__style?: Record<string, unknown>} | undefined,
11
+ newStyle?: Record<string, unknown> | undefined
12
+ ) => {
10
13
  const inline = get(inlineStyle, "__style");
11
14
  return {
12
15
  __style: {
@@ -14,14 +17,14 @@ export function mergeInlineStyles(inlineStyle?: any, newStyle?: any) {
14
17
  ...newStyle,
15
18
  },
16
19
  };
17
- }
20
+ };
18
21
 
19
- export function isTestUser(profile?: BaseProfile) {
22
+ export const isTestUser = (profile?: BaseProfile) => {
20
23
  return (
21
24
  profile?.email &&
22
25
  (profile.email.indexOf("nang.io") > -1 || profile.email.indexOf("example.com") > -1)
23
26
  );
24
- }
27
+ };
25
28
 
26
29
  export const iconNumberToSize = (size = 16): IconSize => {
27
30
  let iconSize: IconSize;
@@ -104,7 +107,7 @@ export const toProps = ({
104
107
  className,
105
108
  inlineStyle,
106
109
  }: Style): {className: string; style: InlineStyle} => {
107
- const props: any = {};
110
+ const props: {className?: string; style?: InlineStyle} = {};
108
111
 
109
112
  if (className.size > 0) {
110
113
  // Sorting here ensures that classNames are always stable, reducing diff
@@ -117,7 +120,7 @@ export const toProps = ({
117
120
  props.style = inlineStyle;
118
121
  }
119
122
 
120
- return props;
123
+ return props as {className: string; style: InlineStyle};
121
124
  };
122
125
 
123
126
  /*
@@ -167,17 +170,17 @@ export const rangeWithoutZero =
167
170
  // Binds a string classname to the value in an object. Useful when interacting
168
171
  // with ranges that need to come dynamically from a style object. This is
169
172
  // similar to the NPM package 'classnames/bind'.
170
- export function bind<T>(
173
+ export const bind = <T,>(
171
174
  fn: Functor<T>,
172
175
  scope:
173
176
  | {
174
177
  readonly [key: string]: string;
175
178
  }
176
- | any
177
- ): (val: T) => Style {
179
+ | Record<string, string>
180
+ ): ((val: T) => Style) => {
178
181
  const map = mapClassName((name) => scope[name]);
179
182
  return (val: T): Style => map(fn(val));
180
- }
183
+ };
181
184
 
182
185
  // This takes a series of the previously defined functors, runs them all
183
186
  // against a value and returns the set of their classnames.
@@ -291,15 +294,13 @@ export const isValidGoogleApiKey = (apiKey: string): boolean => {
291
294
  return true;
292
295
  };
293
296
 
294
- export function formattedCountyCode(state: string, countyName: string): string {
295
- // Remove whitespace and convert to lowercase for comparison
297
+ export const formattedCountyCode = (state: string, countyName: string): string => {
296
298
  const stateKey = state
297
299
  .replace(/\s+/g, "")
298
300
  .toLowerCase() as keyof typeof COUNTY_AND_COUNTY_EQUIVALENT_ENTITIES;
299
301
 
300
302
  const stateData = COUNTY_AND_COUNTY_EQUIVALENT_ENTITIES[stateKey];
301
303
 
302
- // Remove whitespace, periods, apostrophes, and dashes for comparison
303
304
  const countyKey = countyName
304
305
  .trim()
305
306
  .toLowerCase()
@@ -314,16 +315,16 @@ export function formattedCountyCode(state: string, countyName: string): string {
314
315
  }
315
316
 
316
317
  return `${countyData.stateFP}${countyData.countyFP}`;
317
- }
318
+ };
318
319
 
319
- export function isAPIError(error: any): error is APIError {
320
- return error?.data?.title;
321
- }
320
+ export const isAPIError = (error: unknown): error is APIError => {
321
+ return Boolean((error as {data?: {title?: unknown}} | null | undefined)?.data?.title);
322
+ };
322
323
 
323
- export function printAPIError(error: APIError, details = true): string {
324
+ export const printAPIError = (error: APIError, details = true): string => {
324
325
  let message = error.data?.title;
325
326
  if (error.data?.detail && details) {
326
327
  message = `${message}: ${error.data?.detail}`;
327
328
  }
328
329
  return message;
329
- }
330
+ };
@@ -313,5 +313,143 @@ describe("WebAddressAutocomplete", () => {
313
313
  // The .catch path warns and falls back to plain TextField.
314
314
  expect(warnings.length).toBeGreaterThan(0);
315
315
  });
316
+
317
+ it("cleans up the global callback on unmount", async () => {
318
+ const {unmount} = renderWithTheme(
319
+ <WebAddressAutocomplete
320
+ googleMapsApiKey="my-api-key"
321
+ handleAddressChange={() => {}}
322
+ handleAutoCompleteChange={() => {}}
323
+ inputValue=""
324
+ />
325
+ );
326
+
327
+ const win = testGlobal.window as GoogleMapsWindow;
328
+ expect(win.initAutocomplete).toBeDefined();
329
+
330
+ unmount();
331
+
332
+ expect(win.initAutocomplete).toBeNull();
333
+ });
334
+
335
+ it("re-runs effect when googleMapsApiKey changes", async () => {
336
+ const handleAutoCompleteChange = mock((_arg: AddressInterface) => {});
337
+
338
+ renderWithTheme(
339
+ <WebAddressAutocomplete
340
+ googleMapsApiKey="key-1"
341
+ handleAddressChange={() => {}}
342
+ handleAutoCompleteChange={handleAutoCompleteChange}
343
+ inputValue=""
344
+ />
345
+ );
346
+
347
+ expect(appendedScripts.length).toBe(1);
348
+
349
+ // Simulate successful load for the second key
350
+ const win = testGlobal.window as GoogleMapsWindow;
351
+ const autocompleteConstructor = mock((_input: unknown, _opts: unknown) => ({
352
+ addListener: () => {},
353
+ getPlace: () => null,
354
+ }));
355
+ win.google = {
356
+ maps: {
357
+ places: {
358
+ Autocomplete: autocompleteConstructor,
359
+ },
360
+ },
361
+ };
362
+
363
+ await act(async () => {
364
+ (win.initAutocomplete as () => void)?.();
365
+ await new Promise((resolve) => setTimeout(resolve, 0));
366
+ });
367
+
368
+ expect(autocompleteConstructor).toHaveBeenCalled();
369
+ });
370
+
371
+ it("processes address components with includeCounty", async () => {
372
+ const handleAutoCompleteChange = mock((_arg: AddressInterface) => {});
373
+ let placeChangedCb: (() => void) | undefined;
374
+ let localPlaceResult: PlaceResult | null = null;
375
+
376
+ const autocompleteConstructor = mock((_input: unknown, _opts: unknown) => ({
377
+ addListener: (event: string, cb: () => void) => {
378
+ if (event === "place_changed") {
379
+ placeChangedCb = cb;
380
+ }
381
+ },
382
+ getPlace: () => localPlaceResult,
383
+ }));
384
+
385
+ testGlobal.window = {
386
+ google: {
387
+ maps: {
388
+ places: {
389
+ Autocomplete: autocompleteConstructor,
390
+ },
391
+ },
392
+ },
393
+ };
394
+
395
+ renderWithTheme(
396
+ <WebAddressAutocomplete
397
+ googleMapsApiKey="test-key"
398
+ handleAddressChange={() => {}}
399
+ handleAutoCompleteChange={handleAutoCompleteChange}
400
+ includeCounty
401
+ inputValue=""
402
+ />
403
+ );
404
+
405
+ await act(async () => {
406
+ await new Promise((resolve) => setTimeout(resolve, 0));
407
+ });
408
+
409
+ localPlaceResult = {
410
+ address_components: [
411
+ {long_name: "10", short_name: "10", types: ["street_number"]},
412
+ {long_name: "Main St", short_name: "Main St", types: ["route"]},
413
+ {long_name: "Springfield", short_name: "Springfield", types: ["locality"]},
414
+ {
415
+ long_name: "Sangamon County",
416
+ short_name: "Sangamon County",
417
+ types: ["administrative_area_level_2"],
418
+ },
419
+ {long_name: "Illinois", short_name: "IL", types: ["administrative_area_level_1"]},
420
+ {long_name: "62701", short_name: "62701", types: ["postal_code"]},
421
+ ],
422
+ };
423
+
424
+ await act(async () => {
425
+ placeChangedCb?.();
426
+ });
427
+
428
+ expect(handleAutoCompleteChange).toHaveBeenCalled();
429
+ });
430
+ });
431
+
432
+ describe("no API key behavior", () => {
433
+ it("sets scriptLoaded to false and renders plain TextField", async () => {
434
+ const handleAddressChange = mock(() => {});
435
+ const {UNSAFE_getAllByType} = renderWithTheme(
436
+ <WebAddressAutocomplete
437
+ handleAddressChange={handleAddressChange}
438
+ handleAutoCompleteChange={() => {}}
439
+ inputValue="test"
440
+ />
441
+ );
442
+
443
+ await act(async () => {
444
+ await new Promise((resolve) => setTimeout(resolve, 0));
445
+ });
446
+
447
+ const {TextInput} = require("react-native");
448
+ const inputs = UNSAFE_getAllByType(TextInput);
449
+ expect(inputs.length).toBeGreaterThan(0);
450
+ const {fireEvent: fe} = require("@testing-library/react-native");
451
+ fe.changeText(inputs[0], "new value");
452
+ expect(handleAddressChange).toHaveBeenCalledWith("new value");
453
+ });
316
454
  });
317
455
  });
@@ -141,6 +141,29 @@ describe("WebDropdownMenu", () => {
141
141
  expect(getByText("Placeholder").props.style.fontWeight).toBe("400");
142
142
  expect(getByText("Real").props.style.fontWeight).toBe("400");
143
143
  });
144
+
145
+ it("applies dynamic background via the Pressable style callback", () => {
146
+ const {getByTestId} = renderWithTheme(
147
+ <WebDropdownMenu
148
+ anchor={anchor}
149
+ onClose={() => {}}
150
+ onSelect={() => {}}
151
+ options={options}
152
+ selectedValue="b"
153
+ visible
154
+ />
155
+ );
156
+ const optionPressable = getByTestId("web_dropdown_option_a");
157
+ const styleFn = optionPressable.props.style;
158
+ expect(typeof styleFn).toBe("function");
159
+ const defaultStyle = styleFn({hovered: false, pressed: false});
160
+ const hoveredStyle = styleFn({hovered: true, pressed: false});
161
+ const pressedStyle = styleFn({hovered: false, pressed: true});
162
+ expect(defaultStyle.paddingHorizontal).toBe(12);
163
+ expect(defaultStyle.paddingVertical).toBe(10);
164
+ expect(hoveredStyle.backgroundColor).toBeDefined();
165
+ expect(pressedStyle.backgroundColor).toBeDefined();
166
+ });
144
167
  });
145
168
 
146
169
  describe("useWebDropdownAnchor", () => {
@@ -1117,7 +1117,9 @@ exports[`AddressField renders correctly with default props 1`] = `
1117
1117
  },
1118
1118
  ],
1119
1119
  "props": {
1120
- "style": undefined,
1120
+ "style": {
1121
+ "width": "100%",
1122
+ },
1121
1123
  "testID": undefined,
1122
1124
  },
1123
1125
  "type": "View",
@@ -45,9 +45,12 @@ exports[`Button renders correctly with default props 1`] = `
45
45
  ],
46
46
  "props": {
47
47
  "accessibilityHint": "Press to perform action",
48
- "aria-label": "Click me",
49
- "aria-role": "button",
50
- "disabled": undefined,
48
+ "accessibilityLabel": "Click me",
49
+ "accessibilityRole": "button",
50
+ "accessibilityState": {
51
+ "disabled": false,
52
+ },
53
+ "disabled": false,
51
54
  "onPress": [Function: debounced],
52
55
  "style": {
53
56
  "alignItems": "center",
@@ -64,7 +67,7 @@ exports[`Button renders correctly with default props 1`] = `
64
67
  },
65
68
  "testID": undefined,
66
69
  },
67
- "type": "Pressable",
70
+ "type": "PressableScale",
68
71
  }
69
72
  `;
70
73
 
@@ -113,9 +116,12 @@ exports[`Button renders primary variant 1`] = `
113
116
  ],
114
117
  "props": {
115
118
  "accessibilityHint": "Press to perform action",
116
- "aria-label": "Primary",
117
- "aria-role": "button",
118
- "disabled": undefined,
119
+ "accessibilityLabel": "Primary",
120
+ "accessibilityRole": "button",
121
+ "accessibilityState": {
122
+ "disabled": false,
123
+ },
124
+ "disabled": false,
119
125
  "onPress": [Function: debounced],
120
126
  "style": {
121
127
  "alignItems": "center",
@@ -132,7 +138,7 @@ exports[`Button renders primary variant 1`] = `
132
138
  },
133
139
  "testID": undefined,
134
140
  },
135
- "type": "Pressable",
141
+ "type": "PressableScale",
136
142
  }
137
143
  `;
138
144
 
@@ -181,9 +187,12 @@ exports[`Button renders secondary variant 1`] = `
181
187
  ],
182
188
  "props": {
183
189
  "accessibilityHint": "Press to perform action",
184
- "aria-label": "Secondary",
185
- "aria-role": "button",
186
- "disabled": undefined,
190
+ "accessibilityLabel": "Secondary",
191
+ "accessibilityRole": "button",
192
+ "accessibilityState": {
193
+ "disabled": false,
194
+ },
195
+ "disabled": false,
187
196
  "onPress": [Function: debounced],
188
197
  "style": {
189
198
  "alignItems": "center",
@@ -200,7 +209,7 @@ exports[`Button renders secondary variant 1`] = `
200
209
  },
201
210
  "testID": undefined,
202
211
  },
203
- "type": "Pressable",
212
+ "type": "PressableScale",
204
213
  }
205
214
  `;
206
215
 
@@ -249,9 +258,12 @@ exports[`Button renders muted variant 1`] = `
249
258
  ],
250
259
  "props": {
251
260
  "accessibilityHint": "Press to perform action",
252
- "aria-label": "Muted",
253
- "aria-role": "button",
254
- "disabled": undefined,
261
+ "accessibilityLabel": "Muted",
262
+ "accessibilityRole": "button",
263
+ "accessibilityState": {
264
+ "disabled": false,
265
+ },
266
+ "disabled": false,
255
267
  "onPress": [Function: debounced],
256
268
  "style": {
257
269
  "alignItems": "center",
@@ -268,7 +280,7 @@ exports[`Button renders muted variant 1`] = `
268
280
  },
269
281
  "testID": undefined,
270
282
  },
271
- "type": "Pressable",
283
+ "type": "PressableScale",
272
284
  }
273
285
  `;
274
286
 
@@ -317,9 +329,12 @@ exports[`Button renders outline variant 1`] = `
317
329
  ],
318
330
  "props": {
319
331
  "accessibilityHint": "Press to perform action",
320
- "aria-label": "Outline",
321
- "aria-role": "button",
322
- "disabled": undefined,
332
+ "accessibilityLabel": "Outline",
333
+ "accessibilityRole": "button",
334
+ "accessibilityState": {
335
+ "disabled": false,
336
+ },
337
+ "disabled": false,
323
338
  "onPress": [Function: debounced],
324
339
  "style": {
325
340
  "alignItems": "center",
@@ -336,7 +351,7 @@ exports[`Button renders outline variant 1`] = `
336
351
  },
337
352
  "testID": undefined,
338
353
  },
339
- "type": "Pressable",
354
+ "type": "PressableScale",
340
355
  }
341
356
  `;
342
357
 
@@ -385,9 +400,12 @@ exports[`Button renders destructive variant 1`] = `
385
400
  ],
386
401
  "props": {
387
402
  "accessibilityHint": "Press to perform action",
388
- "aria-label": "Delete",
389
- "aria-role": "button",
390
- "disabled": undefined,
403
+ "accessibilityLabel": "Delete",
404
+ "accessibilityRole": "button",
405
+ "accessibilityState": {
406
+ "disabled": false,
407
+ },
408
+ "disabled": false,
391
409
  "onPress": [Function: debounced],
392
410
  "style": {
393
411
  "alignItems": "center",
@@ -404,7 +422,7 @@ exports[`Button renders destructive variant 1`] = `
404
422
  },
405
423
  "testID": undefined,
406
424
  },
407
- "type": "Pressable",
425
+ "type": "PressableScale",
408
426
  }
409
427
  `;
410
428
 
@@ -453,8 +471,11 @@ exports[`Button renders disabled state 1`] = `
453
471
  ],
454
472
  "props": {
455
473
  "accessibilityHint": "Press to perform action",
456
- "aria-label": "Disabled",
457
- "aria-role": "button",
474
+ "accessibilityLabel": "Disabled",
475
+ "accessibilityRole": "button",
476
+ "accessibilityState": {
477
+ "disabled": true,
478
+ },
458
479
  "disabled": true,
459
480
  "onPress": [Function: debounced],
460
481
  "style": {
@@ -521,8 +542,11 @@ exports[`Button applies disabled styles when disabled 1`] = `
521
542
  ],
522
543
  "props": {
523
544
  "accessibilityHint": "Press to perform action",
524
- "aria-label": "Disabled",
525
- "aria-role": "button",
545
+ "accessibilityLabel": "Disabled",
546
+ "accessibilityRole": "button",
547
+ "accessibilityState": {
548
+ "disabled": true,
549
+ },
526
550
  "disabled": true,
527
551
  "onPress": [Function: debounced],
528
552
  "style": {
@@ -612,8 +636,11 @@ exports[`Button renders loading state 1`] = `
612
636
  ],
613
637
  "props": {
614
638
  "accessibilityHint": "Press to perform action",
615
- "aria-label": "Loading",
616
- "aria-role": "button",
639
+ "accessibilityLabel": "Loading",
640
+ "accessibilityRole": "button",
641
+ "accessibilityState": {
642
+ "disabled": true,
643
+ },
617
644
  "disabled": true,
618
645
  "onPress": [Function: debounced],
619
646
  "style": {
@@ -680,9 +707,12 @@ exports[`Button renders fullWidth button 1`] = `
680
707
  ],
681
708
  "props": {
682
709
  "accessibilityHint": "Press to perform action",
683
- "aria-label": "Full Width",
684
- "aria-role": "button",
685
- "disabled": undefined,
710
+ "accessibilityLabel": "Full Width",
711
+ "accessibilityRole": "button",
712
+ "accessibilityState": {
713
+ "disabled": false,
714
+ },
715
+ "disabled": false,
686
716
  "onPress": [Function: debounced],
687
717
  "style": {
688
718
  "alignItems": "center",
@@ -699,7 +729,7 @@ exports[`Button renders fullWidth button 1`] = `
699
729
  },
700
730
  "testID": undefined,
701
731
  },
702
- "type": "Pressable",
732
+ "type": "PressableScale",
703
733
  }
704
734
  `;
705
735
 
@@ -761,9 +791,12 @@ exports[`Button renders with icon on left 1`] = `
761
791
  ],
762
792
  "props": {
763
793
  "accessibilityHint": "Press to perform action",
764
- "aria-label": "With Icon",
765
- "aria-role": "button",
766
- "disabled": undefined,
794
+ "accessibilityLabel": "With Icon",
795
+ "accessibilityRole": "button",
796
+ "accessibilityState": {
797
+ "disabled": false,
798
+ },
799
+ "disabled": false,
767
800
  "onPress": [Function: debounced],
768
801
  "style": {
769
802
  "alignItems": "center",
@@ -780,7 +813,7 @@ exports[`Button renders with icon on left 1`] = `
780
813
  },
781
814
  "testID": undefined,
782
815
  },
783
- "type": "Pressable",
816
+ "type": "PressableScale",
784
817
  }
785
818
  `;
786
819
 
@@ -842,9 +875,12 @@ exports[`Button renders with icon on right 1`] = `
842
875
  ],
843
876
  "props": {
844
877
  "accessibilityHint": "Press to perform action",
845
- "aria-label": "Next",
846
- "aria-role": "button",
847
- "disabled": undefined,
878
+ "accessibilityLabel": "Next",
879
+ "accessibilityRole": "button",
880
+ "accessibilityState": {
881
+ "disabled": false,
882
+ },
883
+ "disabled": false,
848
884
  "onPress": [Function: debounced],
849
885
  "style": {
850
886
  "alignItems": "center",
@@ -861,7 +897,7 @@ exports[`Button renders with icon on right 1`] = `
861
897
  },
862
898
  "testID": undefined,
863
899
  },
864
- "type": "Pressable",
900
+ "type": "PressableScale",
865
901
  }
866
902
  `;
867
903
 
@@ -910,9 +946,12 @@ exports[`Button renders with confirmation modal props 1`] = `
910
946
  ],
911
947
  "props": {
912
948
  "accessibilityHint": "Opens a confirmation dialog",
913
- "aria-label": "Delete",
914
- "aria-role": "button",
915
- "disabled": undefined,
949
+ "accessibilityLabel": "Delete",
950
+ "accessibilityRole": "button",
951
+ "accessibilityState": {
952
+ "disabled": false,
953
+ },
954
+ "disabled": false,
916
955
  "onPress": [Function: debounced],
917
956
  "style": {
918
957
  "alignItems": "center",
@@ -929,7 +968,7 @@ exports[`Button renders with confirmation modal props 1`] = `
929
968
  },
930
969
  "testID": undefined,
931
970
  },
932
- "type": "Pressable",
971
+ "type": "PressableScale",
933
972
  }
934
973
  `;
935
974
 
@@ -978,9 +1017,12 @@ exports[`Button renders with tooltip on desktop (wrapped in Tooltip) 1`] = `
978
1017
  ],
979
1018
  "props": {
980
1019
  "accessibilityHint": "Press to perform action",
981
- "aria-label": "Hover me",
982
- "aria-role": "button",
983
- "disabled": undefined,
1020
+ "accessibilityLabel": "Hover me",
1021
+ "accessibilityRole": "button",
1022
+ "accessibilityState": {
1023
+ "disabled": false,
1024
+ },
1025
+ "disabled": false,
984
1026
  "onPress": [Function: debounced],
985
1027
  "style": {
986
1028
  "alignItems": "center",
@@ -997,6 +1039,6 @@ exports[`Button renders with tooltip on desktop (wrapped in Tooltip) 1`] = `
997
1039
  },
998
1040
  "testID": undefined,
999
1041
  },
1000
- "type": "Pressable",
1042
+ "type": "PressableScale",
1001
1043
  }
1002
1044
  `;