@getmicdrop/svelte-components 5.5.4 → 5.6.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 (512) hide show
  1. package/dist/calendar/AboutShow/AboutShow.spec.d.ts +2 -0
  2. package/dist/calendar/AboutShow/AboutShow.spec.d.ts.map +1 -0
  3. package/dist/calendar/AboutShow/AboutShow.spec.js +791 -0
  4. package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
  5. package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts +2 -0
  6. package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts.map +1 -0
  7. package/dist/calendar/Calendar/MiniMonthCalendar.spec.js +1191 -0
  8. package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
  9. package/dist/calendar/FAQs/FAQs.spec.d.ts +2 -0
  10. package/dist/calendar/FAQs/FAQs.spec.d.ts.map +1 -0
  11. package/dist/calendar/FAQs/FAQs.spec.js +238 -0
  12. package/dist/calendar/FAQs/FAQs.svelte +75 -75
  13. package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts +2 -0
  14. package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts.map +1 -0
  15. package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.js +420 -0
  16. package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
  17. package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts +2 -0
  18. package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts.map +1 -0
  19. package/dist/calendar/OrderSummary/OrderSummary.spec.js +808 -0
  20. package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
  21. package/dist/calendar/PublicCard/PublicCard.spec.d.ts +2 -0
  22. package/dist/calendar/PublicCard/PublicCard.spec.d.ts.map +1 -0
  23. package/dist/calendar/PublicCard/PublicCard.spec.js +301 -0
  24. package/dist/calendar/PublicCard/PublicCard.svelte +134 -134
  25. package/dist/calendar/ShowCard/ShowCard.spec.d.ts +2 -0
  26. package/dist/calendar/ShowCard/ShowCard.spec.d.ts.map +1 -0
  27. package/dist/calendar/ShowCard/ShowCard.spec.js +714 -0
  28. package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
  29. package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts +2 -0
  30. package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts.map +1 -0
  31. package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.js +241 -0
  32. package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
  33. package/dist/components/Layout/AppShell.svelte +104 -0
  34. package/dist/components/Layout/AppShell.svelte.d.ts +26 -0
  35. package/dist/components/Layout/AppShell.svelte.d.ts.map +1 -0
  36. package/dist/components/Layout/ContentSection.svelte +80 -0
  37. package/dist/components/Layout/ContentSection.svelte.d.ts +23 -0
  38. package/dist/components/Layout/ContentSection.svelte.d.ts.map +1 -0
  39. package/dist/components/Layout/Grid.svelte +4 -4
  40. package/dist/components/Layout/Heading.svelte +81 -0
  41. package/dist/components/Layout/Heading.svelte.d.ts +24 -0
  42. package/dist/components/Layout/Heading.svelte.d.ts.map +1 -0
  43. package/dist/components/Layout/PageContainer.svelte +69 -0
  44. package/dist/components/Layout/PageContainer.svelte.d.ts +23 -0
  45. package/dist/components/Layout/PageContainer.svelte.d.ts.map +1 -0
  46. package/dist/components/Layout/Responsive.svelte +75 -0
  47. package/dist/components/Layout/Responsive.svelte.d.ts +19 -0
  48. package/dist/components/Layout/Responsive.svelte.d.ts.map +1 -0
  49. package/dist/components/Layout/Section.spec.d.ts +2 -0
  50. package/dist/components/Layout/Section.spec.d.ts.map +1 -0
  51. package/dist/components/Layout/Section.spec.js +149 -0
  52. package/dist/components/Layout/Section.svelte +80 -80
  53. package/dist/components/Layout/ShowOnDesktop.svelte +37 -0
  54. package/dist/components/Layout/ShowOnDesktop.svelte.d.ts +16 -0
  55. package/dist/components/Layout/ShowOnDesktop.svelte.d.ts.map +1 -0
  56. package/dist/components/Layout/ShowOnMobile.svelte +37 -0
  57. package/dist/components/Layout/ShowOnMobile.svelte.d.ts +16 -0
  58. package/dist/components/Layout/ShowOnMobile.svelte.d.ts.map +1 -0
  59. package/dist/components/Layout/Sidebar.spec.d.ts +2 -0
  60. package/dist/components/Layout/Sidebar.spec.d.ts.map +1 -0
  61. package/dist/components/Layout/Sidebar.spec.js +186 -0
  62. package/dist/components/Layout/Sidebar.svelte +108 -108
  63. package/dist/components/Layout/Stack.spec.js +2 -2
  64. package/dist/components/Layout/Stack.svelte +6 -6
  65. package/dist/components/Layout/Text.svelte +87 -0
  66. package/dist/components/Layout/Text.svelte.d.ts +28 -0
  67. package/dist/components/Layout/Text.svelte.d.ts.map +1 -0
  68. package/dist/components/Layout/TwoColumn.svelte +108 -0
  69. package/dist/components/Layout/TwoColumn.svelte.d.ts +28 -0
  70. package/dist/components/Layout/TwoColumn.svelte.d.ts.map +1 -0
  71. package/dist/components/Layout/__tests__/Heading.test.d.ts +2 -0
  72. package/dist/components/Layout/__tests__/Heading.test.d.ts.map +1 -0
  73. package/dist/components/Layout/__tests__/Heading.test.js +123 -0
  74. package/dist/components/Layout/__tests__/ShowOnDesktop.test.d.ts +2 -0
  75. package/dist/components/Layout/__tests__/ShowOnDesktop.test.d.ts.map +1 -0
  76. package/dist/components/Layout/__tests__/ShowOnDesktop.test.js +84 -0
  77. package/dist/components/Layout/__tests__/ShowOnMobile.test.d.ts +2 -0
  78. package/dist/components/Layout/__tests__/ShowOnMobile.test.d.ts.map +1 -0
  79. package/dist/components/Layout/__tests__/ShowOnMobile.test.js +80 -0
  80. package/dist/components/Layout/__tests__/Text.test.d.ts +2 -0
  81. package/dist/components/Layout/__tests__/Text.test.d.ts.map +1 -0
  82. package/dist/components/Layout/__tests__/Text.test.js +146 -0
  83. package/dist/components/Layout/__tests__/TwoColumn.test.d.ts +2 -0
  84. package/dist/components/Layout/__tests__/TwoColumn.test.d.ts.map +1 -0
  85. package/dist/components/Layout/__tests__/TwoColumn.test.js +129 -0
  86. package/dist/constants/formOptions.spec.js +9 -5
  87. package/dist/constants/validation.js +91 -91
  88. package/dist/constants/validation.spec.js +64 -64
  89. package/dist/datetime/__tests__/timezone.test.js +123 -1
  90. package/dist/forms/createFieldTracker.spec.d.ts +2 -0
  91. package/dist/forms/createFieldTracker.spec.d.ts.map +1 -0
  92. package/dist/forms/createFieldTracker.spec.js +343 -0
  93. package/dist/forms/createFormStore.spec.d.ts +2 -0
  94. package/dist/forms/createFormStore.spec.d.ts.map +1 -0
  95. package/dist/forms/createFormStore.spec.js +689 -0
  96. package/dist/index.d.ts +4 -112
  97. package/dist/index.js +40 -226
  98. package/dist/patterns/data/DataGrid.spec.d.ts +2 -0
  99. package/dist/patterns/data/DataGrid.spec.d.ts.map +1 -0
  100. package/dist/patterns/data/DataGrid.spec.js +159 -0
  101. package/dist/patterns/data/DataGrid.svelte +45 -45
  102. package/dist/patterns/data/DataList.spec.d.ts +2 -0
  103. package/dist/patterns/data/DataList.spec.d.ts.map +1 -0
  104. package/dist/patterns/data/DataList.spec.js +158 -0
  105. package/dist/patterns/data/DataList.svelte +24 -24
  106. package/dist/patterns/data/DataTable.spec.d.ts +2 -0
  107. package/dist/patterns/data/DataTable.spec.d.ts.map +1 -0
  108. package/dist/patterns/data/DataTable.spec.js +196 -0
  109. package/dist/patterns/data/DataTable.svelte +36 -36
  110. package/dist/patterns/forms/FormActions.spec.js +95 -88
  111. package/dist/patterns/forms/FormActions.stories.svelte +97 -97
  112. package/dist/patterns/forms/FormActions.svelte +46 -46
  113. package/dist/patterns/forms/FormGrid.spec.d.ts +2 -0
  114. package/dist/patterns/forms/FormGrid.spec.d.ts.map +1 -0
  115. package/dist/patterns/forms/FormGrid.spec.js +125 -0
  116. package/dist/patterns/forms/FormGrid.svelte +33 -33
  117. package/dist/patterns/forms/FormSection.spec.d.ts +2 -0
  118. package/dist/patterns/forms/FormSection.spec.d.ts.map +1 -0
  119. package/dist/patterns/forms/FormSection.spec.js +153 -0
  120. package/dist/patterns/forms/FormSection.svelte +32 -32
  121. package/dist/patterns/forms/FormValidationSummary.stories.svelte +83 -83
  122. package/dist/patterns/forms/FormValidationSummary.svelte +74 -74
  123. package/dist/patterns/layout/Sidebar.spec.d.ts +2 -0
  124. package/dist/patterns/layout/Sidebar.spec.d.ts.map +1 -0
  125. package/dist/patterns/layout/Sidebar.spec.js +159 -0
  126. package/dist/patterns/layout/Sidebar.svelte +39 -39
  127. package/dist/patterns/layout/index.d.ts +9 -0
  128. package/dist/patterns/layout/index.js +22 -0
  129. package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
  130. package/dist/patterns/navigation/BottomNav.svelte +64 -64
  131. package/dist/patterns/navigation/Header.spec.js +33 -24
  132. package/dist/patterns/navigation/Header.stories.svelte +77 -77
  133. package/dist/patterns/navigation/Header.svelte +193 -193
  134. package/dist/patterns/page/PageHeader.spec.d.ts +2 -0
  135. package/dist/patterns/page/PageHeader.spec.d.ts.map +1 -0
  136. package/dist/patterns/page/PageHeader.spec.js +167 -0
  137. package/dist/patterns/page/PageHeader.svelte +18 -18
  138. package/dist/patterns/page/PageLayout.spec.d.ts +2 -0
  139. package/dist/patterns/page/PageLayout.spec.d.ts.map +1 -0
  140. package/dist/patterns/page/PageLayout.spec.js +145 -0
  141. package/dist/patterns/page/PageLayout.svelte +40 -40
  142. package/dist/patterns/page/PageLoader.spec.js +57 -54
  143. package/dist/patterns/page/PageLoader.stories.svelte +137 -137
  144. package/dist/patterns/page/PageLoader.svelte +24 -24
  145. package/dist/patterns/page/SectionHeader.spec.d.ts +2 -0
  146. package/dist/patterns/page/SectionHeader.spec.d.ts.map +1 -0
  147. package/dist/patterns/page/SectionHeader.spec.js +197 -0
  148. package/dist/patterns/page/SectionHeader.svelte +29 -29
  149. package/dist/presets/badges.js +112 -112
  150. package/dist/presets/badges.spec.d.ts +2 -0
  151. package/dist/presets/badges.spec.d.ts.map +1 -0
  152. package/dist/presets/badges.spec.js +172 -0
  153. package/dist/presets/buttons.js +76 -76
  154. package/dist/presets/buttons.spec.d.ts +2 -0
  155. package/dist/presets/buttons.spec.d.ts.map +1 -0
  156. package/dist/presets/buttons.spec.js +135 -0
  157. package/dist/presets/index.js +9 -9
  158. package/dist/primitives/Accordion/Accordion.spec.d.ts +2 -0
  159. package/dist/primitives/Accordion/Accordion.spec.d.ts.map +1 -0
  160. package/dist/primitives/Accordion/Accordion.spec.js +83 -0
  161. package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
  162. package/dist/primitives/Accordion/Accordion.svelte +42 -42
  163. package/dist/primitives/Accordion/AccordionItem.spec.d.ts +2 -0
  164. package/dist/primitives/Accordion/AccordionItem.spec.d.ts.map +1 -0
  165. package/dist/primitives/Accordion/AccordionItem.spec.js +661 -0
  166. package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
  167. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte +107 -0
  168. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts +35 -0
  169. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts.map +1 -0
  170. package/dist/primitives/Alert/Alert.spec.js +173 -170
  171. package/dist/primitives/Alert/Alert.stories.svelte +88 -88
  172. package/dist/primitives/Alert/Alert.svelte +27 -27
  173. package/dist/primitives/Avatar/Avatar.spec.d.ts +2 -0
  174. package/dist/primitives/Avatar/Avatar.spec.d.ts.map +1 -0
  175. package/dist/primitives/Avatar/Avatar.spec.js +211 -0
  176. package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
  177. package/dist/primitives/Avatar/Avatar.svelte +66 -66
  178. package/dist/primitives/Badges/Badge.spec.js +144 -103
  179. package/dist/primitives/Badges/Badge.stories.svelte +86 -86
  180. package/dist/primitives/Badges/Badge.svelte +79 -79
  181. package/dist/primitives/BottomSheet/BottomSheet.spec.js +136 -127
  182. package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
  183. package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
  184. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte +13 -0
  185. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts +7 -0
  186. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts.map +1 -0
  187. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +122 -120
  188. package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
  189. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
  190. package/dist/primitives/Button/Button.spec.js +223 -211
  191. package/dist/primitives/Button/Button.stories.svelte +76 -76
  192. package/dist/primitives/Button/Button.svelte +270 -270
  193. package/dist/primitives/Button/ButtonSaveDemo.spec.js +146 -48
  194. package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
  195. package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts +2 -0
  196. package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts.map +1 -0
  197. package/dist/primitives/Button/ButtonVariantShowcase.spec.js +202 -0
  198. package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
  199. package/dist/primitives/Card.spec.js +49 -49
  200. package/dist/primitives/Card.stories.svelte +22 -22
  201. package/dist/primitives/Card.svelte +28 -28
  202. package/dist/primitives/Checkbox/Checkbox.spec.d.ts +2 -0
  203. package/dist/primitives/Checkbox/Checkbox.spec.d.ts.map +1 -0
  204. package/dist/primitives/Checkbox/Checkbox.spec.js +252 -0
  205. package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
  206. package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
  207. package/dist/primitives/DarkModeToggle.spec.js +390 -357
  208. package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
  209. package/dist/primitives/DarkModeToggle.svelte +136 -136
  210. package/dist/primitives/Drawer/Drawer.spec.d.ts +2 -0
  211. package/dist/primitives/Drawer/Drawer.spec.d.ts.map +1 -0
  212. package/dist/primitives/Drawer/Drawer.spec.js +212 -0
  213. package/dist/primitives/Drawer/Drawer.stories.svelte +80 -80
  214. package/dist/primitives/Drawer/Drawer.svelte +120 -120
  215. package/dist/primitives/Dropdown/Dropdown.spec.d.ts +2 -0
  216. package/dist/primitives/Dropdown/Dropdown.spec.d.ts.map +1 -0
  217. package/dist/primitives/Dropdown/Dropdown.spec.js +366 -0
  218. package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
  219. package/dist/primitives/Dropdown/Dropdown.svelte +14 -14
  220. package/dist/primitives/Dropdown/DropdownItem.spec.d.ts +2 -0
  221. package/dist/primitives/Dropdown/DropdownItem.spec.d.ts.map +1 -0
  222. package/dist/primitives/Dropdown/DropdownItem.spec.js +182 -0
  223. package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
  224. package/dist/primitives/Icons/ArrowLeft.svelte +8 -8
  225. package/dist/primitives/Icons/ArrowRight.svelte +8 -8
  226. package/dist/primitives/Icons/Availability.svelte +14 -14
  227. package/dist/primitives/Icons/Back.svelte +14 -14
  228. package/dist/primitives/Icons/CheckCircle.svelte +6 -6
  229. package/dist/primitives/Icons/CheckCircleOutline.svelte +15 -15
  230. package/dist/primitives/Icons/ChevronLeft.svelte +4 -4
  231. package/dist/primitives/Icons/ChevronRight.svelte +4 -4
  232. package/dist/primitives/Icons/Copy.svelte +15 -15
  233. package/dist/primitives/Icons/Cross.svelte +5 -5
  234. package/dist/primitives/Icons/DownArrow.svelte +8 -8
  235. package/dist/primitives/Icons/ErrorCircle.svelte +6 -6
  236. package/dist/primitives/Icons/FacebookIcon.svelte +2 -2
  237. package/dist/primitives/Icons/Home.svelte +15 -15
  238. package/dist/primitives/Icons/Icon.spec.js +169 -169
  239. package/dist/primitives/Icons/Icon.stories.svelte +100 -100
  240. package/dist/primitives/Icons/Icon.svelte +52 -52
  241. package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
  242. package/dist/primitives/Icons/Info.svelte +7 -7
  243. package/dist/primitives/Icons/InstagramIcon.svelte +4 -4
  244. package/dist/primitives/Icons/LogoInstagram.svelte +2 -2
  245. package/dist/primitives/Icons/Message.svelte +15 -15
  246. package/dist/primitives/Icons/MoonIcon.svelte +5 -5
  247. package/dist/primitives/Icons/More.svelte +21 -21
  248. package/dist/primitives/Icons/MoreHori.spec.js +61 -61
  249. package/dist/primitives/Icons/MoreHori.svelte +22 -22
  250. package/dist/primitives/Icons/Notification.svelte +14 -14
  251. package/dist/primitives/Icons/Payment.svelte +14 -14
  252. package/dist/primitives/Icons/Profile.svelte +21 -21
  253. package/dist/primitives/Icons/Reload.svelte +29 -29
  254. package/dist/primitives/Icons/Shows.svelte +21 -21
  255. package/dist/primitives/Icons/Signout.svelte +21 -21
  256. package/dist/primitives/Icons/SunIcon.svelte +8 -8
  257. package/dist/primitives/Icons/TiktokIcon.svelte +2 -2
  258. package/dist/primitives/Icons/TwitterIcon.svelte +2 -2
  259. package/dist/primitives/Icons/WarningIcon.spec.js +18 -18
  260. package/dist/primitives/Icons/WarningIcon.svelte +5 -5
  261. package/dist/primitives/Icons/iconTestUtils.spec.d.ts +2 -0
  262. package/dist/primitives/Icons/iconTestUtils.spec.d.ts.map +1 -0
  263. package/dist/primitives/Icons/iconTestUtils.spec.js +235 -0
  264. package/dist/primitives/Input/Input.spec.js +573 -573
  265. package/dist/primitives/Input/Input.stories.svelte +139 -139
  266. package/dist/primitives/Input/Input.svelte +418 -431
  267. package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
  268. package/dist/primitives/Input/Select.spec.js +212 -218
  269. package/dist/primitives/Input/Select.stories.svelte +112 -112
  270. package/dist/primitives/Input/Select.svelte +128 -128
  271. package/dist/primitives/Input/Textarea.spec.d.ts +2 -0
  272. package/dist/primitives/Input/Textarea.spec.d.ts.map +1 -0
  273. package/dist/primitives/Input/Textarea.spec.js +255 -0
  274. package/dist/primitives/Input/Textarea.stories.svelte +137 -137
  275. package/dist/primitives/Input/Textarea.svelte +35 -35
  276. package/dist/primitives/Label/Label.spec.d.ts +2 -0
  277. package/dist/primitives/Label/Label.spec.d.ts.map +1 -0
  278. package/dist/primitives/Label/Label.spec.js +157 -0
  279. package/dist/primitives/Label/Label.svelte +37 -37
  280. package/dist/primitives/Modal/Modal.spec.js +99 -95
  281. package/dist/primitives/Modal/Modal.stories.svelte +86 -86
  282. package/dist/primitives/Modal/Modal.svelte +158 -158
  283. package/dist/primitives/Modal/ModalTestWrapper.svelte +65 -0
  284. package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts +23 -0
  285. package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts.map +1 -0
  286. package/dist/primitives/NumberInput/NumberInput.spec.d.ts +2 -0
  287. package/dist/primitives/NumberInput/NumberInput.spec.d.ts.map +1 -0
  288. package/dist/primitives/NumberInput/NumberInput.spec.js +235 -0
  289. package/dist/primitives/NumberInput/NumberInput.svelte +106 -106
  290. package/dist/primitives/Pagination/Pagination.spec.d.ts +2 -0
  291. package/dist/primitives/Pagination/Pagination.spec.d.ts.map +1 -0
  292. package/dist/primitives/Pagination/Pagination.spec.js +266 -0
  293. package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
  294. package/dist/primitives/Pagination/Pagination.svelte +261 -261
  295. package/dist/primitives/Radio/Radio.spec.d.ts +2 -0
  296. package/dist/primitives/Radio/Radio.spec.d.ts.map +1 -0
  297. package/dist/primitives/Radio/Radio.spec.js +206 -0
  298. package/dist/primitives/Radio/Radio.stories.svelte +80 -80
  299. package/dist/primitives/Radio/Radio.svelte +67 -67
  300. package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts +2 -0
  301. package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts.map +1 -0
  302. package/dist/primitives/Skeleton/CardPlaceholder.spec.js +156 -0
  303. package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
  304. package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts +2 -0
  305. package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts.map +1 -0
  306. package/dist/primitives/Skeleton/ImagePlaceholder.spec.js +120 -0
  307. package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
  308. package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts +2 -0
  309. package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts.map +1 -0
  310. package/dist/primitives/Skeleton/ListPlaceholder.spec.js +220 -0
  311. package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
  312. package/dist/primitives/Skeleton/Skeleton.spec.d.ts +2 -0
  313. package/dist/primitives/Skeleton/Skeleton.spec.d.ts.map +1 -0
  314. package/dist/primitives/Skeleton/Skeleton.spec.js +173 -0
  315. package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
  316. package/dist/primitives/Skeleton/Skeleton.svelte +26 -26
  317. package/dist/primitives/Spinner/Spinner.spec.js +71 -75
  318. package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
  319. package/dist/primitives/Spinner/Spinner.svelte +20 -20
  320. package/dist/primitives/Tabs/TabItem.spec.d.ts +2 -0
  321. package/dist/primitives/Tabs/TabItem.spec.d.ts.map +1 -0
  322. package/dist/primitives/Tabs/TabItem.spec.js +130 -0
  323. package/dist/primitives/Tabs/TabItem.svelte +49 -49
  324. package/dist/primitives/Tabs/Tabs.spec.d.ts +2 -0
  325. package/dist/primitives/Tabs/Tabs.spec.d.ts.map +1 -0
  326. package/dist/primitives/Tabs/Tabs.spec.js +295 -0
  327. package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
  328. package/dist/primitives/Tabs/Tabs.svelte +123 -123
  329. package/dist/primitives/Tabs/TabsWithItems.test.svelte +18 -0
  330. package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts +16 -0
  331. package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts.map +1 -0
  332. package/dist/primitives/Toggle.spec.js +143 -127
  333. package/dist/primitives/Toggle.stories.svelte +92 -92
  334. package/dist/primitives/Toggle.svelte +71 -71
  335. package/dist/primitives/Typography/Typography.spec.d.ts +2 -0
  336. package/dist/primitives/Typography/Typography.spec.d.ts.map +1 -0
  337. package/dist/primitives/Typography/Typography.spec.js +183 -0
  338. package/dist/primitives/Typography/Typography.svelte +53 -53
  339. package/dist/primitives/ValidationError.spec.js +103 -103
  340. package/dist/primitives/ValidationError.stories.svelte +69 -69
  341. package/dist/primitives/ValidationError.svelte +29 -29
  342. package/dist/primitives/index.d.ts +1 -0
  343. package/dist/primitives/index.js +84 -81
  344. package/dist/recipes/CropImage/CropImage.spec.js +208 -216
  345. package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
  346. package/dist/recipes/CropImage/CropImage.svelte +238 -238
  347. package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts +2 -0
  348. package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts.map +1 -0
  349. package/dist/recipes/ImageUploader/ImageUploader.spec.js +1351 -0
  350. package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
  351. package/dist/recipes/ImageUploader/ImageUploader.svelte +804 -804
  352. package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts +2 -0
  353. package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts.map +1 -0
  354. package/dist/recipes/SuperLogin/SuperLogin.spec.js +1436 -0
  355. package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
  356. package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts +2 -0
  357. package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts.map +1 -0
  358. package/dist/recipes/feedback/EmptyState/EmptyState.spec.js +202 -0
  359. package/dist/recipes/feedback/EmptyState/EmptyState.svelte +1 -1
  360. package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
  361. package/dist/recipes/feedback/ErrorDisplay.stories.svelte +101 -101
  362. package/dist/recipes/feedback/ErrorDisplay.svelte +1 -1
  363. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +133 -129
  364. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +157 -157
  365. package/dist/recipes/fields/CheckboxField.spec.d.ts +2 -0
  366. package/dist/recipes/fields/CheckboxField.spec.d.ts.map +1 -0
  367. package/dist/recipes/fields/CheckboxField.spec.js +135 -0
  368. package/dist/recipes/fields/CheckboxField.svelte +85 -85
  369. package/dist/recipes/fields/FormField.spec.d.ts +2 -0
  370. package/dist/recipes/fields/FormField.spec.d.ts.map +1 -0
  371. package/dist/recipes/fields/FormField.spec.js +159 -0
  372. package/dist/recipes/fields/FormField.svelte +58 -58
  373. package/dist/recipes/fields/RadioGroup.spec.d.ts +2 -0
  374. package/dist/recipes/fields/RadioGroup.spec.d.ts.map +1 -0
  375. package/dist/recipes/fields/RadioGroup.spec.js +199 -0
  376. package/dist/recipes/fields/RadioGroup.svelte +95 -95
  377. package/dist/recipes/fields/SelectField.spec.d.ts +2 -0
  378. package/dist/recipes/fields/SelectField.spec.d.ts.map +1 -0
  379. package/dist/recipes/fields/SelectField.spec.js +188 -0
  380. package/dist/recipes/fields/SelectField.svelte +80 -80
  381. package/dist/recipes/fields/TextareaField.spec.d.ts +2 -0
  382. package/dist/recipes/fields/TextareaField.spec.d.ts.map +1 -0
  383. package/dist/recipes/fields/TextareaField.spec.js +205 -0
  384. package/dist/recipes/fields/TextareaField.svelte +97 -97
  385. package/dist/recipes/fields/ToggleField.spec.d.ts +2 -0
  386. package/dist/recipes/fields/ToggleField.spec.d.ts.map +1 -0
  387. package/dist/recipes/fields/ToggleField.spec.js +153 -0
  388. package/dist/recipes/fields/ToggleField.svelte +60 -60
  389. package/dist/recipes/fields/index.js +7 -7
  390. package/dist/recipes/inputs/MultiSelect.spec.js +258 -257
  391. package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
  392. package/dist/recipes/inputs/MultiSelect.svelte +256 -249
  393. package/dist/recipes/inputs/MultiSelect.svelte.d.ts +2 -0
  394. package/dist/recipes/inputs/MultiSelect.svelte.d.ts.map +1 -1
  395. package/dist/recipes/inputs/OTPInput.spec.js +251 -238
  396. package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
  397. package/dist/recipes/inputs/OTPInput.svelte +29 -29
  398. package/dist/recipes/inputs/PasswordInput.spec.d.ts +2 -0
  399. package/dist/recipes/inputs/PasswordInput.spec.d.ts.map +1 -0
  400. package/dist/recipes/inputs/PasswordInput.spec.js +410 -0
  401. package/dist/recipes/inputs/PasswordInput.svelte +22 -22
  402. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +253 -173
  403. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +117 -117
  404. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte +71 -0
  405. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts +9 -0
  406. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts.map +1 -0
  407. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +1246 -300
  408. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +123 -123
  409. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +326 -326
  410. package/dist/recipes/inputs/Search.spec.d.ts +2 -0
  411. package/dist/recipes/inputs/Search.spec.d.ts.map +1 -0
  412. package/dist/recipes/inputs/Search.spec.js +177 -0
  413. package/dist/recipes/inputs/Search.svelte +37 -37
  414. package/dist/recipes/inputs/SelectDropdown.spec.d.ts +2 -0
  415. package/dist/recipes/inputs/SelectDropdown.spec.d.ts.map +1 -0
  416. package/dist/recipes/inputs/SelectDropdown.spec.js +512 -0
  417. package/dist/recipes/inputs/SelectDropdown.svelte +57 -57
  418. package/dist/recipes/modals/AlertModal.spec.d.ts +2 -0
  419. package/dist/recipes/modals/AlertModal.spec.d.ts.map +1 -0
  420. package/dist/recipes/modals/AlertModal.spec.js +432 -0
  421. package/dist/recipes/modals/AlertModal.svelte +130 -130
  422. package/dist/recipes/modals/ConfirmationModal.spec.js +206 -191
  423. package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
  424. package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
  425. package/dist/recipes/modals/InputModal.spec.d.ts +2 -0
  426. package/dist/recipes/modals/InputModal.spec.d.ts.map +1 -0
  427. package/dist/recipes/modals/InputModal.spec.js +872 -0
  428. package/dist/recipes/modals/InputModal.svelte +182 -182
  429. package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
  430. package/dist/recipes/modals/ModalStateManager.svelte +77 -77
  431. package/dist/recipes/modals/ModalTestWrapper.spec.d.ts +2 -0
  432. package/dist/recipes/modals/ModalTestWrapper.spec.d.ts.map +1 -0
  433. package/dist/recipes/modals/ModalTestWrapper.spec.js +502 -0
  434. package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
  435. package/dist/recipes/modals/StatusModal.spec.d.ts +2 -0
  436. package/dist/recipes/modals/StatusModal.spec.d.ts.map +1 -0
  437. package/dist/recipes/modals/StatusModal.spec.js +599 -0
  438. package/dist/recipes/modals/StatusModal.svelte +206 -206
  439. package/dist/services/EventService.js +75 -75
  440. package/dist/services/EventService.spec.js +217 -217
  441. package/dist/services/ShowService.spec.js +345 -342
  442. package/dist/stores/auth.js +36 -36
  443. package/dist/stores/auth.spec.js +139 -139
  444. package/dist/stores/toaster.js +13 -13
  445. package/dist/stories/ButtonAuditDashboard.spec.d.ts +2 -0
  446. package/dist/stories/ButtonAuditDashboard.spec.d.ts.map +1 -0
  447. package/dist/stories/ButtonAuditDashboard.spec.js +913 -0
  448. package/dist/stories/ButtonAuditReview.spec.d.ts +2 -0
  449. package/dist/stories/ButtonAuditReview.spec.d.ts.map +1 -0
  450. package/dist/stories/ButtonAuditReview.spec.js +422 -0
  451. package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
  452. package/dist/stories/ButtonAuditReview.svelte +427 -427
  453. package/dist/stories/ButtonGridView.spec.d.ts +2 -0
  454. package/dist/stories/ButtonGridView.spec.d.ts.map +1 -0
  455. package/dist/stories/ButtonGridView.spec.js +667 -0
  456. package/dist/stories/ButtonShowcase.spec.d.ts +2 -0
  457. package/dist/stories/ButtonShowcase.spec.d.ts.map +1 -0
  458. package/dist/stories/ButtonShowcase.spec.js +499 -0
  459. package/dist/stories/PatternsGallery.spec.d.ts +2 -0
  460. package/dist/stories/PatternsGallery.spec.d.ts.map +1 -0
  461. package/dist/stories/PatternsGallery.spec.js +514 -0
  462. package/dist/stories/PatternsGallery.stories.svelte +19 -19
  463. package/dist/stories/PatternsGallery.svelte +206 -206
  464. package/dist/stories/PrimitivesGallery.spec.d.ts +2 -0
  465. package/dist/stories/PrimitivesGallery.spec.d.ts.map +1 -0
  466. package/dist/stories/PrimitivesGallery.spec.js +813 -0
  467. package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
  468. package/dist/stories/PrimitivesGallery.svelte +725 -725
  469. package/dist/stories/RecipesGallery.spec.d.ts +2 -0
  470. package/dist/stories/RecipesGallery.spec.d.ts.map +1 -0
  471. package/dist/stories/RecipesGallery.spec.js +299 -0
  472. package/dist/stories/RecipesGallery.stories.svelte +19 -19
  473. package/dist/stories/RecipesGallery.svelte +271 -271
  474. package/dist/stories/button-audit-manifest.json +11186 -11186
  475. package/dist/stripe/useStripeTheme.spec.d.ts +2 -0
  476. package/dist/stripe/useStripeTheme.spec.d.ts.map +1 -0
  477. package/dist/stripe/useStripeTheme.spec.js +793 -0
  478. package/dist/tailwind/preset.cjs +82 -82
  479. package/dist/telemetry.d.ts.map +1 -1
  480. package/dist/telemetry.js +405 -404
  481. package/dist/telemetry.spec.js +1144 -661
  482. package/dist/tokens/__tests__/colors.test.d.ts +2 -0
  483. package/dist/tokens/__tests__/colors.test.d.ts.map +1 -0
  484. package/dist/tokens/__tests__/colors.test.js +152 -0
  485. package/dist/tokens/__tests__/radius.test.d.ts +2 -0
  486. package/dist/tokens/__tests__/radius.test.d.ts.map +1 -0
  487. package/dist/tokens/__tests__/radius.test.js +118 -0
  488. package/dist/tokens/__tests__/shadows.test.d.ts +2 -0
  489. package/dist/tokens/__tests__/shadows.test.d.ts.map +1 -0
  490. package/dist/tokens/__tests__/shadows.test.js +105 -0
  491. package/dist/tokens/__tests__/spacing.test.js +11 -8
  492. package/dist/tokens/__tests__/typography-base.test.d.ts +2 -0
  493. package/dist/tokens/__tests__/typography-base.test.d.ts.map +1 -0
  494. package/dist/tokens/__tests__/typography-base.test.js +138 -0
  495. package/dist/tokens/__tests__/typography.test.d.ts +2 -0
  496. package/dist/tokens/__tests__/typography.test.d.ts.map +1 -0
  497. package/dist/tokens/__tests__/typography.test.js +156 -0
  498. package/dist/tokens/__tests__/z-index.test.d.ts +2 -0
  499. package/dist/tokens/__tests__/z-index.test.d.ts.map +1 -0
  500. package/dist/tokens/__tests__/z-index.test.js +121 -0
  501. package/dist/tokens/tokens.css +87 -87
  502. package/dist/tokens/typography-base.css +163 -0
  503. package/dist/utils/apiConfig.spec.js +219 -118
  504. package/dist/utils/formatters.spec.d.ts +2 -0
  505. package/dist/utils/formatters.spec.d.ts.map +1 -0
  506. package/dist/utils/formatters.spec.js +82 -0
  507. package/dist/utils/transitions.js +62 -62
  508. package/dist/utils/transitions.spec.d.ts +2 -0
  509. package/dist/utils/transitions.spec.d.ts.map +1 -0
  510. package/dist/utils/transitions.spec.js +130 -0
  511. package/dist/utils/utils.js +354 -354
  512. package/package.json +292 -286
