@getmicdrop/svelte-components 5.5.1 → 5.5.5

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 (472) 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/Grid.svelte +4 -4
  34. package/dist/components/Layout/Section.spec.d.ts +2 -0
  35. package/dist/components/Layout/Section.spec.d.ts.map +1 -0
  36. package/dist/components/Layout/Section.spec.js +149 -0
  37. package/dist/components/Layout/Section.svelte +80 -80
  38. package/dist/components/Layout/Sidebar.spec.d.ts +2 -0
  39. package/dist/components/Layout/Sidebar.spec.d.ts.map +1 -0
  40. package/dist/components/Layout/Sidebar.spec.js +186 -0
  41. package/dist/components/Layout/Sidebar.svelte +108 -108
  42. package/dist/components/Layout/Stack.spec.js +3 -3
  43. package/dist/components/Layout/Stack.svelte +6 -6
  44. package/dist/constants/formOptions.spec.js +9 -5
  45. package/dist/constants/validation.js +91 -91
  46. package/dist/constants/validation.spec.js +64 -64
  47. package/dist/datetime/__tests__/format.test.js +1 -1
  48. package/dist/datetime/__tests__/parse.test.js +1 -1
  49. package/dist/datetime/__tests__/timezone.test.js +124 -2
  50. package/dist/datetime/parse.js +1 -1
  51. package/dist/forms/createFieldTracker.spec.d.ts +2 -0
  52. package/dist/forms/createFieldTracker.spec.d.ts.map +1 -0
  53. package/dist/forms/createFieldTracker.spec.js +343 -0
  54. package/dist/forms/createFormStore.spec.d.ts +2 -0
  55. package/dist/forms/createFormStore.spec.d.ts.map +1 -0
  56. package/dist/forms/createFormStore.spec.js +689 -0
  57. package/dist/forms/createFormStore.svelte.js +0 -1
  58. package/dist/index.d.ts +5 -112
  59. package/dist/index.js +40 -225
  60. package/dist/patterns/data/DataGrid.spec.d.ts +2 -0
  61. package/dist/patterns/data/DataGrid.spec.d.ts.map +1 -0
  62. package/dist/patterns/data/DataGrid.spec.js +159 -0
  63. package/dist/patterns/data/DataGrid.svelte +45 -45
  64. package/dist/patterns/data/DataList.spec.d.ts +2 -0
  65. package/dist/patterns/data/DataList.spec.d.ts.map +1 -0
  66. package/dist/patterns/data/DataList.spec.js +158 -0
  67. package/dist/patterns/data/DataList.svelte +24 -24
  68. package/dist/patterns/data/DataTable.spec.d.ts +2 -0
  69. package/dist/patterns/data/DataTable.spec.d.ts.map +1 -0
  70. package/dist/patterns/data/DataTable.spec.js +196 -0
  71. package/dist/patterns/data/DataTable.svelte +36 -36
  72. package/dist/patterns/forms/FormActions.spec.js +95 -88
  73. package/dist/patterns/forms/FormActions.stories.svelte +97 -97
  74. package/dist/patterns/forms/FormActions.svelte +46 -46
  75. package/dist/patterns/forms/FormGrid.spec.d.ts +2 -0
  76. package/dist/patterns/forms/FormGrid.spec.d.ts.map +1 -0
  77. package/dist/patterns/forms/FormGrid.spec.js +125 -0
  78. package/dist/patterns/forms/FormGrid.svelte +33 -33
  79. package/dist/patterns/forms/FormSection.spec.d.ts +2 -0
  80. package/dist/patterns/forms/FormSection.spec.d.ts.map +1 -0
  81. package/dist/patterns/forms/FormSection.spec.js +153 -0
  82. package/dist/patterns/forms/FormSection.svelte +32 -32
  83. package/dist/patterns/forms/FormValidationSummary.stories.svelte +83 -83
  84. package/dist/patterns/forms/FormValidationSummary.svelte +33 -33
  85. package/dist/patterns/layout/Sidebar.spec.d.ts +2 -0
  86. package/dist/patterns/layout/Sidebar.spec.d.ts.map +1 -0
  87. package/dist/patterns/layout/Sidebar.spec.js +159 -0
  88. package/dist/patterns/layout/Sidebar.svelte +39 -39
  89. package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
  90. package/dist/patterns/navigation/BottomNav.svelte +20 -20
  91. package/dist/patterns/navigation/Header.spec.js +33 -24
  92. package/dist/patterns/navigation/Header.stories.svelte +77 -77
  93. package/dist/patterns/navigation/Header.svelte +193 -193
  94. package/dist/patterns/page/PageHeader.spec.d.ts +2 -0
  95. package/dist/patterns/page/PageHeader.spec.d.ts.map +1 -0
  96. package/dist/patterns/page/PageHeader.spec.js +167 -0
  97. package/dist/patterns/page/PageHeader.svelte +18 -18
  98. package/dist/patterns/page/PageLayout.spec.d.ts +2 -0
  99. package/dist/patterns/page/PageLayout.spec.d.ts.map +1 -0
  100. package/dist/patterns/page/PageLayout.spec.js +145 -0
  101. package/dist/patterns/page/PageLayout.svelte +40 -40
  102. package/dist/patterns/page/PageLoader.spec.js +57 -54
  103. package/dist/patterns/page/PageLoader.stories.svelte +137 -137
  104. package/dist/patterns/page/PageLoader.svelte +24 -24
  105. package/dist/patterns/page/SectionHeader.spec.d.ts +2 -0
  106. package/dist/patterns/page/SectionHeader.spec.d.ts.map +1 -0
  107. package/dist/patterns/page/SectionHeader.spec.js +197 -0
  108. package/dist/patterns/page/SectionHeader.svelte +29 -29
  109. package/dist/presets/badges.js +112 -112
  110. package/dist/presets/badges.spec.d.ts +2 -0
  111. package/dist/presets/badges.spec.d.ts.map +1 -0
  112. package/dist/presets/badges.spec.js +172 -0
  113. package/dist/presets/buttons.js +76 -76
  114. package/dist/presets/buttons.spec.d.ts +2 -0
  115. package/dist/presets/buttons.spec.d.ts.map +1 -0
  116. package/dist/presets/buttons.spec.js +135 -0
  117. package/dist/presets/index.js +9 -9
  118. package/dist/primitives/Accordion/Accordion.spec.d.ts +2 -0
  119. package/dist/primitives/Accordion/Accordion.spec.d.ts.map +1 -0
  120. package/dist/primitives/Accordion/Accordion.spec.js +83 -0
  121. package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
  122. package/dist/primitives/Accordion/Accordion.svelte +42 -42
  123. package/dist/primitives/Accordion/AccordionItem.spec.d.ts +2 -0
  124. package/dist/primitives/Accordion/AccordionItem.spec.d.ts.map +1 -0
  125. package/dist/primitives/Accordion/AccordionItem.spec.js +661 -0
  126. package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
  127. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte +107 -0
  128. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts +35 -0
  129. package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts.map +1 -0
  130. package/dist/primitives/Alert/Alert.spec.js +173 -170
  131. package/dist/primitives/Alert/Alert.stories.svelte +88 -88
  132. package/dist/primitives/Alert/Alert.svelte +27 -27
  133. package/dist/primitives/Avatar/Avatar.spec.d.ts +2 -0
  134. package/dist/primitives/Avatar/Avatar.spec.d.ts.map +1 -0
  135. package/dist/primitives/Avatar/Avatar.spec.js +211 -0
  136. package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
  137. package/dist/primitives/Avatar/Avatar.svelte +66 -66
  138. package/dist/primitives/Badges/Badge.spec.js +144 -103
  139. package/dist/primitives/Badges/Badge.stories.svelte +86 -86
  140. package/dist/primitives/Badges/Badge.svelte +79 -79
  141. package/dist/primitives/BottomSheet/BottomSheet.spec.js +136 -127
  142. package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
  143. package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
  144. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte +13 -0
  145. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts +7 -0
  146. package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts.map +1 -0
  147. package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +122 -120
  148. package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
  149. package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
  150. package/dist/primitives/Button/Button.spec.js +223 -211
  151. package/dist/primitives/Button/Button.stories.svelte +76 -76
  152. package/dist/primitives/Button/Button.svelte +270 -270
  153. package/dist/primitives/Button/ButtonSaveDemo.spec.js +146 -48
  154. package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
  155. package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts +2 -0
  156. package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts.map +1 -0
  157. package/dist/primitives/Button/ButtonVariantShowcase.spec.js +202 -0
  158. package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
  159. package/dist/primitives/Card.spec.js +49 -49
  160. package/dist/primitives/Card.stories.svelte +22 -22
  161. package/dist/primitives/Card.svelte +28 -28
  162. package/dist/primitives/Checkbox/Checkbox.spec.d.ts +2 -0
  163. package/dist/primitives/Checkbox/Checkbox.spec.d.ts.map +1 -0
  164. package/dist/primitives/Checkbox/Checkbox.spec.js +252 -0
  165. package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
  166. package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
  167. package/dist/primitives/DarkModeToggle.spec.js +390 -357
  168. package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
  169. package/dist/primitives/DarkModeToggle.svelte +136 -136
  170. package/dist/primitives/Drawer/Drawer.spec.d.ts +2 -0
  171. package/dist/primitives/Drawer/Drawer.spec.d.ts.map +1 -0
  172. package/dist/primitives/Drawer/Drawer.spec.js +212 -0
  173. package/dist/primitives/Drawer/Drawer.stories.svelte +80 -80
  174. package/dist/primitives/Drawer/Drawer.svelte +120 -120
  175. package/dist/primitives/Dropdown/Dropdown.spec.d.ts +2 -0
  176. package/dist/primitives/Dropdown/Dropdown.spec.d.ts.map +1 -0
  177. package/dist/primitives/Dropdown/Dropdown.spec.js +366 -0
  178. package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
  179. package/dist/primitives/Dropdown/Dropdown.svelte +14 -14
  180. package/dist/primitives/Dropdown/DropdownItem.spec.d.ts +2 -0
  181. package/dist/primitives/Dropdown/DropdownItem.spec.d.ts.map +1 -0
  182. package/dist/primitives/Dropdown/DropdownItem.spec.js +182 -0
  183. package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
  184. package/dist/primitives/Icons/ArrowLeft.svelte +8 -8
  185. package/dist/primitives/Icons/ArrowRight.svelte +8 -8
  186. package/dist/primitives/Icons/Availability.svelte +14 -14
  187. package/dist/primitives/Icons/Back.svelte +14 -14
  188. package/dist/primitives/Icons/CheckCircle.svelte +6 -6
  189. package/dist/primitives/Icons/CheckCircleOutline.svelte +15 -15
  190. package/dist/primitives/Icons/ChevronLeft.svelte +4 -4
  191. package/dist/primitives/Icons/ChevronRight.svelte +4 -4
  192. package/dist/primitives/Icons/Copy.svelte +15 -15
  193. package/dist/primitives/Icons/Cross.svelte +5 -5
  194. package/dist/primitives/Icons/DownArrow.svelte +8 -8
  195. package/dist/primitives/Icons/ErrorCircle.svelte +6 -6
  196. package/dist/primitives/Icons/FacebookIcon.svelte +2 -2
  197. package/dist/primitives/Icons/Home.svelte +15 -15
  198. package/dist/primitives/Icons/Icon.spec.js +169 -169
  199. package/dist/primitives/Icons/Icon.stories.svelte +100 -100
  200. package/dist/primitives/Icons/Icon.svelte +52 -52
  201. package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
  202. package/dist/primitives/Icons/Info.svelte +7 -7
  203. package/dist/primitives/Icons/InstagramIcon.svelte +4 -4
  204. package/dist/primitives/Icons/LogoInstagram.svelte +2 -2
  205. package/dist/primitives/Icons/Message.svelte +15 -15
  206. package/dist/primitives/Icons/MoonIcon.svelte +5 -5
  207. package/dist/primitives/Icons/More.svelte +21 -21
  208. package/dist/primitives/Icons/MoreHori.spec.js +61 -61
  209. package/dist/primitives/Icons/MoreHori.svelte +22 -22
  210. package/dist/primitives/Icons/Notification.svelte +14 -14
  211. package/dist/primitives/Icons/Payment.svelte +14 -14
  212. package/dist/primitives/Icons/Profile.svelte +21 -21
  213. package/dist/primitives/Icons/Reload.svelte +29 -29
  214. package/dist/primitives/Icons/Shows.svelte +21 -21
  215. package/dist/primitives/Icons/Signout.svelte +21 -21
  216. package/dist/primitives/Icons/SunIcon.svelte +8 -8
  217. package/dist/primitives/Icons/TiktokIcon.svelte +2 -2
  218. package/dist/primitives/Icons/TwitterIcon.svelte +2 -2
  219. package/dist/primitives/Icons/WarningIcon.spec.js +18 -18
  220. package/dist/primitives/Icons/WarningIcon.svelte +5 -5
  221. package/dist/primitives/Icons/iconTestUtils.spec.d.ts +2 -0
  222. package/dist/primitives/Icons/iconTestUtils.spec.d.ts.map +1 -0
  223. package/dist/primitives/Icons/iconTestUtils.spec.js +235 -0
  224. package/dist/primitives/Input/Input.spec.js +573 -573
  225. package/dist/primitives/Input/Input.stories.svelte +139 -139
  226. package/dist/primitives/Input/Input.svelte +384 -397
  227. package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
  228. package/dist/primitives/Input/Select.spec.js +212 -218
  229. package/dist/primitives/Input/Select.stories.svelte +112 -112
  230. package/dist/primitives/Input/Select.svelte +128 -128
  231. package/dist/primitives/Input/Textarea.spec.d.ts +2 -0
  232. package/dist/primitives/Input/Textarea.spec.d.ts.map +1 -0
  233. package/dist/primitives/Input/Textarea.spec.js +255 -0
  234. package/dist/primitives/Input/Textarea.stories.svelte +137 -137
  235. package/dist/primitives/Input/Textarea.svelte +35 -35
  236. package/dist/primitives/Label/Label.spec.d.ts +2 -0
  237. package/dist/primitives/Label/Label.spec.d.ts.map +1 -0
  238. package/dist/primitives/Label/Label.spec.js +157 -0
  239. package/dist/primitives/Label/Label.svelte +37 -37
  240. package/dist/primitives/Modal/Modal.spec.js +99 -95
  241. package/dist/primitives/Modal/Modal.stories.svelte +86 -86
  242. package/dist/primitives/Modal/Modal.svelte +158 -158
  243. package/dist/primitives/Modal/ModalTestWrapper.svelte +65 -0
  244. package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts +23 -0
  245. package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts.map +1 -0
  246. package/dist/primitives/NumberInput/NumberInput.spec.d.ts +2 -0
  247. package/dist/primitives/NumberInput/NumberInput.spec.d.ts.map +1 -0
  248. package/dist/primitives/NumberInput/NumberInput.spec.js +235 -0
  249. package/dist/primitives/NumberInput/NumberInput.svelte +106 -106
  250. package/dist/primitives/Pagination/Pagination.spec.d.ts +2 -0
  251. package/dist/primitives/Pagination/Pagination.spec.d.ts.map +1 -0
  252. package/dist/primitives/Pagination/Pagination.spec.js +266 -0
  253. package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
  254. package/dist/primitives/Pagination/Pagination.svelte +261 -261
  255. package/dist/primitives/Radio/Radio.spec.d.ts +2 -0
  256. package/dist/primitives/Radio/Radio.spec.d.ts.map +1 -0
  257. package/dist/primitives/Radio/Radio.spec.js +206 -0
  258. package/dist/primitives/Radio/Radio.stories.svelte +80 -80
  259. package/dist/primitives/Radio/Radio.svelte +67 -67
  260. package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts +2 -0
  261. package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts.map +1 -0
  262. package/dist/primitives/Skeleton/CardPlaceholder.spec.js +156 -0
  263. package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
  264. package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts +2 -0
  265. package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts.map +1 -0
  266. package/dist/primitives/Skeleton/ImagePlaceholder.spec.js +120 -0
  267. package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
  268. package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts +2 -0
  269. package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts.map +1 -0
  270. package/dist/primitives/Skeleton/ListPlaceholder.spec.js +220 -0
  271. package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
  272. package/dist/primitives/Skeleton/Skeleton.spec.d.ts +2 -0
  273. package/dist/primitives/Skeleton/Skeleton.spec.d.ts.map +1 -0
  274. package/dist/primitives/Skeleton/Skeleton.spec.js +173 -0
  275. package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
  276. package/dist/primitives/Skeleton/Skeleton.svelte +26 -26
  277. package/dist/primitives/Spinner/Spinner.spec.js +71 -75
  278. package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
  279. package/dist/primitives/Spinner/Spinner.svelte +20 -20
  280. package/dist/primitives/Tabs/TabItem.spec.d.ts +2 -0
  281. package/dist/primitives/Tabs/TabItem.spec.d.ts.map +1 -0
  282. package/dist/primitives/Tabs/TabItem.spec.js +130 -0
  283. package/dist/primitives/Tabs/TabItem.svelte +49 -49
  284. package/dist/primitives/Tabs/Tabs.spec.d.ts +2 -0
  285. package/dist/primitives/Tabs/Tabs.spec.d.ts.map +1 -0
  286. package/dist/primitives/Tabs/Tabs.spec.js +295 -0
  287. package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
  288. package/dist/primitives/Tabs/Tabs.svelte +123 -123
  289. package/dist/primitives/Tabs/TabsWithItems.test.svelte +18 -0
  290. package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts +16 -0
  291. package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts.map +1 -0
  292. package/dist/primitives/Toggle.spec.js +143 -127
  293. package/dist/primitives/Toggle.stories.svelte +92 -92
  294. package/dist/primitives/Toggle.svelte +71 -71
  295. package/dist/primitives/Typography/Typography.spec.d.ts +2 -0
  296. package/dist/primitives/Typography/Typography.spec.d.ts.map +1 -0
  297. package/dist/primitives/Typography/Typography.spec.js +183 -0
  298. package/dist/primitives/Typography/Typography.svelte +53 -53
  299. package/dist/primitives/ValidationError.spec.js +103 -103
  300. package/dist/primitives/ValidationError.stories.svelte +69 -69
  301. package/dist/primitives/ValidationError.svelte +29 -29
  302. package/dist/primitives/index.d.ts +1 -0
  303. package/dist/primitives/index.js +3 -0
  304. package/dist/recipes/CropImage/CropImage.spec.js +208 -216
  305. package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
  306. package/dist/recipes/CropImage/CropImage.svelte +238 -238
  307. package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts +2 -0
  308. package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts.map +1 -0
  309. package/dist/recipes/ImageUploader/ImageUploader.spec.js +1351 -0
  310. package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
  311. package/dist/recipes/ImageUploader/ImageUploader.svelte +804 -804
  312. package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts +2 -0
  313. package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts.map +1 -0
  314. package/dist/recipes/SuperLogin/SuperLogin.spec.js +1436 -0
  315. package/dist/recipes/SuperLogin/SuperLogin.svelte +7 -6
  316. package/dist/recipes/SuperLogin/SuperLogin.svelte.d.ts.map +1 -1
  317. package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
  318. package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts +2 -0
  319. package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts.map +1 -0
  320. package/dist/recipes/feedback/EmptyState/EmptyState.spec.js +202 -0
  321. package/dist/recipes/feedback/EmptyState/EmptyState.svelte +1 -1
  322. package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
  323. package/dist/recipes/feedback/ErrorDisplay.stories.svelte +101 -101
  324. package/dist/recipes/feedback/ErrorDisplay.svelte +1 -1
  325. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +133 -129
  326. package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +157 -157
  327. package/dist/recipes/fields/CheckboxField.spec.d.ts +2 -0
  328. package/dist/recipes/fields/CheckboxField.spec.d.ts.map +1 -0
  329. package/dist/recipes/fields/CheckboxField.spec.js +135 -0
  330. package/dist/recipes/fields/CheckboxField.svelte +85 -85
  331. package/dist/recipes/fields/FormField.spec.d.ts +2 -0
  332. package/dist/recipes/fields/FormField.spec.d.ts.map +1 -0
  333. package/dist/recipes/fields/FormField.spec.js +159 -0
  334. package/dist/recipes/fields/FormField.svelte +58 -58
  335. package/dist/recipes/fields/RadioGroup.spec.d.ts +2 -0
  336. package/dist/recipes/fields/RadioGroup.spec.d.ts.map +1 -0
  337. package/dist/recipes/fields/RadioGroup.spec.js +199 -0
  338. package/dist/recipes/fields/RadioGroup.svelte +95 -95
  339. package/dist/recipes/fields/SelectField.spec.d.ts +2 -0
  340. package/dist/recipes/fields/SelectField.spec.d.ts.map +1 -0
  341. package/dist/recipes/fields/SelectField.spec.js +188 -0
  342. package/dist/recipes/fields/SelectField.svelte +80 -80
  343. package/dist/recipes/fields/TextareaField.spec.d.ts +2 -0
  344. package/dist/recipes/fields/TextareaField.spec.d.ts.map +1 -0
  345. package/dist/recipes/fields/TextareaField.spec.js +205 -0
  346. package/dist/recipes/fields/TextareaField.svelte +97 -97
  347. package/dist/recipes/fields/ToggleField.spec.d.ts +2 -0
  348. package/dist/recipes/fields/ToggleField.spec.d.ts.map +1 -0
  349. package/dist/recipes/fields/ToggleField.spec.js +153 -0
  350. package/dist/recipes/fields/ToggleField.svelte +60 -60
  351. package/dist/recipes/fields/index.js +7 -7
  352. package/dist/recipes/inputs/MultiSelect.spec.js +258 -257
  353. package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
  354. package/dist/recipes/inputs/MultiSelect.svelte +256 -249
  355. package/dist/recipes/inputs/MultiSelect.svelte.d.ts +2 -0
  356. package/dist/recipes/inputs/MultiSelect.svelte.d.ts.map +1 -1
  357. package/dist/recipes/inputs/OTPInput.spec.js +251 -238
  358. package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
  359. package/dist/recipes/inputs/OTPInput.svelte +29 -29
  360. package/dist/recipes/inputs/PasswordInput.spec.d.ts +2 -0
  361. package/dist/recipes/inputs/PasswordInput.spec.d.ts.map +1 -0
  362. package/dist/recipes/inputs/PasswordInput.spec.js +410 -0
  363. package/dist/recipes/inputs/PasswordInput.svelte +22 -22
  364. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +245 -165
  365. package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +43 -43
  366. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte +71 -0
  367. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts +9 -0
  368. package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts.map +1 -0
  369. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +1139 -193
  370. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +123 -123
  371. package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +326 -326
  372. package/dist/recipes/inputs/Search.spec.d.ts +2 -0
  373. package/dist/recipes/inputs/Search.spec.d.ts.map +1 -0
  374. package/dist/recipes/inputs/Search.spec.js +177 -0
  375. package/dist/recipes/inputs/Search.svelte +37 -37
  376. package/dist/recipes/inputs/SelectDropdown.spec.d.ts +2 -0
  377. package/dist/recipes/inputs/SelectDropdown.spec.d.ts.map +1 -0
  378. package/dist/recipes/inputs/SelectDropdown.spec.js +512 -0
  379. package/dist/recipes/inputs/SelectDropdown.svelte +57 -57
  380. package/dist/recipes/modals/AlertModal.spec.d.ts +2 -0
  381. package/dist/recipes/modals/AlertModal.spec.d.ts.map +1 -0
  382. package/dist/recipes/modals/AlertModal.spec.js +432 -0
  383. package/dist/recipes/modals/AlertModal.svelte +130 -130
  384. package/dist/recipes/modals/ConfirmationModal.spec.js +206 -191
  385. package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
  386. package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
  387. package/dist/recipes/modals/InputModal.spec.d.ts +2 -0
  388. package/dist/recipes/modals/InputModal.spec.d.ts.map +1 -0
  389. package/dist/recipes/modals/InputModal.spec.js +872 -0
  390. package/dist/recipes/modals/InputModal.svelte +182 -182
  391. package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
  392. package/dist/recipes/modals/ModalStateManager.svelte +77 -77
  393. package/dist/recipes/modals/ModalTestWrapper.spec.d.ts +2 -0
  394. package/dist/recipes/modals/ModalTestWrapper.spec.d.ts.map +1 -0
  395. package/dist/recipes/modals/ModalTestWrapper.spec.js +502 -0
  396. package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
  397. package/dist/recipes/modals/StatusModal.spec.d.ts +2 -0
  398. package/dist/recipes/modals/StatusModal.spec.d.ts.map +1 -0
  399. package/dist/recipes/modals/StatusModal.spec.js +599 -0
  400. package/dist/recipes/modals/StatusModal.svelte +206 -206
  401. package/dist/services/EventService.js +75 -75
  402. package/dist/services/EventService.spec.js +217 -217
  403. package/dist/services/ShowService.spec.js +345 -342
  404. package/dist/stores/auth.js +36 -36
  405. package/dist/stores/auth.spec.js +139 -139
  406. package/dist/stores/toaster.js +13 -13
  407. package/dist/stories/ButtonAuditDashboard.spec.d.ts +2 -0
  408. package/dist/stories/ButtonAuditDashboard.spec.d.ts.map +1 -0
  409. package/dist/stories/ButtonAuditDashboard.spec.js +913 -0
  410. package/dist/stories/ButtonAuditReview.spec.d.ts +2 -0
  411. package/dist/stories/ButtonAuditReview.spec.d.ts.map +1 -0
  412. package/dist/stories/ButtonAuditReview.spec.js +422 -0
  413. package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
  414. package/dist/stories/ButtonAuditReview.svelte +427 -427
  415. package/dist/stories/ButtonGridView.spec.d.ts +2 -0
  416. package/dist/stories/ButtonGridView.spec.d.ts.map +1 -0
  417. package/dist/stories/ButtonGridView.spec.js +667 -0
  418. package/dist/stories/ButtonShowcase.spec.d.ts +2 -0
  419. package/dist/stories/ButtonShowcase.spec.d.ts.map +1 -0
  420. package/dist/stories/ButtonShowcase.spec.js +499 -0
  421. package/dist/stories/PatternsGallery.spec.d.ts +2 -0
  422. package/dist/stories/PatternsGallery.spec.d.ts.map +1 -0
  423. package/dist/stories/PatternsGallery.spec.js +514 -0
  424. package/dist/stories/PatternsGallery.stories.svelte +19 -19
  425. package/dist/stories/PatternsGallery.svelte +206 -206
  426. package/dist/stories/PrimitivesGallery.spec.d.ts +2 -0
  427. package/dist/stories/PrimitivesGallery.spec.d.ts.map +1 -0
  428. package/dist/stories/PrimitivesGallery.spec.js +813 -0
  429. package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
  430. package/dist/stories/PrimitivesGallery.svelte +725 -725
  431. package/dist/stories/RecipesGallery.spec.d.ts +2 -0
  432. package/dist/stories/RecipesGallery.spec.d.ts.map +1 -0
  433. package/dist/stories/RecipesGallery.spec.js +299 -0
  434. package/dist/stories/RecipesGallery.stories.svelte +19 -19
  435. package/dist/stories/RecipesGallery.svelte +271 -271
  436. package/dist/stories/button-audit-manifest.json +11186 -11186
  437. package/dist/stripe/useStripeTheme.spec.d.ts +2 -0
  438. package/dist/stripe/useStripeTheme.spec.d.ts.map +1 -0
  439. package/dist/stripe/useStripeTheme.spec.js +793 -0
  440. package/dist/tailwind/preset.cjs +82 -82
  441. package/dist/telemetry.d.ts.map +1 -1
  442. package/dist/telemetry.js +6 -5
  443. package/dist/telemetry.spec.js +495 -12
  444. package/dist/tokens/__tests__/colors.test.d.ts +2 -0
  445. package/dist/tokens/__tests__/colors.test.d.ts.map +1 -0
  446. package/dist/tokens/__tests__/colors.test.js +152 -0
  447. package/dist/tokens/__tests__/radius.test.d.ts +2 -0
  448. package/dist/tokens/__tests__/radius.test.d.ts.map +1 -0
  449. package/dist/tokens/__tests__/radius.test.js +118 -0
  450. package/dist/tokens/__tests__/shadows.test.d.ts +2 -0
  451. package/dist/tokens/__tests__/shadows.test.d.ts.map +1 -0
  452. package/dist/tokens/__tests__/shadows.test.js +105 -0
  453. package/dist/tokens/__tests__/spacing.test.js +11 -8
  454. package/dist/tokens/__tests__/typography.test.d.ts +2 -0
  455. package/dist/tokens/__tests__/typography.test.d.ts.map +1 -0
  456. package/dist/tokens/__tests__/typography.test.js +156 -0
  457. package/dist/tokens/__tests__/z-index.test.d.ts +2 -0
  458. package/dist/tokens/__tests__/z-index.test.d.ts.map +1 -0
  459. package/dist/tokens/__tests__/z-index.test.js +121 -0
  460. package/dist/tokens/tokens.css +87 -87
  461. package/dist/utils/apiConfig.spec.js +102 -1
  462. package/dist/utils/formatters.spec.d.ts +2 -0
  463. package/dist/utils/formatters.spec.d.ts.map +1 -0
  464. package/dist/utils/formatters.spec.js +82 -0
  465. package/dist/utils/transitions.d.ts +24 -0
  466. package/dist/utils/transitions.d.ts.map +1 -0
  467. package/dist/utils/transitions.js +62 -0
  468. package/dist/utils/transitions.spec.d.ts +2 -0
  469. package/dist/utils/transitions.spec.d.ts.map +1 -0
  470. package/dist/utils/transitions.spec.js +130 -0
  471. package/dist/utils/utils.js +354 -354
  472. package/package.json +288 -283
