@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.
- package/dist/calendar/AboutShow/AboutShow.spec.d.ts +2 -0
- package/dist/calendar/AboutShow/AboutShow.spec.d.ts.map +1 -0
- package/dist/calendar/AboutShow/AboutShow.spec.js +791 -0
- package/dist/calendar/AboutShow/AboutShow.svelte +172 -172
- package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts +2 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.spec.d.ts.map +1 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.spec.js +1191 -0
- package/dist/calendar/Calendar/MiniMonthCalendar.svelte +782 -782
- package/dist/calendar/FAQs/FAQs.spec.d.ts +2 -0
- package/dist/calendar/FAQs/FAQs.spec.d.ts.map +1 -0
- package/dist/calendar/FAQs/FAQs.spec.js +238 -0
- package/dist/calendar/FAQs/FAQs.svelte +75 -75
- package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts +2 -0
- package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.d.ts.map +1 -0
- package/dist/calendar/MonthSwitcher/MonthSwitcher.spec.js +420 -0
- package/dist/calendar/MonthSwitcher/MonthSwitcher.svelte +126 -126
- package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts +2 -0
- package/dist/calendar/OrderSummary/OrderSummary.spec.d.ts.map +1 -0
- package/dist/calendar/OrderSummary/OrderSummary.spec.js +808 -0
- package/dist/calendar/OrderSummary/OrderSummary.svelte +367 -367
- package/dist/calendar/PublicCard/PublicCard.spec.d.ts +2 -0
- package/dist/calendar/PublicCard/PublicCard.spec.d.ts.map +1 -0
- package/dist/calendar/PublicCard/PublicCard.spec.js +301 -0
- package/dist/calendar/PublicCard/PublicCard.svelte +134 -134
- package/dist/calendar/ShowCard/ShowCard.spec.d.ts +2 -0
- package/dist/calendar/ShowCard/ShowCard.spec.d.ts.map +1 -0
- package/dist/calendar/ShowCard/ShowCard.spec.js +714 -0
- package/dist/calendar/ShowCard/ShowCard.svelte +157 -157
- package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts +2 -0
- package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.d.ts.map +1 -0
- package/dist/calendar/ShowTimeCard/ShowTimeCard.spec.js +241 -0
- package/dist/calendar/ShowTimeCard/ShowTimeCard.svelte +61 -61
- package/dist/components/Layout/Grid.svelte +4 -4
- package/dist/components/Layout/Section.spec.d.ts +2 -0
- package/dist/components/Layout/Section.spec.d.ts.map +1 -0
- package/dist/components/Layout/Section.spec.js +149 -0
- package/dist/components/Layout/Section.svelte +80 -80
- package/dist/components/Layout/Sidebar.spec.d.ts +2 -0
- package/dist/components/Layout/Sidebar.spec.d.ts.map +1 -0
- package/dist/components/Layout/Sidebar.spec.js +186 -0
- package/dist/components/Layout/Sidebar.svelte +108 -108
- package/dist/components/Layout/Stack.spec.js +3 -3
- package/dist/components/Layout/Stack.svelte +6 -6
- package/dist/constants/formOptions.spec.js +9 -5
- package/dist/constants/validation.js +91 -91
- package/dist/constants/validation.spec.js +64 -64
- package/dist/datetime/__tests__/format.test.js +1 -1
- package/dist/datetime/__tests__/parse.test.js +1 -1
- package/dist/datetime/__tests__/timezone.test.js +124 -2
- package/dist/datetime/parse.js +1 -1
- package/dist/forms/createFieldTracker.spec.d.ts +2 -0
- package/dist/forms/createFieldTracker.spec.d.ts.map +1 -0
- package/dist/forms/createFieldTracker.spec.js +343 -0
- package/dist/forms/createFormStore.spec.d.ts +2 -0
- package/dist/forms/createFormStore.spec.d.ts.map +1 -0
- package/dist/forms/createFormStore.spec.js +689 -0
- package/dist/forms/createFormStore.svelte.js +0 -1
- package/dist/index.d.ts +5 -112
- package/dist/index.js +40 -225
- package/dist/patterns/data/DataGrid.spec.d.ts +2 -0
- package/dist/patterns/data/DataGrid.spec.d.ts.map +1 -0
- package/dist/patterns/data/DataGrid.spec.js +159 -0
- package/dist/patterns/data/DataGrid.svelte +45 -45
- package/dist/patterns/data/DataList.spec.d.ts +2 -0
- package/dist/patterns/data/DataList.spec.d.ts.map +1 -0
- package/dist/patterns/data/DataList.spec.js +158 -0
- package/dist/patterns/data/DataList.svelte +24 -24
- package/dist/patterns/data/DataTable.spec.d.ts +2 -0
- package/dist/patterns/data/DataTable.spec.d.ts.map +1 -0
- package/dist/patterns/data/DataTable.spec.js +196 -0
- package/dist/patterns/data/DataTable.svelte +36 -36
- package/dist/patterns/forms/FormActions.spec.js +95 -88
- package/dist/patterns/forms/FormActions.stories.svelte +97 -97
- package/dist/patterns/forms/FormActions.svelte +46 -46
- package/dist/patterns/forms/FormGrid.spec.d.ts +2 -0
- package/dist/patterns/forms/FormGrid.spec.d.ts.map +1 -0
- package/dist/patterns/forms/FormGrid.spec.js +125 -0
- package/dist/patterns/forms/FormGrid.svelte +33 -33
- package/dist/patterns/forms/FormSection.spec.d.ts +2 -0
- package/dist/patterns/forms/FormSection.spec.d.ts.map +1 -0
- package/dist/patterns/forms/FormSection.spec.js +153 -0
- package/dist/patterns/forms/FormSection.svelte +32 -32
- package/dist/patterns/forms/FormValidationSummary.stories.svelte +83 -83
- package/dist/patterns/forms/FormValidationSummary.svelte +33 -33
- package/dist/patterns/layout/Sidebar.spec.d.ts +2 -0
- package/dist/patterns/layout/Sidebar.spec.d.ts.map +1 -0
- package/dist/patterns/layout/Sidebar.spec.js +159 -0
- package/dist/patterns/layout/Sidebar.svelte +39 -39
- package/dist/patterns/navigation/BottomNav.stories.svelte +117 -117
- package/dist/patterns/navigation/BottomNav.svelte +20 -20
- package/dist/patterns/navigation/Header.spec.js +33 -24
- package/dist/patterns/navigation/Header.stories.svelte +77 -77
- package/dist/patterns/navigation/Header.svelte +193 -193
- package/dist/patterns/page/PageHeader.spec.d.ts +2 -0
- package/dist/patterns/page/PageHeader.spec.d.ts.map +1 -0
- package/dist/patterns/page/PageHeader.spec.js +167 -0
- package/dist/patterns/page/PageHeader.svelte +18 -18
- package/dist/patterns/page/PageLayout.spec.d.ts +2 -0
- package/dist/patterns/page/PageLayout.spec.d.ts.map +1 -0
- package/dist/patterns/page/PageLayout.spec.js +145 -0
- package/dist/patterns/page/PageLayout.svelte +40 -40
- package/dist/patterns/page/PageLoader.spec.js +57 -54
- package/dist/patterns/page/PageLoader.stories.svelte +137 -137
- package/dist/patterns/page/PageLoader.svelte +24 -24
- package/dist/patterns/page/SectionHeader.spec.d.ts +2 -0
- package/dist/patterns/page/SectionHeader.spec.d.ts.map +1 -0
- package/dist/patterns/page/SectionHeader.spec.js +197 -0
- package/dist/patterns/page/SectionHeader.svelte +29 -29
- package/dist/presets/badges.js +112 -112
- package/dist/presets/badges.spec.d.ts +2 -0
- package/dist/presets/badges.spec.d.ts.map +1 -0
- package/dist/presets/badges.spec.js +172 -0
- package/dist/presets/buttons.js +76 -76
- package/dist/presets/buttons.spec.d.ts +2 -0
- package/dist/presets/buttons.spec.d.ts.map +1 -0
- package/dist/presets/buttons.spec.js +135 -0
- package/dist/presets/index.js +9 -9
- package/dist/primitives/Accordion/Accordion.spec.d.ts +2 -0
- package/dist/primitives/Accordion/Accordion.spec.d.ts.map +1 -0
- package/dist/primitives/Accordion/Accordion.spec.js +83 -0
- package/dist/primitives/Accordion/Accordion.stories.svelte +75 -75
- package/dist/primitives/Accordion/Accordion.svelte +42 -42
- package/dist/primitives/Accordion/AccordionItem.spec.d.ts +2 -0
- package/dist/primitives/Accordion/AccordionItem.spec.d.ts.map +1 -0
- package/dist/primitives/Accordion/AccordionItem.spec.js +661 -0
- package/dist/primitives/Accordion/AccordionItem.svelte +95 -95
- package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte +107 -0
- package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts +35 -0
- package/dist/primitives/Accordion/AccordionItemWrapper.test.svelte.d.ts.map +1 -0
- package/dist/primitives/Alert/Alert.spec.js +173 -170
- package/dist/primitives/Alert/Alert.stories.svelte +88 -88
- package/dist/primitives/Alert/Alert.svelte +27 -27
- package/dist/primitives/Avatar/Avatar.spec.d.ts +2 -0
- package/dist/primitives/Avatar/Avatar.spec.d.ts.map +1 -0
- package/dist/primitives/Avatar/Avatar.spec.js +211 -0
- package/dist/primitives/Avatar/Avatar.stories.svelte +94 -94
- package/dist/primitives/Avatar/Avatar.svelte +66 -66
- package/dist/primitives/Badges/Badge.spec.js +144 -103
- package/dist/primitives/Badges/Badge.stories.svelte +86 -86
- package/dist/primitives/Badges/Badge.svelte +79 -79
- package/dist/primitives/BottomSheet/BottomSheet.spec.js +136 -127
- package/dist/primitives/BottomSheet/BottomSheet.stories.svelte +83 -83
- package/dist/primitives/BottomSheet/BottomSheet.svelte +100 -100
- package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte +13 -0
- package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts +7 -0
- package/dist/primitives/BottomSheet/BottomSheetWrapper.test.svelte.d.ts.map +1 -0
- package/dist/primitives/Breadcrumb/Breadcrumb.spec.js +122 -120
- package/dist/primitives/Breadcrumb/Breadcrumb.stories.svelte +23 -23
- package/dist/primitives/Breadcrumb/Breadcrumb.svelte +89 -89
- package/dist/primitives/Button/Button.spec.js +223 -211
- package/dist/primitives/Button/Button.stories.svelte +76 -76
- package/dist/primitives/Button/Button.svelte +270 -270
- package/dist/primitives/Button/ButtonSaveDemo.spec.js +146 -48
- package/dist/primitives/Button/ButtonSaveDemo.svelte +25 -25
- package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts +2 -0
- package/dist/primitives/Button/ButtonVariantShowcase.spec.d.ts.map +1 -0
- package/dist/primitives/Button/ButtonVariantShowcase.spec.js +202 -0
- package/dist/primitives/Button/ButtonVariantShowcase.svelte +129 -129
- package/dist/primitives/Card.spec.js +49 -49
- package/dist/primitives/Card.stories.svelte +22 -22
- package/dist/primitives/Card.svelte +28 -28
- package/dist/primitives/Checkbox/Checkbox.spec.d.ts +2 -0
- package/dist/primitives/Checkbox/Checkbox.spec.d.ts.map +1 -0
- package/dist/primitives/Checkbox/Checkbox.spec.js +252 -0
- package/dist/primitives/Checkbox/Checkbox.stories.svelte +84 -84
- package/dist/primitives/Checkbox/Checkbox.svelte +88 -88
- package/dist/primitives/DarkModeToggle.spec.js +390 -357
- package/dist/primitives/DarkModeToggle.stories.svelte +57 -57
- package/dist/primitives/DarkModeToggle.svelte +136 -136
- package/dist/primitives/Drawer/Drawer.spec.d.ts +2 -0
- package/dist/primitives/Drawer/Drawer.spec.d.ts.map +1 -0
- package/dist/primitives/Drawer/Drawer.spec.js +212 -0
- package/dist/primitives/Drawer/Drawer.stories.svelte +80 -80
- package/dist/primitives/Drawer/Drawer.svelte +120 -120
- package/dist/primitives/Dropdown/Dropdown.spec.d.ts +2 -0
- package/dist/primitives/Dropdown/Dropdown.spec.d.ts.map +1 -0
- package/dist/primitives/Dropdown/Dropdown.spec.js +366 -0
- package/dist/primitives/Dropdown/Dropdown.stories.svelte +137 -137
- package/dist/primitives/Dropdown/Dropdown.svelte +14 -14
- package/dist/primitives/Dropdown/DropdownItem.spec.d.ts +2 -0
- package/dist/primitives/Dropdown/DropdownItem.spec.d.ts.map +1 -0
- package/dist/primitives/Dropdown/DropdownItem.spec.js +182 -0
- package/dist/primitives/Dropdown/DropdownItem.svelte +80 -80
- package/dist/primitives/Icons/ArrowLeft.svelte +8 -8
- package/dist/primitives/Icons/ArrowRight.svelte +8 -8
- package/dist/primitives/Icons/Availability.svelte +14 -14
- package/dist/primitives/Icons/Back.svelte +14 -14
- package/dist/primitives/Icons/CheckCircle.svelte +6 -6
- package/dist/primitives/Icons/CheckCircleOutline.svelte +15 -15
- package/dist/primitives/Icons/ChevronLeft.svelte +4 -4
- package/dist/primitives/Icons/ChevronRight.svelte +4 -4
- package/dist/primitives/Icons/Copy.svelte +15 -15
- package/dist/primitives/Icons/Cross.svelte +5 -5
- package/dist/primitives/Icons/DownArrow.svelte +8 -8
- package/dist/primitives/Icons/ErrorCircle.svelte +6 -6
- package/dist/primitives/Icons/FacebookIcon.svelte +2 -2
- package/dist/primitives/Icons/Home.svelte +15 -15
- package/dist/primitives/Icons/Icon.spec.js +169 -169
- package/dist/primitives/Icons/Icon.stories.svelte +100 -100
- package/dist/primitives/Icons/Icon.svelte +52 -52
- package/dist/primitives/Icons/IconGallery.stories.svelte +235 -235
- package/dist/primitives/Icons/Info.svelte +7 -7
- package/dist/primitives/Icons/InstagramIcon.svelte +4 -4
- package/dist/primitives/Icons/LogoInstagram.svelte +2 -2
- package/dist/primitives/Icons/Message.svelte +15 -15
- package/dist/primitives/Icons/MoonIcon.svelte +5 -5
- package/dist/primitives/Icons/More.svelte +21 -21
- package/dist/primitives/Icons/MoreHori.spec.js +61 -61
- package/dist/primitives/Icons/MoreHori.svelte +22 -22
- package/dist/primitives/Icons/Notification.svelte +14 -14
- package/dist/primitives/Icons/Payment.svelte +14 -14
- package/dist/primitives/Icons/Profile.svelte +21 -21
- package/dist/primitives/Icons/Reload.svelte +29 -29
- package/dist/primitives/Icons/Shows.svelte +21 -21
- package/dist/primitives/Icons/Signout.svelte +21 -21
- package/dist/primitives/Icons/SunIcon.svelte +8 -8
- package/dist/primitives/Icons/TiktokIcon.svelte +2 -2
- package/dist/primitives/Icons/TwitterIcon.svelte +2 -2
- package/dist/primitives/Icons/WarningIcon.spec.js +18 -18
- package/dist/primitives/Icons/WarningIcon.svelte +5 -5
- package/dist/primitives/Icons/iconTestUtils.spec.d.ts +2 -0
- package/dist/primitives/Icons/iconTestUtils.spec.d.ts.map +1 -0
- package/dist/primitives/Icons/iconTestUtils.spec.js +235 -0
- package/dist/primitives/Input/Input.spec.js +573 -573
- package/dist/primitives/Input/Input.stories.svelte +139 -139
- package/dist/primitives/Input/Input.svelte +384 -397
- package/dist/primitives/Input/Input.svelte.d.ts.map +1 -1
- package/dist/primitives/Input/Select.spec.js +212 -218
- package/dist/primitives/Input/Select.stories.svelte +112 -112
- package/dist/primitives/Input/Select.svelte +128 -128
- package/dist/primitives/Input/Textarea.spec.d.ts +2 -0
- package/dist/primitives/Input/Textarea.spec.d.ts.map +1 -0
- package/dist/primitives/Input/Textarea.spec.js +255 -0
- package/dist/primitives/Input/Textarea.stories.svelte +137 -137
- package/dist/primitives/Input/Textarea.svelte +35 -35
- package/dist/primitives/Label/Label.spec.d.ts +2 -0
- package/dist/primitives/Label/Label.spec.d.ts.map +1 -0
- package/dist/primitives/Label/Label.spec.js +157 -0
- package/dist/primitives/Label/Label.svelte +37 -37
- package/dist/primitives/Modal/Modal.spec.js +99 -95
- package/dist/primitives/Modal/Modal.stories.svelte +86 -86
- package/dist/primitives/Modal/Modal.svelte +158 -158
- package/dist/primitives/Modal/ModalTestWrapper.svelte +65 -0
- package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts +23 -0
- package/dist/primitives/Modal/ModalTestWrapper.svelte.d.ts.map +1 -0
- package/dist/primitives/NumberInput/NumberInput.spec.d.ts +2 -0
- package/dist/primitives/NumberInput/NumberInput.spec.d.ts.map +1 -0
- package/dist/primitives/NumberInput/NumberInput.spec.js +235 -0
- package/dist/primitives/NumberInput/NumberInput.svelte +106 -106
- package/dist/primitives/Pagination/Pagination.spec.d.ts +2 -0
- package/dist/primitives/Pagination/Pagination.spec.d.ts.map +1 -0
- package/dist/primitives/Pagination/Pagination.spec.js +266 -0
- package/dist/primitives/Pagination/Pagination.stories.svelte +76 -76
- package/dist/primitives/Pagination/Pagination.svelte +261 -261
- package/dist/primitives/Radio/Radio.spec.d.ts +2 -0
- package/dist/primitives/Radio/Radio.spec.d.ts.map +1 -0
- package/dist/primitives/Radio/Radio.spec.js +206 -0
- package/dist/primitives/Radio/Radio.stories.svelte +80 -80
- package/dist/primitives/Radio/Radio.svelte +67 -67
- package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/CardPlaceholder.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/CardPlaceholder.spec.js +156 -0
- package/dist/primitives/Skeleton/CardPlaceholder.svelte +87 -87
- package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/ImagePlaceholder.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/ImagePlaceholder.spec.js +120 -0
- package/dist/primitives/Skeleton/ImagePlaceholder.svelte +59 -59
- package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/ListPlaceholder.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/ListPlaceholder.spec.js +220 -0
- package/dist/primitives/Skeleton/ListPlaceholder.svelte +76 -76
- package/dist/primitives/Skeleton/Skeleton.spec.d.ts +2 -0
- package/dist/primitives/Skeleton/Skeleton.spec.d.ts.map +1 -0
- package/dist/primitives/Skeleton/Skeleton.spec.js +173 -0
- package/dist/primitives/Skeleton/Skeleton.stories.svelte +151 -151
- package/dist/primitives/Skeleton/Skeleton.svelte +26 -26
- package/dist/primitives/Spinner/Spinner.spec.js +71 -75
- package/dist/primitives/Spinner/Spinner.stories.svelte +29 -29
- package/dist/primitives/Spinner/Spinner.svelte +20 -20
- package/dist/primitives/Tabs/TabItem.spec.d.ts +2 -0
- package/dist/primitives/Tabs/TabItem.spec.d.ts.map +1 -0
- package/dist/primitives/Tabs/TabItem.spec.js +130 -0
- package/dist/primitives/Tabs/TabItem.svelte +49 -49
- package/dist/primitives/Tabs/Tabs.spec.d.ts +2 -0
- package/dist/primitives/Tabs/Tabs.spec.d.ts.map +1 -0
- package/dist/primitives/Tabs/Tabs.spec.js +295 -0
- package/dist/primitives/Tabs/Tabs.stories.svelte +112 -112
- package/dist/primitives/Tabs/Tabs.svelte +123 -123
- package/dist/primitives/Tabs/TabsWithItems.test.svelte +18 -0
- package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts +16 -0
- package/dist/primitives/Tabs/TabsWithItems.test.svelte.d.ts.map +1 -0
- package/dist/primitives/Toggle.spec.js +143 -127
- package/dist/primitives/Toggle.stories.svelte +92 -92
- package/dist/primitives/Toggle.svelte +71 -71
- package/dist/primitives/Typography/Typography.spec.d.ts +2 -0
- package/dist/primitives/Typography/Typography.spec.d.ts.map +1 -0
- package/dist/primitives/Typography/Typography.spec.js +183 -0
- package/dist/primitives/Typography/Typography.svelte +53 -53
- package/dist/primitives/ValidationError.spec.js +103 -103
- package/dist/primitives/ValidationError.stories.svelte +69 -69
- package/dist/primitives/ValidationError.svelte +29 -29
- package/dist/primitives/index.d.ts +1 -0
- package/dist/primitives/index.js +3 -0
- package/dist/recipes/CropImage/CropImage.spec.js +208 -216
- package/dist/recipes/CropImage/CropImage.stories.svelte +104 -104
- package/dist/recipes/CropImage/CropImage.svelte +238 -238
- package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts +2 -0
- package/dist/recipes/ImageUploader/ImageUploader.spec.d.ts.map +1 -0
- package/dist/recipes/ImageUploader/ImageUploader.spec.js +1351 -0
- package/dist/recipes/ImageUploader/ImageUploader.stories.svelte +125 -125
- package/dist/recipes/ImageUploader/ImageUploader.svelte +804 -804
- package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts +2 -0
- package/dist/recipes/SuperLogin/SuperLogin.spec.d.ts.map +1 -0
- package/dist/recipes/SuperLogin/SuperLogin.spec.js +1436 -0
- package/dist/recipes/SuperLogin/SuperLogin.svelte +7 -6
- package/dist/recipes/SuperLogin/SuperLogin.svelte.d.ts.map +1 -1
- package/dist/recipes/Toaster/Toaster.stories.svelte +62 -62
- package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts +2 -0
- package/dist/recipes/feedback/EmptyState/EmptyState.spec.d.ts.map +1 -0
- package/dist/recipes/feedback/EmptyState/EmptyState.spec.js +202 -0
- package/dist/recipes/feedback/EmptyState/EmptyState.svelte +1 -1
- package/dist/recipes/feedback/ErrorDisplay.spec.js +69 -69
- package/dist/recipes/feedback/ErrorDisplay.stories.svelte +101 -101
- package/dist/recipes/feedback/ErrorDisplay.svelte +1 -1
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.spec.js +133 -129
- package/dist/recipes/feedback/StatusIndicator/StatusIndicator.svelte +157 -157
- package/dist/recipes/fields/CheckboxField.spec.d.ts +2 -0
- package/dist/recipes/fields/CheckboxField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/CheckboxField.spec.js +135 -0
- package/dist/recipes/fields/CheckboxField.svelte +85 -85
- package/dist/recipes/fields/FormField.spec.d.ts +2 -0
- package/dist/recipes/fields/FormField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/FormField.spec.js +159 -0
- package/dist/recipes/fields/FormField.svelte +58 -58
- package/dist/recipes/fields/RadioGroup.spec.d.ts +2 -0
- package/dist/recipes/fields/RadioGroup.spec.d.ts.map +1 -0
- package/dist/recipes/fields/RadioGroup.spec.js +199 -0
- package/dist/recipes/fields/RadioGroup.svelte +95 -95
- package/dist/recipes/fields/SelectField.spec.d.ts +2 -0
- package/dist/recipes/fields/SelectField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/SelectField.spec.js +188 -0
- package/dist/recipes/fields/SelectField.svelte +80 -80
- package/dist/recipes/fields/TextareaField.spec.d.ts +2 -0
- package/dist/recipes/fields/TextareaField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/TextareaField.spec.js +205 -0
- package/dist/recipes/fields/TextareaField.svelte +97 -97
- package/dist/recipes/fields/ToggleField.spec.d.ts +2 -0
- package/dist/recipes/fields/ToggleField.spec.d.ts.map +1 -0
- package/dist/recipes/fields/ToggleField.spec.js +153 -0
- package/dist/recipes/fields/ToggleField.svelte +60 -60
- package/dist/recipes/fields/index.js +7 -7
- package/dist/recipes/inputs/MultiSelect.spec.js +258 -257
- package/dist/recipes/inputs/MultiSelect.stories.svelte +133 -133
- package/dist/recipes/inputs/MultiSelect.svelte +256 -249
- package/dist/recipes/inputs/MultiSelect.svelte.d.ts +2 -0
- package/dist/recipes/inputs/MultiSelect.svelte.d.ts.map +1 -1
- package/dist/recipes/inputs/OTPInput.spec.js +251 -238
- package/dist/recipes/inputs/OTPInput.stories.svelte +162 -162
- package/dist/recipes/inputs/OTPInput.svelte +29 -29
- package/dist/recipes/inputs/PasswordInput.spec.d.ts +2 -0
- package/dist/recipes/inputs/PasswordInput.spec.d.ts.map +1 -0
- package/dist/recipes/inputs/PasswordInput.spec.js +410 -0
- package/dist/recipes/inputs/PasswordInput.svelte +22 -22
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.spec.js +245 -165
- package/dist/recipes/inputs/PasswordStrengthIndicator/PasswordStrengthIndicator.svelte +43 -43
- package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte +71 -0
- package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts +9 -0
- package/dist/recipes/inputs/PasswordStrengthIndicator/TestWrapper.svelte.d.ts.map +1 -0
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.spec.js +1139 -193
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.stories.svelte +123 -123
- package/dist/recipes/inputs/PlaceAutocomplete/PlaceAutocomplete.svelte +326 -326
- package/dist/recipes/inputs/Search.spec.d.ts +2 -0
- package/dist/recipes/inputs/Search.spec.d.ts.map +1 -0
- package/dist/recipes/inputs/Search.spec.js +177 -0
- package/dist/recipes/inputs/Search.svelte +37 -37
- package/dist/recipes/inputs/SelectDropdown.spec.d.ts +2 -0
- package/dist/recipes/inputs/SelectDropdown.spec.d.ts.map +1 -0
- package/dist/recipes/inputs/SelectDropdown.spec.js +512 -0
- package/dist/recipes/inputs/SelectDropdown.svelte +57 -57
- package/dist/recipes/modals/AlertModal.spec.d.ts +2 -0
- package/dist/recipes/modals/AlertModal.spec.d.ts.map +1 -0
- package/dist/recipes/modals/AlertModal.spec.js +432 -0
- package/dist/recipes/modals/AlertModal.svelte +130 -130
- package/dist/recipes/modals/ConfirmationModal.spec.js +206 -191
- package/dist/recipes/modals/ConfirmationModal.stories.svelte +119 -119
- package/dist/recipes/modals/ConfirmationModal.svelte +152 -152
- package/dist/recipes/modals/InputModal.spec.d.ts +2 -0
- package/dist/recipes/modals/InputModal.spec.d.ts.map +1 -0
- package/dist/recipes/modals/InputModal.spec.js +872 -0
- package/dist/recipes/modals/InputModal.svelte +182 -182
- package/dist/recipes/modals/ModalStateManager.spec.js +100 -100
- package/dist/recipes/modals/ModalStateManager.svelte +77 -77
- package/dist/recipes/modals/ModalTestWrapper.spec.d.ts +2 -0
- package/dist/recipes/modals/ModalTestWrapper.spec.d.ts.map +1 -0
- package/dist/recipes/modals/ModalTestWrapper.spec.js +502 -0
- package/dist/recipes/modals/ModalTestWrapper.svelte +65 -65
- package/dist/recipes/modals/StatusModal.spec.d.ts +2 -0
- package/dist/recipes/modals/StatusModal.spec.d.ts.map +1 -0
- package/dist/recipes/modals/StatusModal.spec.js +599 -0
- package/dist/recipes/modals/StatusModal.svelte +206 -206
- package/dist/services/EventService.js +75 -75
- package/dist/services/EventService.spec.js +217 -217
- package/dist/services/ShowService.spec.js +345 -342
- package/dist/stores/auth.js +36 -36
- package/dist/stores/auth.spec.js +139 -139
- package/dist/stores/toaster.js +13 -13
- package/dist/stories/ButtonAuditDashboard.spec.d.ts +2 -0
- package/dist/stories/ButtonAuditDashboard.spec.d.ts.map +1 -0
- package/dist/stories/ButtonAuditDashboard.spec.js +913 -0
- package/dist/stories/ButtonAuditReview.spec.d.ts +2 -0
- package/dist/stories/ButtonAuditReview.spec.d.ts.map +1 -0
- package/dist/stories/ButtonAuditReview.spec.js +422 -0
- package/dist/stories/ButtonAuditReview.stories.svelte +14 -14
- package/dist/stories/ButtonAuditReview.svelte +427 -427
- package/dist/stories/ButtonGridView.spec.d.ts +2 -0
- package/dist/stories/ButtonGridView.spec.d.ts.map +1 -0
- package/dist/stories/ButtonGridView.spec.js +667 -0
- package/dist/stories/ButtonShowcase.spec.d.ts +2 -0
- package/dist/stories/ButtonShowcase.spec.d.ts.map +1 -0
- package/dist/stories/ButtonShowcase.spec.js +499 -0
- package/dist/stories/PatternsGallery.spec.d.ts +2 -0
- package/dist/stories/PatternsGallery.spec.d.ts.map +1 -0
- package/dist/stories/PatternsGallery.spec.js +514 -0
- package/dist/stories/PatternsGallery.stories.svelte +19 -19
- package/dist/stories/PatternsGallery.svelte +206 -206
- package/dist/stories/PrimitivesGallery.spec.d.ts +2 -0
- package/dist/stories/PrimitivesGallery.spec.d.ts.map +1 -0
- package/dist/stories/PrimitivesGallery.spec.js +813 -0
- package/dist/stories/PrimitivesGallery.stories.svelte +19 -19
- package/dist/stories/PrimitivesGallery.svelte +725 -725
- package/dist/stories/RecipesGallery.spec.d.ts +2 -0
- package/dist/stories/RecipesGallery.spec.d.ts.map +1 -0
- package/dist/stories/RecipesGallery.spec.js +299 -0
- package/dist/stories/RecipesGallery.stories.svelte +19 -19
- package/dist/stories/RecipesGallery.svelte +271 -271
- package/dist/stories/button-audit-manifest.json +11186 -11186
- package/dist/stripe/useStripeTheme.spec.d.ts +2 -0
- package/dist/stripe/useStripeTheme.spec.d.ts.map +1 -0
- package/dist/stripe/useStripeTheme.spec.js +793 -0
- package/dist/tailwind/preset.cjs +82 -82
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +6 -5
- package/dist/telemetry.spec.js +495 -12
- package/dist/tokens/__tests__/colors.test.d.ts +2 -0
- package/dist/tokens/__tests__/colors.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/colors.test.js +152 -0
- package/dist/tokens/__tests__/radius.test.d.ts +2 -0
- package/dist/tokens/__tests__/radius.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/radius.test.js +118 -0
- package/dist/tokens/__tests__/shadows.test.d.ts +2 -0
- package/dist/tokens/__tests__/shadows.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/shadows.test.js +105 -0
- package/dist/tokens/__tests__/spacing.test.js +11 -8
- package/dist/tokens/__tests__/typography.test.d.ts +2 -0
- package/dist/tokens/__tests__/typography.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/typography.test.js +156 -0
- package/dist/tokens/__tests__/z-index.test.d.ts +2 -0
- package/dist/tokens/__tests__/z-index.test.d.ts.map +1 -0
- package/dist/tokens/__tests__/z-index.test.js +121 -0
- package/dist/tokens/tokens.css +87 -87
- package/dist/utils/apiConfig.spec.js +102 -1
- package/dist/utils/formatters.spec.d.ts +2 -0
- package/dist/utils/formatters.spec.d.ts.map +1 -0
- package/dist/utils/formatters.spec.js +82 -0
- package/dist/utils/transitions.d.ts +24 -0
- package/dist/utils/transitions.d.ts.map +1 -0
- package/dist/utils/transitions.js +62 -0
- package/dist/utils/transitions.spec.d.ts +2 -0
- package/dist/utils/transitions.spec.d.ts.map +1 -0
- package/dist/utils/transitions.spec.js +130 -0
- package/dist/utils/utils.js +354 -354
- 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
|
+
});
|