@@ -1,300 +1,1246 @@
1
- import { describe, it, expect, vi, beforeEach, beforeAll, afterEach } from 'vitest';
2
- import { render, fireEvent, waitFor, cleanup } from '@testing-library/svelte';
3
-
4
- // Mock the config
5
- vi.mock('../../lib/config.js', () => ({
6
- PUBLIC_GOOGLE_MAPS_API_KEY: 'mock-api-key',
7
- }));
8
-
9
- // Mock Google Maps API
10
- const mockAutocompleteSessionToken = vi.fn().mockImplementation(() => ({}));
11
- const mockFetchAutocompleteSuggestions = vi.fn().mockResolvedValue({
12
- suggestions: [
13
- {
14
- placePrediction: {
15
- text: { toString: () => 'Los Angeles, CA, USA' },
16
- types: ['locality', 'political'],
17
- toPlace: vi.fn().mockReturnValue({
18
- fetchFields: vi.fn().mockResolvedValue({}),
19
- toJSON: vi.fn().mockReturnValue({
20
- text: 'Los Angeles, CA, USA',
21
- formattedAddress: '123 Main St, Los Angeles, CA 90012',
22
- }),
23
- }),
24
- },
25
- },
26
- {
27
- placePrediction: {
28
- text: { toString: () => 'Long Beach, CA, USA' },
29
- types: ['locality', 'political'],
30
- toPlace: vi.fn().mockReturnValue({
31
- fetchFields: vi.fn().mockResolvedValue({}),
32
- toJSON: vi.fn().mockReturnValue({
33
- text: 'Long Beach, CA, USA',
34
- formattedAddress: '456 Beach St, Long Beach, CA 90801',
35
- }),
36
- }),
37
- },
38
- },
39
- ],
40
- });
41
-
42
- vi.mock('@googlemaps/js-api-loader', () => ({
43
- Loader: vi.fn().mockImplementation(() => ({
44
- importLibrary: vi.fn().mockResolvedValue({
45
- AutocompleteSessionToken: mockAutocompleteSessionToken,
46
- AutocompleteSuggestion: {
47
- fetchAutocompleteSuggestions: mockFetchAutocompleteSuggestions,
48
- },
49
- }),
50
- })),
51
- }));
52
-
53
- describe('PlaceAutocomplete', () => {
54
- let PlaceAutocomplete;
55
-
56
- beforeAll(async () => {
57
- vi.resetModules();
58
- PlaceAutocomplete = (await import('./PlaceAutocomplete.svelte')).default;
59
- }, 60000);
60
-
61
- beforeEach(() => {
62
- vi.clearAllMocks();
63
- });
64
-
65
- afterEach(() => {
66
- cleanup();
67
- });
68
-
69
- describe('Basic Rendering', () => {
70
- it('renders the component', async () => {
71
- const { container } = render(PlaceAutocomplete);
72
- expect(container.querySelector('.place-wrapper')).toBeDefined();
73
- });
74
-
75
- it('renders with default placeholder', async () => {
76
- const { container } = render(PlaceAutocomplete);
77
- const input = container.querySelector('input');
78
- expect(input?.placeholder).toContain('Search');
79
- });
80
-
81
- it('renders with custom placeholder', async () => {
82
- const { container } = render(PlaceAutocomplete, {
83
- props: { placeholder: 'Enter address' },
84
- });
85
- const input = container.querySelector('input');
86
- expect(input?.placeholder).toBe('Enter address');
87
- });
88
-
89
- it('renders search icon', async () => {
90
- const { container } = render(PlaceAutocomplete);
91
- expect(container.querySelector('svg')).toBeDefined();
92
- });
93
- });
94
-
95
- describe('Props', () => {
96
- it('accepts initialValue prop', async () => {
97
- const { container } = render(PlaceAutocomplete, {
98
- props: { initialValue: 'New York, NY' },
99
- });
100
- const input = container.querySelector('input');
101
- expect(input?.value).toBe('New York, NY');
102
- });
103
-
104
- it('accepts disabled prop', async () => {
105
- const { container } = render(PlaceAutocomplete, {
106
- props: { disabled: true },
107
- });
108
- const input = container.querySelector('input');
109
- expect(input?.disabled).toBe(true);
110
- });
111
-
112
- it('accepts mode prop for cityState', async () => {
113
- const { container } = render(PlaceAutocomplete, {
114
- props: { mode: 'cityState' },
115
- });
116
- const input = container.querySelector('input');
117
- expect(input?.placeholder).toContain('city, state');
118
- });
119
-
120
- it('accepts animateFocus prop', async () => {
121
- const { container } = render(PlaceAutocomplete, {
122
- props: { animateFocus: false },
123
- });
124
- expect(container.querySelector('.place-animate-focus')).toBeNull();
125
- });
126
-
127
- it('accepts autocomplete prop', async () => {
128
- const { container } = render(PlaceAutocomplete, {
129
- props: { autocomplete: 'off' },
130
- });
131
- const input = container.querySelector('input');
132
- expect(input?.autocomplete).toBe('off');
133
- });
134
- });
135
-
136
- describe('Input Interaction', () => {
137
- it('updates value on input', async () => {
138
- const { container } = render(PlaceAutocomplete);
139
- const input = container.querySelector('input');
140
-
141
- await fireEvent.input(input, { target: { value: 'Los' } });
142
- expect(input?.value).toBe('Los');
143
- });
144
-
145
- it('clears results on empty input', async () => {
146
- const { container } = render(PlaceAutocomplete);
147
- const input = container.querySelector('input');
148
-
149
- await fireEvent.input(input, { target: { value: 'Los' } });
150
- await fireEvent.input(input, { target: { value: '' } });
151
- expect(input?.value).toBe('');
152
- });
153
- });
154
-
155
- describe('Keyboard Navigation', () => {
156
- it('handles Escape key', async () => {
157
- const { container } = render(PlaceAutocomplete);
158
- const input = container.querySelector('input');
159
-
160
- await fireEvent.keyDown(input, { key: 'Escape' });
161
- expect(container).toBeDefined();
162
- });
163
-
164
- it('handles ArrowDown key with no results', async () => {
165
- const { container } = render(PlaceAutocomplete);
166
- const input = container.querySelector('input');
167
-
168
- await fireEvent.keyDown(input, { key: 'ArrowDown' });
169
- expect(container).toBeDefined();
170
- });
171
-
172
- it('handles ArrowUp key with no results', async () => {
173
- const { container } = render(PlaceAutocomplete);
174
- const input = container.querySelector('input');
175
-
176
- await fireEvent.keyDown(input, { key: 'ArrowUp' });
177
- expect(container).toBeDefined();
178
- });
179
-
180
- it('handles Enter key with no selection', async () => {
181
- const { container } = render(PlaceAutocomplete);
182
- const input = container.querySelector('input');
183
-
184
- await fireEvent.keyDown(input, { key: 'Enter' });
185
- expect(container).toBeDefined();
186
- });
187
- });
188
-
189
- describe('Callbacks', () => {
190
- it('calls onResponse when provided', async () => {
191
- const onResponse = vi.fn();
192
- const { container } = render(PlaceAutocomplete, {
193
- props: { onResponse },
194
- });
195
- expect(container).toBeDefined();
196
- });
197
-
198
- it('calls onError when provided', async () => {
199
- const onError = vi.fn();
200
- const { container } = render(PlaceAutocomplete, {
201
- props: { onError },
202
- });
203
- expect(container).toBeDefined();
204
- });
205
- });
206
-
207
- describe('Styling', () => {
208
- it('has correct wrapper class', async () => {
209
- const { container } = render(PlaceAutocomplete);
210
- expect(container.querySelector('.place-wrapper')).toBeDefined();
211
- });
212
-
213
- it('has correct container class', async () => {
214
- const { container } = render(PlaceAutocomplete);
215
- expect(container.querySelector('.place-container')).toBeDefined();
216
- });
217
-
218
- it('has correct input class', async () => {
219
- const { container } = render(PlaceAutocomplete);
220
- expect(container.querySelector('.place-input')).toBeDefined();
221
- });
222
-
223
- it('applies animate focus class when enabled', async () => {
224
- const { container } = render(PlaceAutocomplete, {
225
- props: { animateFocus: true, disabled: false },
226
- });
227
- expect(container.querySelector('.place-animate-focus')).toBeDefined();
228
- });
229
-
230
- it('does not apply animate focus class when disabled', async () => {
231
- const { container } = render(PlaceAutocomplete, {
232
- props: { animateFocus: true, disabled: true },
233
- });
234
- expect(container.querySelector('.place-animate-focus')).toBeNull();
235
- });
236
- });
237
-
238
- describe('Mode: cityState', () => {
239
- it('filters results to only localities', async () => {
240
- const { container } = render(PlaceAutocomplete, {
241
- props: { mode: 'cityState' },
242
- });
243
- expect(container).toBeDefined();
244
- });
245
-
246
- it('updates placeholder for cityState mode', async () => {
247
- const { container } = render(PlaceAutocomplete, {
248
- props: { mode: 'cityState' },
249
- });
250
- const input = container.querySelector('input');
251
- expect(input?.placeholder).toContain('city, state');
252
- });
253
- });
254
-
255
- describe('Error Handling', () => {
256
- it('handles missing API key gracefully', async () => {
257
- const onError = vi.fn();
258
- const { container } = render(PlaceAutocomplete, {
259
- props: { onError },
260
- });
261
- expect(container).toBeDefined();
262
- });
263
- });
264
-
265
- describe('Click Outside', () => {
266
- it('handles click outside to close dropdown', async () => {
267
- const { container } = render(PlaceAutocomplete);
268
-
269
- // Simulate click outside
270
- document.body.click();
271
-
272
- await waitFor(() => {
273
- expect(container.querySelector('.place-dropdown')).toBeNull();
274
- });
275
- });
276
- });
277
-
278
- describe('Initial Value', () => {
279
- it('sets initial value on mount', async () => {
280
- const { container } = render(PlaceAutocomplete, {
281
- props: { initialValue: 'San Francisco, CA' },
282
- });
283
-
284
- const input = container.querySelector('input');
285
- expect(input?.value).toBe('San Francisco, CA');
286
- });
287
-
288
- it('initializes only once', async () => {
289
- const { container, rerender } = render(PlaceAutocomplete, {
290
- props: { initialValue: 'San Francisco, CA' },
291
- });
292
-
293
- await rerender({ initialValue: 'Los Angeles, CA' });
294
-
295
- // Value should remain the initial value
296
- const input = container.querySelector('input');
297
- expect(input).toBeDefined();
298
- });
299
- });
300
- });
1
+ import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expect, describe, test, vi, beforeEach, afterEach } from 'vitest';
4
+ import PlaceAutocomplete from './PlaceAutocomplete.svelte';
5
+
6
+ // Mock Google Maps API
7
+ const mockAutocompleteSuggestion = {
8
+ fetchAutocompleteSuggestions: vi.fn(),
9
+ };
10
+
11
+ const mockAutocompleteSessionToken = vi.fn();
12
+
13
+ const mockLoader = {
14
+ importLibrary: vi.fn().mockResolvedValue({
15
+ AutocompleteSessionToken: mockAutocompleteSessionToken,
16
+ AutocompleteSuggestion: mockAutocompleteSuggestion,
17
+ }),
18
+ };
19
+
20
+ // Mock the Google Maps Loader
21
+ vi.mock('@googlemaps/js-api-loader', () => ({
22
+ Loader: vi.fn().mockImplementation(() => mockLoader),
23
+ }));
24
+
25
+ // Mock config
26
+ vi.mock('$lib/config.js', () => ({
27
+ PUBLIC_GOOGLE_MAPS_API_KEY: 'test-api-key',
28
+ }));
29
+
30
+ function setupTest(props = {}) {
31
+ const user = userEvent.setup();
32
+ const result = render(PlaceAutocomplete, { props });
33
+ return { user, container: result.container };
34
+ }
35
+
36
+ describe('PlaceAutocomplete Component', () => {
37
+ beforeEach(() => {
38
+ vi.clearAllMocks();
39
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
40
+ suggestions: [],
41
+ });
42
+ });
43
+
44
+ afterEach(() => {
45
+ vi.clearAllMocks();
46
+ });
47
+
48
+ describe('Rendering', () => {
49
+ test('renders input element', () => {
50
+ setupTest();
51
+ const input = screen.getByRole('textbox');
52
+ expect(input).toBeInTheDocument();
53
+ });
54
+
55
+ test('renders with default placeholder', async () => {
56
+ setupTest();
57
+ await waitFor(() => {
58
+ const input = screen.getByRole('textbox');
59
+ expect(input).toHaveAttribute('placeholder', 'Search for location...');
60
+ });
61
+ });
62
+
63
+ test('renders with custom placeholder', () => {
64
+ setupTest({ placeholder: 'Find a venue...' });
65
+ const input = screen.getByRole('textbox');
66
+ expect(input).toHaveAttribute('placeholder', 'Find a venue...');
67
+ });
68
+
69
+ test('renders search icon', () => {
70
+ const { container } = setupTest();
71
+ const icon = container.querySelector('svg');
72
+ expect(icon).toBeInTheDocument();
73
+ });
74
+
75
+ test('applies wrapper styles correctly', () => {
76
+ const { container } = setupTest();
77
+ const wrapper = container.querySelector('.relative.rounded-lg');
78
+ expect(wrapper).toBeInTheDocument();
79
+ });
80
+
81
+ test('input has correct name attribute', () => {
82
+ setupTest();
83
+ const input = screen.getByRole('textbox');
84
+ expect(input).toHaveAttribute('name', 'location');
85
+ });
86
+ });
87
+
88
+ describe('Initial Value', () => {
89
+ test('displays initial value', async () => {
90
+ setupTest({ initialValue: 'New York, NY' });
91
+ await waitFor(() => {
92
+ const input = screen.getByRole('textbox');
93
+ expect(input.value).toBe('New York, NY');
94
+ });
95
+ });
96
+
97
+ test('does not display value when initialValue is empty', () => {
98
+ setupTest({ initialValue: '' });
99
+ const input = screen.getByRole('textbox');
100
+ expect(input.value).toBe('');
101
+ });
102
+ });
103
+
104
+ describe('Disabled State', () => {
105
+ test('input can be disabled', () => {
106
+ setupTest({ disabled: true });
107
+ const input = screen.getByRole('textbox');
108
+ expect(input).toBeDisabled();
109
+ });
110
+
111
+ test('input is enabled by default', () => {
112
+ setupTest();
113
+ const input = screen.getByRole('textbox');
114
+ expect(input).not.toBeDisabled();
115
+ });
116
+
117
+ test('applies disabled opacity class', () => {
118
+ setupTest({ disabled: true });
119
+ const input = screen.getByRole('textbox');
120
+ expect(input).toHaveClass('disabled:opacity-50');
121
+ });
122
+ });
123
+
124
+ describe('Animation', () => {
125
+ test('applies animation class when animateFocus is true', () => {
126
+ const { container } = setupTest({ animateFocus: true });
127
+ const wrapper = container.querySelector('.transition-transform');
128
+ expect(wrapper).toBeInTheDocument();
129
+ expect(wrapper).toHaveClass('focus-within:scale-[1.01]');
130
+ });
131
+
132
+ test('does not apply animation when animateFocus is false', () => {
133
+ const { container } = setupTest({ animateFocus: false });
134
+ const wrapper = container.querySelector('.transition-transform');
135
+ expect(wrapper).not.toBeInTheDocument();
136
+ });
137
+
138
+ test('does not apply animation when disabled', () => {
139
+ const { container } = setupTest({ animateFocus: true, disabled: true });
140
+ const wrapper = container.querySelector('.transition-transform');
141
+ expect(wrapper).not.toBeInTheDocument();
142
+ });
143
+ });
144
+
145
+ describe('Mode Variants', () => {
146
+ test('default mode is full', () => {
147
+ setupTest();
148
+ const input = screen.getByRole('textbox');
149
+ expect(input).toHaveAttribute('placeholder', 'Search for location...');
150
+ });
151
+
152
+ test('cityState mode updates placeholder', async () => {
153
+ setupTest({ mode: 'cityState' });
154
+ await waitFor(() => {
155
+ const input = screen.getByRole('textbox');
156
+ expect(input).toHaveAttribute('placeholder', 'Search for city, state...');
157
+ });
158
+ });
159
+
160
+ test('cityState mode with custom placeholder keeps custom value', () => {
161
+ setupTest({ mode: 'cityState', placeholder: 'Custom placeholder' });
162
+ const input = screen.getByRole('textbox');
163
+ expect(input).toHaveAttribute('placeholder', 'Custom placeholder');
164
+ });
165
+ });
166
+
167
+ describe('Autocomplete Attribute', () => {
168
+ test('has autocomplete off by default', () => {
169
+ setupTest();
170
+ const input = screen.getByRole('textbox');
171
+ expect(input).toHaveAttribute('autocomplete', 'off');
172
+ });
173
+
174
+ test('can set autocomplete to on', () => {
175
+ setupTest({ autocomplete: 'on' });
176
+ const input = screen.getByRole('textbox');
177
+ expect(input).toHaveAttribute('autocomplete', 'on');
178
+ });
179
+ });
180
+
181
+ describe('User Input', () => {
182
+ test('updates input value on typing', async () => {
183
+ const { user } = setupTest();
184
+ const input = screen.getByRole('textbox');
185
+
186
+ await user.type(input, 'Los Angeles');
187
+ expect(input.value).toBe('Los Angeles');
188
+ });
189
+
190
+ test('clears input when empty value is typed', async () => {
191
+ const { user } = setupTest({ initialValue: 'Test' });
192
+ const input = screen.getByRole('textbox');
193
+
194
+ await user.clear(input);
195
+ expect(input.value).toBe('');
196
+ });
197
+ });
198
+
199
+ describe('Autocomplete Suggestions', () => {
200
+ test('displays suggestions when typing', async () => {
201
+ const mockSuggestions = [
202
+ {
203
+ placePrediction: {
204
+ text: { toString: () => 'Los Angeles, CA, USA' },
205
+ toPlace: () => ({ fetchFields: vi.fn() }),
206
+ },
207
+ },
208
+ {
209
+ placePrediction: {
210
+ text: { toString: () => 'Los Angeles County, CA, USA' },
211
+ toPlace: () => ({ fetchFields: vi.fn() }),
212
+ },
213
+ },
214
+ ];
215
+
216
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
217
+ suggestions: mockSuggestions,
218
+ });
219
+
220
+ const { user } = setupTest();
221
+ const input = screen.getByRole('textbox');
222
+
223
+ await user.type(input, 'Los Angeles');
224
+
225
+ await waitFor(() => {
226
+ const options = screen.getByRole('list', { id: 'options' });
227
+ expect(options).toBeInTheDocument();
228
+ });
229
+ });
230
+
231
+ test('does not display suggestions list when results are empty', async () => {
232
+ const { user } = setupTest();
233
+ const input = screen.getByRole('textbox');
234
+
235
+ await user.type(input, 'xyz');
236
+
237
+ const options = screen.queryByRole('list');
238
+ expect(options).not.toBeInTheDocument();
239
+ });
240
+
241
+ test('filters cityState results correctly', async () => {
242
+ const mockSuggestions = [
243
+ {
244
+ placePrediction: {
245
+ text: { toString: () => 'Los Angeles, CA, USA' },
246
+ types: ['locality', 'political'],
247
+ toPlace: () => ({ fetchFields: vi.fn() }),
248
+ },
249
+ },
250
+ {
251
+ placePrediction: {
252
+ text: { toString: () => 'California, USA' },
253
+ types: ['administrative_area_level_1', 'political'],
254
+ toPlace: () => ({ fetchFields: vi.fn() }),
255
+ },
256
+ },
257
+ {
258
+ placePrediction: {
259
+ text: { toString: () => 'Van Nuys, Los Angeles, CA, USA' },
260
+ types: ['sublocality', 'political'],
261
+ toPlace: () => ({ fetchFields: vi.fn() }),
262
+ },
263
+ },
264
+ ];
265
+
266
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
267
+ suggestions: mockSuggestions,
268
+ });
269
+
270
+ const { user } = setupTest({ mode: 'cityState' });
271
+ const input = screen.getByRole('textbox');
272
+
273
+ await user.type(input, 'Los Angeles');
274
+
275
+ await waitFor(() => {
276
+ const buttons = screen.getAllByRole('button');
277
+ // Should have 2 results: Los Angeles and Van Nuys (not California)
278
+ expect(buttons).toHaveLength(2);
279
+ });
280
+ });
281
+
282
+ test('formats cityState results to show first two parts', async () => {
283
+ const mockSuggestions = [
284
+ {
285
+ placePrediction: {
286
+ text: { toString: () => 'Los Angeles, CA, USA' },
287
+ types: ['locality'],
288
+ toPlace: () => ({ fetchFields: vi.fn() }),
289
+ },
290
+ },
291
+ ];
292
+
293
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
294
+ suggestions: mockSuggestions,
295
+ });
296
+
297
+ const { user } = setupTest({ mode: 'cityState' });
298
+ const input = screen.getByRole('textbox');
299
+
300
+ await user.type(input, 'Los Angeles');
301
+
302
+ await waitFor(() => {
303
+ const button = screen.getByText('Los Angeles, CA');
304
+ expect(button).toBeInTheDocument();
305
+ });
306
+ });
307
+ });
308
+
309
+ describe('Keyboard Navigation', () => {
310
+ beforeEach(() => {
311
+ const mockSuggestions = [
312
+ {
313
+ placePrediction: {
314
+ text: { toString: () => 'Result 1' },
315
+ toPlace: () => ({
316
+ fetchFields: vi.fn().mockResolvedValue({}),
317
+ toJSON: () => ({ text: 'Result 1' }),
318
+ }),
319
+ },
320
+ },
321
+ {
322
+ placePrediction: {
323
+ text: { toString: () => 'Result 2' },
324
+ toPlace: () => ({
325
+ fetchFields: vi.fn().mockResolvedValue({}),
326
+ toJSON: () => ({ text: 'Result 2' }),
327
+ }),
328
+ },
329
+ },
330
+ {
331
+ placePrediction: {
332
+ text: { toString: () => 'Result 3' },
333
+ toPlace: () => ({
334
+ fetchFields: vi.fn().mockResolvedValue({}),
335
+ toJSON: () => ({ text: 'Result 3' }),
336
+ }),
337
+ },
338
+ },
339
+ ];
340
+
341
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
342
+ suggestions: mockSuggestions,
343
+ });
344
+ });
345
+
346
+ test('ArrowDown highlights first suggestion', async () => {
347
+ const { user, container } = setupTest();
348
+ const input = screen.getByRole('textbox');
349
+
350
+ await user.type(input, 'Test');
351
+
352
+ await waitFor(() => {
353
+ expect(screen.getByRole('list')).toBeInTheDocument();
354
+ });
355
+
356
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
357
+
358
+ await waitFor(() => {
359
+ const highlighted = container.querySelector('.bg-gray-100');
360
+ expect(highlighted).toBeInTheDocument();
361
+ });
362
+ });
363
+
364
+ test('ArrowDown cycles through suggestions', async () => {
365
+ const { user, container } = setupTest();
366
+ const input = screen.getByRole('textbox');
367
+
368
+ await user.type(input, 'Test');
369
+
370
+ await waitFor(() => {
371
+ expect(screen.getByRole('list')).toBeInTheDocument();
372
+ });
373
+
374
+ // Press down three times
375
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
376
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
377
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
378
+
379
+ // Should cycle back to first
380
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
381
+
382
+ const highlighted = container.querySelectorAll('.bg-gray-100');
383
+ expect(highlighted.length).toBeGreaterThan(0);
384
+ });
385
+
386
+ test('ArrowUp highlights last suggestion when none selected', async () => {
387
+ const { user, container } = setupTest();
388
+ const input = screen.getByRole('textbox');
389
+
390
+ await user.type(input, 'Test');
391
+
392
+ await waitFor(() => {
393
+ expect(screen.getByRole('list')).toBeInTheDocument();
394
+ });
395
+
396
+ await fireEvent.keyDown(input, { key: 'ArrowUp' });
397
+
398
+ const highlighted = container.querySelector('.bg-gray-100');
399
+ expect(highlighted).toBeInTheDocument();
400
+ });
401
+
402
+ test('ArrowUp moves selection upward', async () => {
403
+ const { user, container } = setupTest();
404
+ const input = screen.getByRole('textbox');
405
+
406
+ await user.type(input, 'Test');
407
+
408
+ await waitFor(() => {
409
+ expect(screen.getByRole('list')).toBeInTheDocument();
410
+ });
411
+
412
+ // Go down twice, then up once
413
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
414
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
415
+ await fireEvent.keyDown(input, { key: 'ArrowUp' });
416
+
417
+ // Should be on first suggestion - check the li element
418
+ await waitFor(() => {
419
+ const highlighted = container.querySelector('li.bg-gray-100');
420
+ expect(highlighted).toBeInTheDocument();
421
+ });
422
+ });
423
+
424
+ test('Enter selects highlighted suggestion', async () => {
425
+ const onResponse = vi.fn();
426
+ const { user } = setupTest({ onResponse });
427
+ const input = screen.getByRole('textbox');
428
+
429
+ await user.type(input, 'Test');
430
+
431
+ await waitFor(() => {
432
+ expect(screen.getByRole('list')).toBeInTheDocument();
433
+ });
434
+
435
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
436
+ await fireEvent.keyDown(input, { key: 'Enter' });
437
+
438
+ await waitFor(() => {
439
+ expect(onResponse).toHaveBeenCalled();
440
+ });
441
+ });
442
+
443
+ test('Enter does nothing when no suggestion is highlighted', async () => {
444
+ const onResponse = vi.fn();
445
+ const { user } = setupTest({ onResponse });
446
+ const input = screen.getByRole('textbox');
447
+
448
+ await user.type(input, 'Test');
449
+
450
+ await waitFor(() => {
451
+ expect(screen.getByRole('list')).toBeInTheDocument();
452
+ });
453
+
454
+ await fireEvent.keyDown(input, { key: 'Enter' });
455
+
456
+ expect(onResponse).not.toHaveBeenCalled();
457
+ });
458
+
459
+ test('Escape clears results', async () => {
460
+ const { user } = setupTest();
461
+ const input = screen.getByRole('textbox');
462
+
463
+ await user.type(input, 'Test');
464
+
465
+ await waitFor(() => {
466
+ expect(screen.getByRole('list')).toBeInTheDocument();
467
+ });
468
+
469
+ await fireEvent.keyDown(input, { key: 'Escape' });
470
+
471
+ await waitFor(() => {
472
+ expect(screen.queryByRole('list')).not.toBeInTheDocument();
473
+ });
474
+ });
475
+
476
+ test('keyboard navigation does nothing when no results', async () => {
477
+ const { user } = setupTest();
478
+ const input = screen.getByRole('textbox');
479
+
480
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
481
+ await fireEvent.keyDown(input, { key: 'ArrowUp' });
482
+ await fireEvent.keyDown(input, { key: 'Enter' });
483
+
484
+ // Should not throw error
485
+ expect(input).toBeInTheDocument();
486
+ });
487
+ });
488
+
489
+ describe('Place Selection', () => {
490
+ test('clicking suggestion calls onResponse', async () => {
491
+ const onResponse = vi.fn();
492
+ const mockPlace = {
493
+ fetchFields: vi.fn().mockResolvedValue({}),
494
+ toJSON: () => ({ formattedAddress: '123 Main St', text: 'Test Place' }),
495
+ };
496
+
497
+ const mockSuggestions = [
498
+ {
499
+ placePrediction: {
500
+ text: { toString: () => 'Test Place' },
501
+ toPlace: () => mockPlace,
502
+ },
503
+ },
504
+ ];
505
+
506
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
507
+ suggestions: mockSuggestions,
508
+ });
509
+
510
+ const { user } = setupTest({ onResponse });
511
+ const input = screen.getByRole('textbox');
512
+
513
+ await user.type(input, 'Test');
514
+
515
+ await waitFor(() => {
516
+ expect(screen.getByRole('list')).toBeInTheDocument();
517
+ });
518
+
519
+ const button = screen.getByRole('button', { name: /Test Place/i });
520
+ await fireEvent.click(button);
521
+
522
+ await waitFor(() => {
523
+ expect(onResponse).toHaveBeenCalledWith(
524
+ expect.objectContaining({ text: 'Test Place' })
525
+ );
526
+ });
527
+ });
528
+
529
+ test('selecting place updates input value', async () => {
530
+ const mockPlace = {
531
+ fetchFields: vi.fn().mockResolvedValue({}),
532
+ toJSON: () => ({ text: 'Selected Place' }),
533
+ };
534
+
535
+ const mockSuggestions = [
536
+ {
537
+ placePrediction: {
538
+ text: { toString: () => 'Selected Place' },
539
+ toPlace: () => mockPlace,
540
+ },
541
+ },
542
+ ];
543
+
544
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
545
+ suggestions: mockSuggestions,
546
+ });
547
+
548
+ const { user } = setupTest();
549
+ const input = screen.getByRole('textbox');
550
+
551
+ await user.type(input, 'Test');
552
+
553
+ await waitFor(() => {
554
+ expect(screen.getByRole('list')).toBeInTheDocument();
555
+ });
556
+
557
+ const button = screen.getByRole('button');
558
+ await fireEvent.click(button);
559
+
560
+ await waitFor(() => {
561
+ expect(input.value).toBe('Selected Place');
562
+ });
563
+ });
564
+
565
+ test('selecting place clears results list', async () => {
566
+ const mockPlace = {
567
+ fetchFields: vi.fn().mockResolvedValue({}),
568
+ toJSON: () => ({ text: 'Selected Place' }),
569
+ };
570
+
571
+ const mockSuggestions = [
572
+ {
573
+ placePrediction: {
574
+ text: { toString: () => 'Selected Place' },
575
+ toPlace: () => mockPlace,
576
+ },
577
+ },
578
+ ];
579
+
580
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
581
+ suggestions: mockSuggestions,
582
+ });
583
+
584
+ const { user } = setupTest();
585
+ const input = screen.getByRole('textbox');
586
+
587
+ await user.type(input, 'Test');
588
+
589
+ await waitFor(() => {
590
+ expect(screen.getByRole('list')).toBeInTheDocument();
591
+ });
592
+
593
+ const button = screen.getByRole('button');
594
+ await fireEvent.click(button);
595
+
596
+ await waitFor(() => {
597
+ expect(screen.queryByRole('list')).not.toBeInTheDocument();
598
+ });
599
+ });
600
+
601
+ test('uses originalText for cityState mode', async () => {
602
+ const onResponse = vi.fn();
603
+ const mockPlace = {
604
+ fetchFields: vi.fn().mockResolvedValue({}),
605
+ toJSON: () => ({ text: 'Los Angeles, CA' }),
606
+ };
607
+
608
+ const mockSuggestions = [
609
+ {
610
+ placePrediction: {
611
+ text: { toString: () => 'Los Angeles, CA, USA' },
612
+ types: ['locality'],
613
+ toPlace: () => mockPlace,
614
+ },
615
+ },
616
+ ];
617
+
618
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
619
+ suggestions: mockSuggestions,
620
+ });
621
+
622
+ const { user } = setupTest({ mode: 'cityState', onResponse });
623
+ const input = screen.getByRole('textbox');
624
+
625
+ await user.type(input, 'Los Angeles');
626
+
627
+ await waitFor(() => {
628
+ expect(screen.getByRole('list')).toBeInTheDocument();
629
+ });
630
+
631
+ const button = screen.getByRole('button');
632
+ await fireEvent.click(button);
633
+
634
+ await waitFor(() => {
635
+ expect(input.value).toBe('Los Angeles, CA');
636
+ });
637
+ });
638
+ });
639
+
640
+ describe('Click Outside', () => {
641
+ test('clicking outside clears results', async () => {
642
+ const mockSuggestions = [
643
+ {
644
+ placePrediction: {
645
+ text: { toString: () => 'Test Place' },
646
+ toPlace: () => ({ fetchFields: vi.fn() }),
647
+ },
648
+ },
649
+ ];
650
+
651
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
652
+ suggestions: mockSuggestions,
653
+ });
654
+
655
+ const { user } = setupTest();
656
+ const input = screen.getByRole('textbox');
657
+
658
+ await user.type(input, 'Test');
659
+
660
+ await waitFor(() => {
661
+ expect(screen.getByRole('list')).toBeInTheDocument();
662
+ });
663
+
664
+ // Click outside
665
+ await fireEvent.click(document.body);
666
+
667
+ await waitFor(() => {
668
+ expect(screen.queryByRole('list')).not.toBeInTheDocument();
669
+ });
670
+ });
671
+
672
+ test('clicking inside does not clear results', async () => {
673
+ const mockSuggestions = [
674
+ {
675
+ placePrediction: {
676
+ text: { toString: () => 'Test Place' },
677
+ toPlace: () => ({ fetchFields: vi.fn() }),
678
+ },
679
+ },
680
+ ];
681
+
682
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
683
+ suggestions: mockSuggestions,
684
+ });
685
+
686
+ const { user, container } = setupTest();
687
+ const input = screen.getByRole('textbox');
688
+
689
+ await user.type(input, 'Test');
690
+
691
+ await waitFor(() => {
692
+ expect(screen.getByRole('list')).toBeInTheDocument();
693
+ });
694
+
695
+ // Click on the input itself
696
+ await fireEvent.click(input);
697
+
698
+ // Results should still be visible
699
+ expect(screen.getByRole('list')).toBeInTheDocument();
700
+ });
701
+ });
702
+
703
+ describe('Error Handling', () => {
704
+ test('calls onError when API request fails', async () => {
705
+ const onError = vi.fn();
706
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockRejectedValue(
707
+ new Error('API Error')
708
+ );
709
+
710
+ const { user } = setupTest({ onError });
711
+ const input = screen.getByRole('textbox');
712
+
713
+ await user.type(input, 'Test');
714
+
715
+ await waitFor(() => {
716
+ expect(onError).toHaveBeenCalledWith(expect.stringContaining('API Error'));
717
+ });
718
+ });
719
+
720
+ test('calls onError when place selection fails', async () => {
721
+ const onError = vi.fn();
722
+ const mockPlace = {
723
+ fetchFields: vi.fn().mockRejectedValue(new Error('Fetch Error')),
724
+ };
725
+
726
+ const mockSuggestions = [
727
+ {
728
+ placePrediction: {
729
+ text: { toString: () => 'Test Place' },
730
+ toPlace: () => mockPlace,
731
+ },
732
+ },
733
+ ];
734
+
735
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
736
+ suggestions: mockSuggestions,
737
+ });
738
+
739
+ const { user } = setupTest({ onError });
740
+ const input = screen.getByRole('textbox');
741
+
742
+ await user.type(input, 'Test');
743
+
744
+ await waitFor(() => {
745
+ expect(screen.getByRole('list')).toBeInTheDocument();
746
+ });
747
+
748
+ const button = screen.getByRole('button');
749
+ await fireEvent.click(button);
750
+
751
+ await waitFor(() => {
752
+ expect(onError).toHaveBeenCalledWith(expect.stringContaining('Fetch Error'));
753
+ });
754
+ });
755
+
756
+ test('handles missing API key gracefully', async () => {
757
+ const onError = vi.fn();
758
+
759
+ // This test verifies the component handles initialization properly
760
+ setupTest({ onError });
761
+
762
+ // Component should initialize without throwing
763
+ const input = screen.getByRole('textbox');
764
+ expect(input).toBeInTheDocument();
765
+ });
766
+ });
767
+
768
+ describe('Keyboard Hints', () => {
769
+ test('displays keyboard hints when results are visible', async () => {
770
+ const mockSuggestions = [
771
+ {
772
+ placePrediction: {
773
+ text: { toString: () => 'Test Place' },
774
+ toPlace: () => ({ fetchFields: vi.fn() }),
775
+ },
776
+ },
777
+ ];
778
+
779
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
780
+ suggestions: mockSuggestions,
781
+ });
782
+
783
+ const { user, container } = setupTest();
784
+ const input = screen.getByRole('textbox');
785
+
786
+ await user.type(input, 'Test');
787
+
788
+ await waitFor(() => {
789
+ expect(screen.getByRole('list')).toBeInTheDocument();
790
+ });
791
+
792
+ // Check for keyboard hints
793
+ const hints = container.querySelectorAll('kbd');
794
+ expect(hints.length).toBeGreaterThan(0);
795
+ });
796
+
797
+ test('does not display hints when no results', () => {
798
+ const { container } = setupTest();
799
+ const hints = container.querySelectorAll('kbd');
800
+ expect(hints.length).toBe(0);
801
+ });
802
+ });
803
+
804
+ describe('Fetch Fields Configuration', () => {
805
+ test('uses default fetch fields', async () => {
806
+ const mockPlace = {
807
+ fetchFields: vi.fn().mockResolvedValue({}),
808
+ toJSON: () => ({ text: 'Test' }),
809
+ };
810
+
811
+ const mockSuggestions = [
812
+ {
813
+ placePrediction: {
814
+ text: { toString: () => 'Test' },
815
+ toPlace: () => mockPlace,
816
+ },
817
+ },
818
+ ];
819
+
820
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
821
+ suggestions: mockSuggestions,
822
+ });
823
+
824
+ const { user } = setupTest();
825
+ const input = screen.getByRole('textbox');
826
+
827
+ await user.type(input, 'Test');
828
+
829
+ await waitFor(() => {
830
+ expect(screen.getByRole('list')).toBeInTheDocument();
831
+ });
832
+
833
+ const button = screen.getByRole('button');
834
+ await fireEvent.click(button);
835
+
836
+ await waitFor(() => {
837
+ expect(mockPlace.fetchFields).toHaveBeenCalledWith({
838
+ fields: ['formattedAddress', 'addressComponents'],
839
+ });
840
+ });
841
+ });
842
+
843
+ test('uses custom fetch fields', async () => {
844
+ const mockPlace = {
845
+ fetchFields: vi.fn().mockResolvedValue({}),
846
+ toJSON: () => ({ text: 'Test' }),
847
+ };
848
+
849
+ const mockSuggestions = [
850
+ {
851
+ placePrediction: {
852
+ text: { toString: () => 'Test' },
853
+ toPlace: () => mockPlace,
854
+ },
855
+ },
856
+ ];
857
+
858
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
859
+ suggestions: mockSuggestions,
860
+ });
861
+
862
+ const { user } = setupTest({ fetchFields: ['location', 'displayName'] });
863
+ const input = screen.getByRole('textbox');
864
+
865
+ await user.type(input, 'Test');
866
+
867
+ await waitFor(() => {
868
+ expect(screen.getByRole('list')).toBeInTheDocument();
869
+ });
870
+
871
+ const button = screen.getByRole('button');
872
+ await fireEvent.click(button);
873
+
874
+ await waitFor(() => {
875
+ expect(mockPlace.fetchFields).toHaveBeenCalledWith({
876
+ fields: ['location', 'displayName'],
877
+ });
878
+ });
879
+ });
880
+ });
881
+
882
+ describe('Language and Region', () => {
883
+ test('uses default language and region', async () => {
884
+ const { user } = setupTest();
885
+ const input = screen.getByRole('textbox');
886
+
887
+ await user.type(input, 'Test');
888
+
889
+ await waitFor(() => {
890
+ expect(mockAutocompleteSuggestion.fetchAutocompleteSuggestions).toHaveBeenCalledWith(
891
+ expect.objectContaining({
892
+ language: 'en-US',
893
+ region: 'US',
894
+ })
895
+ );
896
+ });
897
+ });
898
+
899
+ test('uses custom language and region', async () => {
900
+ const { user } = setupTest({ language: 'es-ES', region: 'ES' });
901
+ const input = screen.getByRole('textbox');
902
+
903
+ await user.type(input, 'Test');
904
+
905
+ await waitFor(() => {
906
+ expect(mockAutocompleteSuggestion.fetchAutocompleteSuggestions).toHaveBeenCalledWith(
907
+ expect.objectContaining({
908
+ language: 'es-ES',
909
+ region: 'ES',
910
+ })
911
+ );
912
+ });
913
+ });
914
+ });
915
+
916
+ describe('Input Styling', () => {
917
+ test('has correct base classes', () => {
918
+ setupTest();
919
+ const input = screen.getByRole('textbox');
920
+ expect(input).toHaveClass('block');
921
+ expect(input).toHaveClass('w-full');
922
+ expect(input).toHaveClass('rounded-lg');
923
+ expect(input).toHaveClass('bg-gray-50');
924
+ expect(input).toHaveClass('border');
925
+ expect(input).toHaveClass('border-gray-300');
926
+ });
927
+
928
+ test('has focus styles', () => {
929
+ setupTest();
930
+ const input = screen.getByRole('textbox');
931
+ expect(input).toHaveClass('focus:ring-blue-500');
932
+ expect(input).toHaveClass('focus:border-blue-500');
933
+ });
934
+
935
+ test('has hover styles', () => {
936
+ setupTest();
937
+ const input = screen.getByRole('textbox');
938
+ expect(input).toHaveClass('hover:border-blue-500');
939
+ });
940
+
941
+ test('icon has correct positioning', () => {
942
+ const { container } = setupTest();
943
+ const iconContainer = container.querySelector('.pointer-events-none.absolute');
944
+ expect(iconContainer).toBeInTheDocument();
945
+ expect(iconContainer).toHaveClass('inset-y-0');
946
+ expect(iconContainer).toHaveClass('left-0');
947
+ });
948
+ });
949
+
950
+ describe('Results List Styling', () => {
951
+ test('results list has correct styling', async () => {
952
+ const mockSuggestions = [
953
+ {
954
+ placePrediction: {
955
+ text: { toString: () => 'Test Place' },
956
+ toPlace: () => ({ fetchFields: vi.fn() }),
957
+ },
958
+ },
959
+ ];
960
+
961
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
962
+ suggestions: mockSuggestions,
963
+ });
964
+
965
+ const { user } = setupTest();
966
+ const input = screen.getByRole('textbox');
967
+
968
+ await user.type(input, 'Test');
969
+
970
+ await waitFor(() => {
971
+ const list = screen.getByRole('list');
972
+ expect(list).toHaveClass('absolute');
973
+ expect(list).toHaveClass('top-full');
974
+ expect(list).toHaveClass('rounded-lg');
975
+ expect(list).toHaveClass('shadow-lg');
976
+ expect(list).toHaveClass('bg-white');
977
+ });
978
+ });
979
+
980
+ test('result items have hover styling', async () => {
981
+ const mockSuggestions = [
982
+ {
983
+ placePrediction: {
984
+ text: { toString: () => 'Test Place' },
985
+ toPlace: () => ({ fetchFields: vi.fn() }),
986
+ },
987
+ },
988
+ ];
989
+
990
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
991
+ suggestions: mockSuggestions,
992
+ });
993
+
994
+ const { user, container } = setupTest();
995
+ const input = screen.getByRole('textbox');
996
+
997
+ await user.type(input, 'Test');
998
+
999
+ await waitFor(() => {
1000
+ const listItem = container.querySelector('li');
1001
+ expect(listItem).toHaveClass('cursor-pointer');
1002
+ expect(listItem).toHaveClass('transition-colors');
1003
+ });
1004
+ });
1005
+ });
1006
+
1007
+ describe('Multiple Suggestions', () => {
1008
+ test('displays all suggestions', async () => {
1009
+ const mockSuggestions = [
1010
+ {
1011
+ placePrediction: {
1012
+ text: { toString: () => 'Result 1' },
1013
+ toPlace: () => ({ fetchFields: vi.fn() }),
1014
+ },
1015
+ },
1016
+ {
1017
+ placePrediction: {
1018
+ text: { toString: () => 'Result 2' },
1019
+ toPlace: () => ({ fetchFields: vi.fn() }),
1020
+ },
1021
+ },
1022
+ {
1023
+ placePrediction: {
1024
+ text: { toString: () => 'Result 3' },
1025
+ toPlace: () => ({ fetchFields: vi.fn() }),
1026
+ },
1027
+ },
1028
+ {
1029
+ placePrediction: {
1030
+ text: { toString: () => 'Result 4' },
1031
+ toPlace: () => ({ fetchFields: vi.fn() }),
1032
+ },
1033
+ },
1034
+ {
1035
+ placePrediction: {
1036
+ text: { toString: () => 'Result 5' },
1037
+ toPlace: () => ({ fetchFields: vi.fn() }),
1038
+ },
1039
+ },
1040
+ ];
1041
+
1042
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
1043
+ suggestions: mockSuggestions,
1044
+ });
1045
+
1046
+ const { user } = setupTest();
1047
+ const input = screen.getByRole('textbox');
1048
+
1049
+ await user.type(input, 'Test');
1050
+
1051
+ await waitFor(() => {
1052
+ const buttons = screen.getAllByRole('button');
1053
+ expect(buttons).toHaveLength(5);
1054
+ });
1055
+ });
1056
+
1057
+ test('each suggestion has correct id attribute', async () => {
1058
+ const mockSuggestions = [
1059
+ {
1060
+ placePrediction: {
1061
+ text: { toString: () => 'Result 1' },
1062
+ toPlace: () => ({ fetchFields: vi.fn() }),
1063
+ },
1064
+ },
1065
+ {
1066
+ placePrediction: {
1067
+ text: { toString: () => 'Result 2' },
1068
+ toPlace: () => ({ fetchFields: vi.fn() }),
1069
+ },
1070
+ },
1071
+ ];
1072
+
1073
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
1074
+ suggestions: mockSuggestions,
1075
+ });
1076
+
1077
+ const { user, container } = setupTest();
1078
+ const input = screen.getByRole('textbox');
1079
+
1080
+ await user.type(input, 'Test');
1081
+
1082
+ await waitFor(() => {
1083
+ expect(container.querySelector('#option-1')).toBeInTheDocument();
1084
+ expect(container.querySelector('#option-2')).toBeInTheDocument();
1085
+ });
1086
+ });
1087
+ });
1088
+
1089
+ describe('Integration Tests', () => {
1090
+ test('complete flow: type, navigate, select', async () => {
1091
+ const onResponse = vi.fn();
1092
+ const mockPlace = {
1093
+ fetchFields: vi.fn().mockResolvedValue({}),
1094
+ toJSON: () => ({ text: 'Final Selection', formattedAddress: '123 Main St' }),
1095
+ };
1096
+
1097
+ const mockSuggestions = [
1098
+ {
1099
+ placePrediction: {
1100
+ text: { toString: () => 'Result 1' },
1101
+ toPlace: () => mockPlace,
1102
+ },
1103
+ },
1104
+ {
1105
+ placePrediction: {
1106
+ text: { toString: () => 'Final Selection' },
1107
+ toPlace: () => mockPlace,
1108
+ },
1109
+ },
1110
+ ];
1111
+
1112
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
1113
+ suggestions: mockSuggestions,
1114
+ });
1115
+
1116
+ const { user } = setupTest({ onResponse });
1117
+ const input = screen.getByRole('textbox');
1118
+
1119
+ // Type to get suggestions
1120
+ await user.type(input, 'Test');
1121
+
1122
+ await waitFor(() => {
1123
+ expect(screen.getByRole('list')).toBeInTheDocument();
1124
+ });
1125
+
1126
+ // Navigate down to second item
1127
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
1128
+ await fireEvent.keyDown(input, { key: 'ArrowDown' });
1129
+
1130
+ // Select with Enter
1131
+ await fireEvent.keyDown(input, { key: 'Enter' });
1132
+
1133
+ await waitFor(() => {
1134
+ expect(onResponse).toHaveBeenCalledWith(
1135
+ expect.objectContaining({ text: 'Final Selection' })
1136
+ );
1137
+ expect(input.value).toBe('Final Selection');
1138
+ expect(screen.queryByRole('list')).not.toBeInTheDocument();
1139
+ });
1140
+ });
1141
+
1142
+ test('complete flow with cityState mode', async () => {
1143
+ const onResponse = vi.fn();
1144
+ const mockPlace = {
1145
+ fetchFields: vi.fn().mockResolvedValue({}),
1146
+ toJSON: () => ({ text: 'Los Angeles, CA' }),
1147
+ };
1148
+
1149
+ const mockSuggestions = [
1150
+ {
1151
+ placePrediction: {
1152
+ text: { toString: () => 'Los Angeles, CA, USA' },
1153
+ types: ['locality'],
1154
+ toPlace: () => mockPlace,
1155
+ },
1156
+ },
1157
+ ];
1158
+
1159
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
1160
+ suggestions: mockSuggestions,
1161
+ });
1162
+
1163
+ const { user } = setupTest({ mode: 'cityState', onResponse });
1164
+ const input = screen.getByRole('textbox');
1165
+
1166
+ await user.type(input, 'Los Angeles');
1167
+
1168
+ await waitFor(() => {
1169
+ expect(screen.getByText('Los Angeles, CA')).toBeInTheDocument();
1170
+ });
1171
+
1172
+ const button = screen.getByRole('button');
1173
+ await fireEvent.click(button);
1174
+
1175
+ await waitFor(() => {
1176
+ expect(input.value).toBe('Los Angeles, CA');
1177
+ expect(onResponse).toHaveBeenCalled();
1178
+ });
1179
+ });
1180
+ });
1181
+
1182
+ describe('Accessibility', () => {
1183
+ test('input has aria-controls attribute', () => {
1184
+ setupTest();
1185
+ const input = screen.getByRole('textbox');
1186
+ expect(input).toHaveAttribute('aria-controls', 'options');
1187
+ });
1188
+
1189
+ test('results list has correct id', async () => {
1190
+ const mockSuggestions = [
1191
+ {
1192
+ placePrediction: {
1193
+ text: { toString: () => 'Test Place' },
1194
+ toPlace: () => ({ fetchFields: vi.fn() }),
1195
+ },
1196
+ },
1197
+ ];
1198
+
1199
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
1200
+ suggestions: mockSuggestions,
1201
+ });
1202
+
1203
+ const { user } = setupTest();
1204
+ const input = screen.getByRole('textbox');
1205
+
1206
+ await user.type(input, 'Test');
1207
+
1208
+ await waitFor(() => {
1209
+ const list = screen.getByRole('list');
1210
+ expect(list).toHaveAttribute('id', 'options');
1211
+ });
1212
+ });
1213
+
1214
+ test('suggestion buttons have correct tabindex', async () => {
1215
+ const mockSuggestions = [
1216
+ {
1217
+ placePrediction: {
1218
+ text: { toString: () => 'Result 1' },
1219
+ toPlace: () => ({ fetchFields: vi.fn() }),
1220
+ },
1221
+ },
1222
+ {
1223
+ placePrediction: {
1224
+ text: { toString: () => 'Result 2' },
1225
+ toPlace: () => ({ fetchFields: vi.fn() }),
1226
+ },
1227
+ },
1228
+ ];
1229
+
1230
+ mockAutocompleteSuggestion.fetchAutocompleteSuggestions.mockResolvedValue({
1231
+ suggestions: mockSuggestions,
1232
+ });
1233
+
1234
+ const { user } = setupTest();
1235
+ const input = screen.getByRole('textbox');
1236
+
1237
+ await user.type(input, 'Test');
1238
+
1239
+ await waitFor(() => {
1240
+ const buttons = screen.getAllByRole('button');
1241
+ expect(buttons[0]).toHaveAttribute('tabindex', '1');
1242
+ expect(buttons[1]).toHaveAttribute('tabindex', '2');
1243
+ });
1244
+ });
1245
+ });
1246
+ });