@@ -0,0 +1,1191 @@
1
+ import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
2
+ import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/svelte';
3
+ import MiniMonthCalendar from './MiniMonthCalendar.svelte';
4
+
5
+ describe('MiniMonthCalendar Component', () => {
6
+ const mockEvents = [
7
+ { date: '2024-03-15', id: 1, name: 'Event 1' },
8
+ { date: '2024-03-20', id: 2, name: 'Event 2' },
9
+ { date: '2024-03-20', id: 3, name: 'Event 3' },
10
+ ];
11
+
12
+ beforeEach(() => {
13
+ vi.useFakeTimers();
14
+ vi.setSystemTime(new Date('2024-03-10T12:00:00Z'));
15
+ });
16
+
17
+ afterEach(() => {
18
+ cleanup();
19
+ vi.useRealTimers();
20
+ vi.restoreAllMocks();
21
+ });
22
+
23
+ describe('rendering', () => {
24
+ it('renders calendar grid', () => {
25
+ const { container } = render(MiniMonthCalendar);
26
+ expect(container.querySelector('[role="grid"]')).toBeInTheDocument();
27
+ });
28
+
29
+ it('renders day headers', () => {
30
+ render(MiniMonthCalendar);
31
+ expect(screen.getByRole('columnheader', { name: 'Sunday' })).toBeInTheDocument();
32
+ expect(screen.getByRole('columnheader', { name: 'Monday' })).toBeInTheDocument();
33
+ expect(screen.getByRole('columnheader', { name: 'Tuesday' })).toBeInTheDocument();
34
+ expect(screen.getByRole('columnheader', { name: 'Wednesday' })).toBeInTheDocument();
35
+ expect(screen.getByRole('columnheader', { name: 'Thursday' })).toBeInTheDocument();
36
+ expect(screen.getByRole('columnheader', { name: 'Friday' })).toBeInTheDocument();
37
+ expect(screen.getByRole('columnheader', { name: 'Saturday' })).toBeInTheDocument();
38
+ });
39
+
40
+ it('renders days of the month', () => {
41
+ const { container } = render(MiniMonthCalendar);
42
+ // March 2024 has 31 days
43
+ const allCells = container.querySelectorAll('[role="gridcell"]');
44
+ // Should have at least 31 day cells (actual days, not preview days)
45
+ expect(allCells.length).toBeGreaterThanOrEqual(31);
46
+ });
47
+
48
+ it('renders month name in header', () => {
49
+ render(MiniMonthCalendar);
50
+ expect(screen.getByText('March')).toBeInTheDocument();
51
+ });
52
+
53
+ it('shows navigation by default', () => {
54
+ render(MiniMonthCalendar);
55
+ expect(screen.getByLabelText('Previous month')).toBeInTheDocument();
56
+ expect(screen.getByLabelText('Next month')).toBeInTheDocument();
57
+ expect(screen.getByLabelText('Go to current month')).toBeInTheDocument();
58
+ });
59
+
60
+ it('hides navigation when showNavigation is false', () => {
61
+ render(MiniMonthCalendar, { props: { showNavigation: false } });
62
+ expect(screen.queryByLabelText('Previous month')).not.toBeInTheDocument();
63
+ expect(screen.queryByLabelText('Next month')).not.toBeInTheDocument();
64
+ });
65
+
66
+ it('hides today button when showTodayButton is false', () => {
67
+ render(MiniMonthCalendar, { props: { showTodayButton: false } });
68
+ expect(screen.queryByLabelText('Go to current month')).not.toBeInTheDocument();
69
+ });
70
+
71
+ it('shows legend for availability variant by default', () => {
72
+ render(MiniMonthCalendar, { props: { variant: 'availability' } });
73
+ expect(screen.getByText('Available')).toBeInTheDocument();
74
+ expect(screen.getByText('Unavailable')).toBeInTheDocument();
75
+ });
76
+
77
+ it('hides legend when showLegend is false', () => {
78
+ render(MiniMonthCalendar, { props: { variant: 'availability', showLegend: false } });
79
+ expect(screen.queryByText('Available')).not.toBeInTheDocument();
80
+ expect(screen.queryByText('Unavailable')).not.toBeInTheDocument();
81
+ });
82
+ });
83
+
84
+ describe('variants', () => {
85
+ it('renders availability variant', () => {
86
+ const { container } = render(MiniMonthCalendar, {
87
+ props: { variant: 'availability' }
88
+ });
89
+ expect(screen.getByText('Available')).toBeInTheDocument();
90
+ });
91
+
92
+ it('renders display variant', () => {
93
+ const { container } = render(MiniMonthCalendar, {
94
+ props: { variant: 'display', events: mockEvents }
95
+ });
96
+ // Display variant shows event indicators
97
+ const gridCells = container.querySelectorAll('[role="gridcell"]');
98
+ expect(gridCells.length).toBeGreaterThan(0);
99
+ });
100
+
101
+ it('renders public variant', () => {
102
+ const { container } = render(MiniMonthCalendar, {
103
+ props: { variant: 'public', events: mockEvents }
104
+ });
105
+ const gridCells = container.querySelectorAll('[role="gridcell"]');
106
+ expect(gridCells.length).toBeGreaterThan(0);
107
+ });
108
+ });
109
+
110
+ describe('date selection (availability variant)', () => {
111
+ it('selects a date when clicked', async () => {
112
+ const onDateSelect = vi.fn();
113
+ const selectedDates = [];
114
+ render(MiniMonthCalendar, {
115
+ props: {
116
+ variant: 'availability',
117
+ selectedDates,
118
+ onDateSelect
119
+ }
120
+ });
121
+
122
+ // Find a future date (March 15, 2024)
123
+ const cells = screen.getAllByRole('gridcell');
124
+ const day15 = cells.find(cell => cell.textContent.includes('15') && !cell.hasAttribute('aria-disabled'));
125
+
126
+ if (day15) {
127
+ await fireEvent.click(day15);
128
+ expect(onDateSelect).toHaveBeenCalledWith(
129
+ expect.objectContaining({
130
+ date: expect.stringContaining('2024-03-15'),
131
+ selected: true
132
+ })
133
+ );
134
+ }
135
+ });
136
+
137
+ it('deselects a date when clicked again', async () => {
138
+ const onDateSelect = vi.fn();
139
+ const selectedDates = ['2024-03-15'];
140
+ render(MiniMonthCalendar, {
141
+ props: {
142
+ variant: 'availability',
143
+ selectedDates,
144
+ onDateSelect
145
+ }
146
+ });
147
+
148
+ const cells = screen.getAllByRole('gridcell');
149
+ const day15 = cells.find(cell =>
150
+ cell.textContent.includes('15') && cell.getAttribute('aria-selected') === 'true'
151
+ );
152
+
153
+ if (day15) {
154
+ await fireEvent.click(day15);
155
+ expect(onDateSelect).toHaveBeenCalledWith(
156
+ expect.objectContaining({
157
+ date: expect.stringContaining('2024-03-15'),
158
+ selected: false
159
+ })
160
+ );
161
+ }
162
+ });
163
+
164
+ it('does not select past dates in availability variant', async () => {
165
+ const onDateSelect = vi.fn();
166
+ render(MiniMonthCalendar, {
167
+ props: {
168
+ variant: 'availability',
169
+ onDateSelect
170
+ }
171
+ });
172
+
173
+ // Try to click March 5 (past date, today is March 10)
174
+ const cells = screen.getAllByRole('gridcell');
175
+ const day5 = cells.find(cell => {
176
+ const text = cell.textContent.trim();
177
+ return text === '5' && cell.getAttribute('aria-disabled') !== 'true';
178
+ });
179
+
180
+ if (day5) {
181
+ await fireEvent.click(day5);
182
+ // Should not be called for past dates
183
+ expect(onDateSelect).not.toHaveBeenCalled();
184
+ }
185
+ });
186
+
187
+ it('does not select dates in readOnly mode', async () => {
188
+ const onDateSelect = vi.fn();
189
+ render(MiniMonthCalendar, {
190
+ props: {
191
+ variant: 'availability',
192
+ readOnly: true,
193
+ onDateSelect
194
+ }
195
+ });
196
+
197
+ const cells = screen.getAllByRole('gridcell');
198
+ const day15 = cells.find(cell => cell.textContent.includes('15'));
199
+
200
+ if (day15) {
201
+ await fireEvent.click(day15);
202
+ expect(onDateSelect).not.toHaveBeenCalled();
203
+ }
204
+ });
205
+ });
206
+
207
+ describe('events display', () => {
208
+ it('shows event indicators for display variant', () => {
209
+ const { container } = render(MiniMonthCalendar, {
210
+ props: {
211
+ variant: 'display',
212
+ events: mockEvents
213
+ }
214
+ });
215
+
216
+ // Check for event indicator dots
217
+ const dots = container.querySelectorAll('.bg-blue-600, .bg-blue-500');
218
+ expect(dots.length).toBeGreaterThan(0);
219
+ });
220
+
221
+ it('shows multiple event indicators when multiple events on same day', () => {
222
+ const { container } = render(MiniMonthCalendar, {
223
+ props: {
224
+ variant: 'display',
225
+ events: mockEvents // March 20 has 2 events
226
+ }
227
+ });
228
+
229
+ const dots = container.querySelectorAll('.bg-blue-600, .bg-blue-500');
230
+ // Should show up to 3 dots per day
231
+ expect(dots.length).toBeGreaterThan(0);
232
+ });
233
+
234
+ it('calls onDayClick when clicking day with events', async () => {
235
+ const onDayClick = vi.fn();
236
+ render(MiniMonthCalendar, {
237
+ props: {
238
+ variant: 'display',
239
+ events: mockEvents,
240
+ onDayClick
241
+ }
242
+ });
243
+
244
+ const cells = screen.getAllByRole('gridcell');
245
+ const day15 = cells.find(cell => cell.textContent.includes('15'));
246
+
247
+ if (day15) {
248
+ await fireEvent.click(day15);
249
+ expect(onDayClick).toHaveBeenCalledWith(
250
+ expect.objectContaining({
251
+ date: expect.stringContaining('2024-03-15'),
252
+ events: expect.arrayContaining([
253
+ expect.objectContaining({ id: 1 })
254
+ ])
255
+ })
256
+ );
257
+ }
258
+ });
259
+
260
+ it('does not call onDayClick for days without events', async () => {
261
+ const onDayClick = vi.fn();
262
+ render(MiniMonthCalendar, {
263
+ props: {
264
+ variant: 'display',
265
+ events: mockEvents,
266
+ onDayClick
267
+ }
268
+ });
269
+
270
+ const cells = screen.getAllByRole('gridcell');
271
+ // Click day 5 which has no events
272
+ const day5 = cells.find(cell => cell.textContent.trim() === '5');
273
+
274
+ if (day5) {
275
+ await fireEvent.click(day5);
276
+ expect(onDayClick).not.toHaveBeenCalled();
277
+ }
278
+ });
279
+ });
280
+
281
+ describe('month navigation', () => {
282
+ it('navigates to next month', async () => {
283
+ const onMonthChange = vi.fn();
284
+ render(MiniMonthCalendar, { props: { onMonthChange } });
285
+
286
+ const nextButton = screen.getByLabelText('Next month');
287
+ await fireEvent.click(nextButton);
288
+
289
+ await waitFor(() => {
290
+ expect(screen.getByText('April')).toBeInTheDocument();
291
+ });
292
+
293
+ expect(onMonthChange).toHaveBeenCalledWith(
294
+ expect.objectContaining({ month: 3, year: 2024 })
295
+ );
296
+ });
297
+
298
+ it('navigates to previous month', async () => {
299
+ const onMonthChange = vi.fn();
300
+ render(MiniMonthCalendar, { props: { onMonthChange } });
301
+
302
+ const prevButton = screen.getByLabelText('Previous month');
303
+ await fireEvent.click(prevButton);
304
+
305
+ await waitFor(() => {
306
+ expect(screen.getByText('February')).toBeInTheDocument();
307
+ });
308
+
309
+ expect(onMonthChange).toHaveBeenCalledWith(
310
+ expect.objectContaining({ month: 1, year: 2024 })
311
+ );
312
+ });
313
+
314
+ it('wraps to next year when navigating from December', async () => {
315
+ vi.setSystemTime(new Date('2024-12-15T12:00:00Z'));
316
+ render(MiniMonthCalendar);
317
+
318
+ const nextButton = screen.getByLabelText('Next month');
319
+ await fireEvent.click(nextButton);
320
+
321
+ await waitFor(() => {
322
+ expect(screen.getByText('January')).toBeInTheDocument();
323
+ });
324
+ });
325
+
326
+ it('wraps to previous year when navigating from January', async () => {
327
+ vi.setSystemTime(new Date('2024-01-15T12:00:00Z'));
328
+ render(MiniMonthCalendar);
329
+
330
+ const prevButton = screen.getByLabelText('Previous month');
331
+ await fireEvent.click(prevButton);
332
+
333
+ await waitFor(() => {
334
+ expect(screen.getByText('December')).toBeInTheDocument();
335
+ });
336
+ });
337
+
338
+ it('navigates to current month when Today button clicked', async () => {
339
+ vi.setSystemTime(new Date('2024-03-10T12:00:00Z'));
340
+ render(MiniMonthCalendar);
341
+
342
+ // Navigate to a different month first
343
+ const nextButton = screen.getByLabelText('Next month');
344
+ await fireEvent.click(nextButton);
345
+ await waitFor(() => expect(screen.getByText('April')).toBeInTheDocument(), { timeout: 1000 });
346
+
347
+ // Wait for animation to complete
348
+ await vi.advanceTimersByTimeAsync(500);
349
+
350
+ // Click Today button
351
+ const todayButton = screen.getByLabelText('Go to current month');
352
+ await fireEvent.click(todayButton);
353
+
354
+ await waitFor(() => {
355
+ expect(screen.getByText('March')).toBeInTheDocument();
356
+ }, { timeout: 1000 });
357
+ });
358
+
359
+ it('disables Today button when at current month', () => {
360
+ render(MiniMonthCalendar);
361
+ const todayButton = screen.getByLabelText('Go to current month');
362
+ expect(todayButton).toBeDisabled();
363
+ });
364
+
365
+ it('enables Today button when not at current month', async () => {
366
+ render(MiniMonthCalendar);
367
+
368
+ const nextButton = screen.getByLabelText('Next month');
369
+ await fireEvent.click(nextButton);
370
+
371
+ await waitFor(() => {
372
+ const todayButton = screen.getByLabelText('Go to current month');
373
+ expect(todayButton).not.toBeDisabled();
374
+ });
375
+ });
376
+ });
377
+
378
+ describe('disablePastNavigation', () => {
379
+ it('disables previous month button at current month', () => {
380
+ render(MiniMonthCalendar, { props: { disablePastNavigation: true } });
381
+ const prevButton = screen.getByLabelText('Previous month');
382
+ expect(prevButton).toBeDisabled();
383
+ });
384
+
385
+ it('enables previous month button when not at current month', async () => {
386
+ render(MiniMonthCalendar, { props: { disablePastNavigation: true } });
387
+
388
+ const nextButton = screen.getByLabelText('Next month');
389
+ await fireEvent.click(nextButton);
390
+
391
+ await waitFor(() => {
392
+ const prevButton = screen.getByLabelText('Previous month');
393
+ expect(prevButton).not.toBeDisabled();
394
+ });
395
+ });
396
+
397
+ it('allows past navigation when disablePastNavigation is false', () => {
398
+ render(MiniMonthCalendar, { props: { disablePastNavigation: false } });
399
+ const prevButton = screen.getByLabelText('Previous month');
400
+ expect(prevButton).not.toBeDisabled();
401
+ });
402
+ });
403
+
404
+ describe('save status indicator', () => {
405
+ it('shows saving spinner', () => {
406
+ render(MiniMonthCalendar, {
407
+ props: {
408
+ variant: 'availability',
409
+ saveStatus: 'saving'
410
+ }
411
+ });
412
+
413
+ const { container } = render(MiniMonthCalendar, {
414
+ props: {
415
+ variant: 'availability',
416
+ saveStatus: 'saving'
417
+ }
418
+ });
419
+ // Check for spinner
420
+ expect(container.querySelector('.w-5.h-5')).toBeInTheDocument();
421
+ });
422
+
423
+ it('shows saved checkmark', () => {
424
+ const { container } = render(MiniMonthCalendar, {
425
+ props: {
426
+ variant: 'availability',
427
+ saveStatus: 'saved'
428
+ }
429
+ });
430
+
431
+ // Check for green checkmark
432
+ const checkmark = container.querySelector('.text-green-600');
433
+ expect(checkmark).toBeInTheDocument();
434
+ });
435
+
436
+ it('does not show status for non-availability variants', () => {
437
+ const { container } = render(MiniMonthCalendar, {
438
+ props: {
439
+ variant: 'display',
440
+ saveStatus: 'saving'
441
+ }
442
+ });
443
+
444
+ // Should not show spinner for display variant
445
+ expect(container.querySelector('.text-green-600')).not.toBeInTheDocument();
446
+ });
447
+ });
448
+
449
+ describe('keyboard navigation', () => {
450
+ it('triggers selection on Enter key', async () => {
451
+ const onDateSelect = vi.fn();
452
+ render(MiniMonthCalendar, {
453
+ props: {
454
+ variant: 'availability',
455
+ onDateSelect
456
+ }
457
+ });
458
+
459
+ const cells = screen.getAllByRole('gridcell');
460
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
461
+
462
+ if (day15) {
463
+ await fireEvent.keyDown(day15, { key: 'Enter' });
464
+ expect(onDateSelect).toHaveBeenCalled();
465
+ }
466
+ });
467
+
468
+ it('triggers selection on Space key', async () => {
469
+ const onDateSelect = vi.fn();
470
+ render(MiniMonthCalendar, {
471
+ props: {
472
+ variant: 'availability',
473
+ onDateSelect
474
+ }
475
+ });
476
+
477
+ const cells = screen.getAllByRole('gridcell');
478
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
479
+
480
+ if (day15) {
481
+ await fireEvent.keyDown(day15, { key: ' ' });
482
+ expect(onDateSelect).toHaveBeenCalled();
483
+ }
484
+ });
485
+
486
+ it('does not trigger on other keys', async () => {
487
+ const onDateSelect = vi.fn();
488
+ render(MiniMonthCalendar, {
489
+ props: {
490
+ variant: 'availability',
491
+ onDateSelect
492
+ }
493
+ });
494
+
495
+ const cells = screen.getAllByRole('gridcell');
496
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
497
+
498
+ if (day15) {
499
+ await fireEvent.keyDown(day15, { key: 'Tab' });
500
+ expect(onDateSelect).not.toHaveBeenCalled();
501
+ }
502
+ });
503
+ });
504
+
505
+ describe('accessibility', () => {
506
+ it('has proper ARIA grid structure', () => {
507
+ const { container } = render(MiniMonthCalendar);
508
+ const grid = container.querySelector('[role="grid"]');
509
+ expect(grid).toHaveAttribute('aria-label', expect.stringContaining('March'));
510
+ expect(grid).toHaveAttribute('aria-label', expect.stringContaining('2024'));
511
+ });
512
+
513
+ it('marks selected dates with aria-selected', () => {
514
+ render(MiniMonthCalendar, {
515
+ props: {
516
+ variant: 'availability',
517
+ selectedDates: ['2024-03-15']
518
+ }
519
+ });
520
+
521
+ const cells = screen.getAllByRole('gridcell');
522
+ const selectedCell = cells.find(cell => cell.getAttribute('aria-selected') === 'true');
523
+ expect(selectedCell).toBeInTheDocument();
524
+ });
525
+
526
+ it('marks past dates as disabled in availability variant', () => {
527
+ render(MiniMonthCalendar, {
528
+ props: { variant: 'availability' }
529
+ });
530
+
531
+ const cells = screen.getAllByRole('gridcell');
532
+ const disabledCells = cells.filter(cell =>
533
+ cell.getAttribute('aria-disabled') === 'true'
534
+ );
535
+ expect(disabledCells.length).toBeGreaterThan(0);
536
+ });
537
+
538
+ it('provides descriptive aria-labels for days', () => {
539
+ render(MiniMonthCalendar, {
540
+ props: { variant: 'availability' }
541
+ });
542
+
543
+ const cells = screen.getAllByRole('gridcell');
544
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
545
+
546
+ if (day15) {
547
+ const ariaLabel = day15.getAttribute('aria-label');
548
+ expect(ariaLabel).toBeTruthy();
549
+ expect(ariaLabel).toContain('March');
550
+ expect(ariaLabel).toContain('15');
551
+ }
552
+ });
553
+
554
+ it('includes today indicator in aria-label', () => {
555
+ render(MiniMonthCalendar);
556
+
557
+ const cells = screen.getAllByRole('gridcell');
558
+ const todayCell = cells.find(cell => {
559
+ const label = cell.getAttribute('aria-label');
560
+ return label && label.includes('Today');
561
+ });
562
+
563
+ expect(todayCell).toBeInTheDocument();
564
+ });
565
+
566
+ it('sets correct tabindex for interactive days', () => {
567
+ render(MiniMonthCalendar, {
568
+ props: { variant: 'availability' }
569
+ });
570
+
571
+ const cells = screen.getAllByRole('gridcell');
572
+ const focusableCells = cells.filter(cell => cell.tabIndex === 0);
573
+
574
+ // Future dates should be focusable
575
+ expect(focusableCells.length).toBeGreaterThan(0);
576
+ });
577
+
578
+ it('sets tabindex -1 for non-interactive days', () => {
579
+ render(MiniMonthCalendar, {
580
+ props: { variant: 'availability' }
581
+ });
582
+
583
+ const cells = screen.getAllByRole('gridcell');
584
+ const nonFocusableCells = cells.filter(cell => cell.tabIndex === -1);
585
+
586
+ // Past dates should not be focusable
587
+ expect(nonFocusableCells.length).toBeGreaterThan(0);
588
+ });
589
+ });
590
+
591
+ describe('preview days', () => {
592
+ it('shows previous month preview days by default', () => {
593
+ const { container } = render(MiniMonthCalendar);
594
+ const previewDays = container.querySelectorAll('.text-gray-300, .dark\\:text-gray-600');
595
+ // March 2024 starts on a Friday, so should show some preview days
596
+ expect(previewDays.length).toBeGreaterThan(0);
597
+ });
598
+
599
+ it('shows next month preview days by default', () => {
600
+ const { container } = render(MiniMonthCalendar);
601
+ const previewDays = container.querySelectorAll('.text-gray-300, .dark\\:text-gray-600');
602
+ expect(previewDays.length).toBeGreaterThan(0);
603
+ });
604
+
605
+ it('hides preview days when showPartialPreview is false', () => {
606
+ const { container } = render(MiniMonthCalendar, {
607
+ props: { showPartialPreview: false }
608
+ });
609
+
610
+ // When showPartialPreview is false, there should be no preview day elements with pointer-events-none
611
+ const previewDays = container.querySelectorAll('.pointer-events-none');
612
+ expect(previewDays.length).toBe(0);
613
+ });
614
+ });
615
+
616
+ describe('touch events', () => {
617
+ it('handles touch events for day selection', async () => {
618
+ const onDateSelect = vi.fn();
619
+ render(MiniMonthCalendar, {
620
+ props: {
621
+ variant: 'availability',
622
+ onDateSelect
623
+ }
624
+ });
625
+
626
+ const cells = screen.getAllByRole('gridcell');
627
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
628
+
629
+ if (day15) {
630
+ await fireEvent.touchEnd(day15, {
631
+ preventDefault: vi.fn()
632
+ });
633
+ expect(onDateSelect).toHaveBeenCalled();
634
+ }
635
+ });
636
+ });
637
+
638
+ describe('swipe navigation', () => {
639
+ it('handles swipe right to go to previous month', async () => {
640
+ const { container } = render(MiniMonthCalendar);
641
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
642
+
643
+ // Start touch
644
+ await fireEvent.touchStart(calendarContainer, {
645
+ touches: [{ clientX: 100, clientY: 100 }]
646
+ });
647
+
648
+ // Swipe right (previous month)
649
+ await fireEvent.touchMove(calendarContainer, {
650
+ touches: [{ clientX: 200, clientY: 100 }]
651
+ });
652
+
653
+ await fireEvent.touchEnd(calendarContainer);
654
+
655
+ // Should navigate to previous month
656
+ await waitFor(() => {
657
+ expect(screen.getByText('February')).toBeInTheDocument();
658
+ });
659
+ });
660
+
661
+ it('handles swipe left to go to next month', async () => {
662
+ const { container } = render(MiniMonthCalendar);
663
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
664
+
665
+ // Start touch
666
+ await fireEvent.touchStart(calendarContainer, {
667
+ touches: [{ clientX: 200, clientY: 100 }]
668
+ });
669
+
670
+ // Swipe left (next month)
671
+ await fireEvent.touchMove(calendarContainer, {
672
+ touches: [{ clientX: 100, clientY: 100 }]
673
+ });
674
+
675
+ await fireEvent.touchEnd(calendarContainer);
676
+
677
+ // Should navigate to next month
678
+ await waitFor(() => {
679
+ expect(screen.getByText('April')).toBeInTheDocument();
680
+ });
681
+ });
682
+
683
+ it('cancels swipe if vertical scroll detected', async () => {
684
+ const { container } = render(MiniMonthCalendar);
685
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
686
+
687
+ // Start touch
688
+ await fireEvent.touchStart(calendarContainer, {
689
+ touches: [{ clientX: 100, clientY: 100 }]
690
+ });
691
+
692
+ // Move vertically (should cancel horizontal swipe)
693
+ await fireEvent.touchMove(calendarContainer, {
694
+ touches: [{ clientX: 105, clientY: 200 }]
695
+ });
696
+
697
+ await fireEvent.touchEnd(calendarContainer);
698
+
699
+ // Should still be on March
700
+ expect(screen.getByText('March')).toBeInTheDocument();
701
+ });
702
+
703
+ it('does not swipe when threshold not met', async () => {
704
+ const { container } = render(MiniMonthCalendar);
705
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
706
+
707
+ // Start touch
708
+ await fireEvent.touchStart(calendarContainer, {
709
+ touches: [{ clientX: 100, clientY: 100 }]
710
+ });
711
+
712
+ // Small swipe (below threshold)
713
+ await fireEvent.touchMove(calendarContainer, {
714
+ touches: [{ clientX: 110, clientY: 100 }]
715
+ });
716
+
717
+ await fireEvent.touchEnd(calendarContainer);
718
+
719
+ // Should still be on March
720
+ expect(screen.getByText('March')).toBeInTheDocument();
721
+ });
722
+ });
723
+
724
+ describe('mouse drag navigation', () => {
725
+ it('handles mouse drag right to go to previous month', async () => {
726
+ const { container } = render(MiniMonthCalendar);
727
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
728
+
729
+ // Start drag
730
+ await fireEvent.mouseDown(calendarContainer, {
731
+ clientX: 100,
732
+ clientY: 100,
733
+ button: 0
734
+ });
735
+
736
+ // Drag right (previous month)
737
+ await fireEvent.mouseMove(calendarContainer, {
738
+ clientX: 200,
739
+ clientY: 100
740
+ });
741
+
742
+ await fireEvent.mouseUp(calendarContainer);
743
+
744
+ // Should navigate to previous month
745
+ await waitFor(() => {
746
+ expect(screen.getByText('February')).toBeInTheDocument();
747
+ });
748
+ });
749
+
750
+ it('handles mouse drag left to go to next month', async () => {
751
+ const { container } = render(MiniMonthCalendar);
752
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
753
+
754
+ // Start drag
755
+ await fireEvent.mouseDown(calendarContainer, {
756
+ clientX: 200,
757
+ clientY: 100,
758
+ button: 0
759
+ });
760
+
761
+ // Drag left (next month)
762
+ await fireEvent.mouseMove(calendarContainer, {
763
+ clientX: 100,
764
+ clientY: 100
765
+ });
766
+
767
+ await fireEvent.mouseUp(calendarContainer);
768
+
769
+ // Should navigate to next month
770
+ await waitFor(() => {
771
+ expect(screen.getByText('April')).toBeInTheDocument();
772
+ });
773
+ });
774
+
775
+ it('ignores non-left mouse button', async () => {
776
+ const { container } = render(MiniMonthCalendar);
777
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
778
+
779
+ // Try to drag with right button
780
+ await fireEvent.mouseDown(calendarContainer, {
781
+ clientX: 100,
782
+ clientY: 100,
783
+ button: 2
784
+ });
785
+
786
+ await fireEvent.mouseMove(calendarContainer, {
787
+ clientX: 200,
788
+ clientY: 100
789
+ });
790
+
791
+ await fireEvent.mouseUp(calendarContainer);
792
+
793
+ // Should still be on March
794
+ expect(screen.getByText('March')).toBeInTheDocument();
795
+ });
796
+
797
+ it('handles mouseleave during drag', async () => {
798
+ const { container } = render(MiniMonthCalendar);
799
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
800
+
801
+ // Start drag
802
+ await fireEvent.mouseDown(calendarContainer, {
803
+ clientX: 100,
804
+ clientY: 100,
805
+ button: 0
806
+ });
807
+
808
+ // Small move (not enough to trigger swipe)
809
+ await fireEvent.mouseMove(calendarContainer, {
810
+ clientX: 120,
811
+ clientY: 100
812
+ });
813
+
814
+ // Mouse leaves container
815
+ await fireEvent.mouseLeave(calendarContainer);
816
+
817
+ // Should complete or cancel the swipe - just verify no error
818
+ const monthName = screen.queryByText('March') || screen.queryByText('February');
819
+ expect(monthName).toBeInTheDocument();
820
+ });
821
+
822
+ it('cancels mouse drag if vertical scroll detected', async () => {
823
+ const { container } = render(MiniMonthCalendar);
824
+ const calendarContainer = container.querySelector('.w-full.max-w-full');
825
+
826
+ // Start drag
827
+ await fireEvent.mouseDown(calendarContainer, {
828
+ clientX: 100,
829
+ clientY: 100,
830
+ button: 0
831
+ });
832
+
833
+ // Move vertically
834
+ await fireEvent.mouseMove(calendarContainer, {
835
+ clientX: 105,
836
+ clientY: 200
837
+ });
838
+
839
+ await fireEvent.mouseUp(calendarContainer);
840
+
841
+ // Should still be on March
842
+ expect(screen.getByText('March')).toBeInTheDocument();
843
+ });
844
+ });
845
+
846
+ describe('button press states', () => {
847
+ it('applies pressed state on previous button mousedown', async () => {
848
+ const { container } = render(MiniMonthCalendar, {
849
+ props: { disablePastNavigation: false }
850
+ });
851
+
852
+ const prevButton = screen.getByLabelText('Previous month');
853
+ await fireEvent.mouseDown(prevButton);
854
+
855
+ expect(prevButton.className).toContain('scale-90');
856
+ });
857
+
858
+ it('removes pressed state on previous button mouseup', async () => {
859
+ const { container } = render(MiniMonthCalendar, {
860
+ props: { disablePastNavigation: false }
861
+ });
862
+
863
+ const prevButton = screen.getByLabelText('Previous month');
864
+ await fireEvent.mouseDown(prevButton);
865
+ await fireEvent.mouseUp(prevButton);
866
+
867
+ expect(prevButton.className).not.toContain('scale-90');
868
+ });
869
+
870
+ it('applies pressed state on next button mousedown', async () => {
871
+ render(MiniMonthCalendar);
872
+
873
+ const nextButton = screen.getByLabelText('Next month');
874
+ await fireEvent.mouseDown(nextButton);
875
+
876
+ expect(nextButton.className).toContain('scale-90');
877
+ });
878
+
879
+ it('applies pressed state on today button mousedown', async () => {
880
+ render(MiniMonthCalendar);
881
+
882
+ // Navigate away from current month first
883
+ const nextButton = screen.getByLabelText('Next month');
884
+ await fireEvent.click(nextButton);
885
+
886
+ await waitFor(() => {
887
+ const todayButton = screen.getByLabelText('Go to current month');
888
+ fireEvent.mouseDown(todayButton);
889
+ expect(todayButton.className).toContain('scale-95');
890
+ });
891
+ });
892
+
893
+ it('handles touch events on navigation buttons', async () => {
894
+ const { container } = render(MiniMonthCalendar, {
895
+ props: { disablePastNavigation: false }
896
+ });
897
+
898
+ const prevButton = screen.getByLabelText('Previous month');
899
+ await fireEvent.touchStart(prevButton, {
900
+ touches: [{ clientX: 0, clientY: 0 }]
901
+ });
902
+
903
+ expect(prevButton.className).toContain('scale-90');
904
+
905
+ await fireEvent.touchEnd(prevButton, {
906
+ touches: []
907
+ });
908
+ expect(prevButton.className).not.toContain('scale-90');
909
+ });
910
+
911
+ it('handles touchcancel on navigation buttons', async () => {
912
+ const { container } = render(MiniMonthCalendar, {
913
+ props: { disablePastNavigation: false }
914
+ });
915
+
916
+ const prevButton = screen.getByLabelText('Previous month');
917
+ await fireEvent.touchStart(prevButton, {
918
+ touches: [{ clientX: 0, clientY: 0 }]
919
+ });
920
+ await fireEvent.touchCancel(prevButton);
921
+
922
+ expect(prevButton.className).not.toContain('scale-90');
923
+ });
924
+ });
925
+
926
+ describe('styling and visual states', () => {
927
+ it('applies selected style to selected dates', () => {
928
+ const { container } = render(MiniMonthCalendar, {
929
+ props: {
930
+ variant: 'availability',
931
+ selectedDates: ['2024-03-15']
932
+ }
933
+ });
934
+
935
+ const cells = screen.getAllByRole('gridcell');
936
+ const selectedCell = cells.find(cell =>
937
+ cell.getAttribute('aria-selected') === 'true'
938
+ );
939
+
940
+ expect(selectedCell?.className).toContain('bg-blue-700');
941
+ });
942
+
943
+ it('applies past date style for availability variant', () => {
944
+ const { container } = render(MiniMonthCalendar, {
945
+ props: { variant: 'availability' }
946
+ });
947
+
948
+ const cells = screen.getAllByRole('gridcell');
949
+ const pastCell = cells.find(cell =>
950
+ cell.textContent.trim() === '5' && // Day 5 is in the past
951
+ !cell.hasAttribute('aria-disabled')
952
+ );
953
+
954
+ if (pastCell) {
955
+ expect(pastCell.className).toContain('bg-gray-');
956
+ }
957
+ });
958
+
959
+ it('applies event indicator background for display variant', () => {
960
+ const { container } = render(MiniMonthCalendar, {
961
+ props: {
962
+ variant: 'display',
963
+ events: mockEvents
964
+ }
965
+ });
966
+
967
+ const cells = screen.getAllByRole('gridcell');
968
+ const eventCell = cells.find(cell => cell.textContent.includes('15'));
969
+
970
+ if (eventCell) {
971
+ expect(eventCell.className).toContain('bg-blue-');
972
+ }
973
+ });
974
+
975
+ it('shows cursor pointer for interactive days', () => {
976
+ const { container } = render(MiniMonthCalendar, {
977
+ props: { variant: 'availability' }
978
+ });
979
+
980
+ const cells = screen.getAllByRole('gridcell');
981
+ const futureCell = cells.find(cell =>
982
+ cell.tabIndex === 0
983
+ );
984
+
985
+ expect(futureCell?.className).toContain('cursor-pointer');
986
+ });
987
+
988
+ it('shows default cursor for past days', () => {
989
+ const { container } = render(MiniMonthCalendar, {
990
+ props: { variant: 'availability' }
991
+ });
992
+
993
+ const cells = screen.getAllByRole('gridcell');
994
+ const pastCell = cells.find(cell =>
995
+ cell.textContent.trim() === '5'
996
+ );
997
+
998
+ if (pastCell) {
999
+ expect(pastCell.className).toContain('cursor-default');
1000
+ }
1001
+ });
1002
+ });
1003
+
1004
+ describe('prefers-reduced-motion', () => {
1005
+ it('detects prefers-reduced-motion media query', () => {
1006
+ const mockMatchMedia = vi.fn().mockImplementation(query => ({
1007
+ matches: query === '(prefers-reduced-motion: reduce)',
1008
+ media: query,
1009
+ addEventListener: vi.fn(),
1010
+ removeEventListener: vi.fn(),
1011
+ }));
1012
+
1013
+ window.matchMedia = mockMatchMedia;
1014
+
1015
+ render(MiniMonthCalendar);
1016
+
1017
+ expect(mockMatchMedia).toHaveBeenCalledWith('(prefers-reduced-motion: reduce)');
1018
+ });
1019
+ });
1020
+
1021
+ describe('haptic feedback', () => {
1022
+ beforeEach(() => {
1023
+ // Reset window.matchMedia to prevent errors
1024
+ window.matchMedia = vi.fn().mockImplementation(query => ({
1025
+ matches: false,
1026
+ media: query,
1027
+ addEventListener: vi.fn(),
1028
+ removeEventListener: vi.fn(),
1029
+ }));
1030
+
1031
+ delete window.webkit;
1032
+ delete window.TapticEngine;
1033
+ navigator.vibrate = vi.fn().mockReturnValue(true);
1034
+ });
1035
+
1036
+ it('triggers webkit haptic when available', async () => {
1037
+ const postMessage = vi.fn();
1038
+ window.webkit = {
1039
+ messageHandlers: {
1040
+ haptic: { postMessage }
1041
+ }
1042
+ };
1043
+
1044
+ const onDateSelect = vi.fn();
1045
+ render(MiniMonthCalendar, {
1046
+ props: {
1047
+ variant: 'availability',
1048
+ onDateSelect
1049
+ }
1050
+ });
1051
+
1052
+ const cells = screen.getAllByRole('gridcell');
1053
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
1054
+
1055
+ if (day15) {
1056
+ await fireEvent.click(day15);
1057
+ expect(postMessage).toHaveBeenCalledWith('light');
1058
+ }
1059
+ });
1060
+
1061
+ it('triggers TapticEngine when available', async () => {
1062
+ const impact = vi.fn();
1063
+ window.TapticEngine = { impact };
1064
+
1065
+ const onDateSelect = vi.fn();
1066
+ render(MiniMonthCalendar, {
1067
+ props: {
1068
+ variant: 'availability',
1069
+ onDateSelect
1070
+ }
1071
+ });
1072
+
1073
+ const cells = screen.getAllByRole('gridcell');
1074
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
1075
+
1076
+ if (day15) {
1077
+ await fireEvent.click(day15);
1078
+ expect(impact).toHaveBeenCalledWith({ style: 'light' });
1079
+ }
1080
+ });
1081
+
1082
+ it('triggers navigator.vibrate as fallback', async () => {
1083
+ const onDateSelect = vi.fn();
1084
+ render(MiniMonthCalendar, {
1085
+ props: {
1086
+ variant: 'availability',
1087
+ onDateSelect
1088
+ }
1089
+ });
1090
+
1091
+ const cells = screen.getAllByRole('gridcell');
1092
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
1093
+
1094
+ if (day15) {
1095
+ await fireEvent.click(day15);
1096
+ expect(navigator.vibrate).toHaveBeenCalledWith(10);
1097
+ }
1098
+ });
1099
+
1100
+ it('triggers medium haptic for deselection', async () => {
1101
+ const postMessage = vi.fn();
1102
+ window.webkit = {
1103
+ messageHandlers: {
1104
+ haptic: { postMessage }
1105
+ }
1106
+ };
1107
+
1108
+ const onDateSelect = vi.fn();
1109
+ render(MiniMonthCalendar, {
1110
+ props: {
1111
+ variant: 'availability',
1112
+ selectedDates: ['2024-03-15'],
1113
+ onDateSelect
1114
+ }
1115
+ });
1116
+
1117
+ const cells = screen.getAllByRole('gridcell');
1118
+ const day15 = cells.find(cell =>
1119
+ cell.textContent.includes('15') && cell.getAttribute('aria-selected') === 'true'
1120
+ );
1121
+
1122
+ if (day15) {
1123
+ await fireEvent.click(day15);
1124
+ expect(postMessage).toHaveBeenCalledWith('medium');
1125
+ }
1126
+ });
1127
+ });
1128
+
1129
+ describe('edge cases', () => {
1130
+ beforeEach(() => {
1131
+ // Reset window.matchMedia to prevent errors
1132
+ window.matchMedia = vi.fn().mockImplementation(query => ({
1133
+ matches: false,
1134
+ media: query,
1135
+ addEventListener: vi.fn(),
1136
+ removeEventListener: vi.fn(),
1137
+ }));
1138
+ });
1139
+
1140
+ it('handles empty events array', () => {
1141
+ const { container } = render(MiniMonthCalendar, {
1142
+ props: {
1143
+ variant: 'display',
1144
+ events: []
1145
+ }
1146
+ });
1147
+
1148
+ expect(container.querySelector('[role="grid"]')).toBeInTheDocument();
1149
+ });
1150
+
1151
+ it('handles undefined callbacks', async () => {
1152
+ render(MiniMonthCalendar, {
1153
+ props: { variant: 'availability' }
1154
+ });
1155
+
1156
+ const cells = screen.getAllByRole('gridcell');
1157
+ const day15 = cells.find(cell => cell.textContent.includes('15') && cell.tabIndex === 0);
1158
+
1159
+ if (day15) {
1160
+ await expect(fireEvent.click(day15)).resolves.not.toThrow();
1161
+ }
1162
+ });
1163
+
1164
+ it('handles month with different starting day', async () => {
1165
+ vi.setSystemTime(new Date('2024-04-10T12:00:00Z'));
1166
+ const { container } = render(MiniMonthCalendar);
1167
+
1168
+ // April 2024 has 30 days
1169
+ const allCells = container.querySelectorAll('[role="gridcell"]');
1170
+ expect(allCells.length).toBeGreaterThanOrEqual(30);
1171
+ });
1172
+
1173
+ it('handles February in leap year', async () => {
1174
+ vi.setSystemTime(new Date('2024-02-10T12:00:00Z'));
1175
+ const { container } = render(MiniMonthCalendar);
1176
+
1177
+ // 2024 is a leap year, February has 29 days
1178
+ const allCells = container.querySelectorAll('[role="gridcell"]');
1179
+ expect(allCells.length).toBeGreaterThanOrEqual(29);
1180
+ });
1181
+
1182
+ it('handles February in non-leap year', async () => {
1183
+ vi.setSystemTime(new Date('2023-02-10T12:00:00Z'));
1184
+ const { container } = render(MiniMonthCalendar);
1185
+
1186
+ // 2023 is not a leap year, February has 28 days
1187
+ const allCells = container.querySelectorAll('[role="gridcell"]');
1188
+ expect(allCells.length).toBeGreaterThanOrEqual(28);
1189
+ });
1190
+ });
1191
+ });