@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
@@ -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
+ });