@aleph-alpha/ui-library 1.9.0 → 1.11.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.
- package/README.md +14 -0
- package/dist/system/index-CkH7HQaa.js +7 -0
- package/dist/system/index-CuHwEAQ_.js +7 -0
- package/dist/system/index.d.ts +1322 -318
- package/dist/system/lib.js +8839 -6993
- package/package.json +2 -1
- package/src/@types/shims-vue.d.ts +5 -0
- package/src/__tests__/placeholder.test.ts +7 -0
- package/src/compositions/UiCompositionPlaceholder/UiCompositionPlaceholder.vue +9 -0
- package/src/compositions/UiCompositionPlaceholder/index.ts +1 -0
- package/src/compositions/UiCompositionPlaceholder/types.ts +8 -0
- package/src/compositions/UiDataTable/UiDataTable.mock.ts +104 -0
- package/src/compositions/UiDataTable/UiDataTable.stories.ts +1575 -0
- package/src/compositions/UiDataTable/UiDataTable.vue +129 -0
- package/src/compositions/UiDataTable/UiDataTableColumnHeader.vue +97 -0
- package/src/compositions/UiDataTable/UiDataTablePagination.vue +147 -0
- package/src/compositions/UiDataTable/UiDataTableToolbar.vue +85 -0
- package/src/compositions/UiDataTable/__tests__/UiDataTable.test.ts +372 -0
- package/src/compositions/UiDataTable/__tests__/UiDataTableColumnHeader.test.ts +217 -0
- package/src/compositions/UiDataTable/__tests__/UiDataTablePagination.test.ts +274 -0
- package/src/compositions/UiDataTable/__tests__/UiDataTableToolbar.test.ts +198 -0
- package/src/compositions/UiDataTable/constants.ts +77 -0
- package/src/compositions/UiDataTable/index.ts +6 -0
- package/src/compositions/UiDataTable/types.ts +39 -0
- package/src/compositions/UiDatePicker/UiDatePicker.stories.ts +976 -0
- package/src/compositions/UiDatePicker/UiDatePicker.vue +193 -0
- package/src/compositions/UiDatePicker/__tests__/UiDatePicker.test.ts +325 -0
- package/src/compositions/UiDatePicker/index.ts +14 -0
- package/src/compositions/UiDatePicker/types.ts +220 -0
- package/src/compositions/index.ts +8 -0
- package/src/foundations/UiPlaceholder/UiPlaceholder.vue +9 -0
- package/src/foundations/UiPlaceholder/index.ts +1 -0
- package/src/foundations/UiPlaceholder/types.ts +8 -0
- package/src/foundations/index.ts +6 -0
- package/src/index.ts +27 -0
- package/src/lib/utils.ts +6 -0
- package/src/primitives/UiAccordion/UiAccordion.stories.ts +476 -0
- package/src/primitives/UiAccordion/UiAccordion.vue +31 -0
- package/src/primitives/UiAccordion/UiAccordionContent.vue +16 -0
- package/src/primitives/UiAccordion/UiAccordionItem.vue +16 -0
- package/src/primitives/UiAccordion/UiAccordionTrigger.vue +23 -0
- package/src/primitives/UiAccordion/__tests__/UiAccordion.test.ts +198 -0
- package/src/primitives/UiAccordion/index.ts +6 -0
- package/src/primitives/UiAccordion/types.ts +95 -0
- package/src/primitives/UiAlert/UiAlert.stories.ts +199 -0
- package/src/primitives/UiAlert/UiAlert.vue +27 -0
- package/src/primitives/UiAlert/UiAlertDescription.vue +13 -0
- package/src/primitives/UiAlert/UiAlertTitle.vue +13 -0
- package/src/primitives/UiAlert/__tests__/UiAlert.test.ts +20 -0
- package/src/primitives/UiAlert/constants.ts +3 -0
- package/src/primitives/UiAlert/index.ts +5 -0
- package/src/primitives/UiAlert/types.ts +14 -0
- package/src/primitives/UiAlertDialog/UiAlertDialog.stories.ts +186 -0
- package/src/primitives/UiAlertDialog/UiAlertDialog.vue +18 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogAction.vue +16 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogCancel.vue +16 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogContent.vue +26 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogDescription.vue +16 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogFooter.vue +13 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogHeader.vue +16 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogTitle.vue +16 -0
- package/src/primitives/UiAlertDialog/UiAlertDialogTrigger.vue +17 -0
- package/src/primitives/UiAlertDialog/__tests__/UiAlertDialog.test.ts +184 -0
- package/src/primitives/UiAlertDialog/index.ts +9 -0
- package/src/primitives/UiAlertDialog/types.ts +83 -0
- package/src/primitives/UiAvatar/UiAvatar.stories.ts +194 -0
- package/src/primitives/UiAvatar/UiAvatar.vue +13 -0
- package/src/primitives/UiAvatar/UiAvatarFallback.vue +13 -0
- package/src/primitives/UiAvatar/UiAvatarImage.vue +14 -0
- package/src/primitives/UiAvatar/__tests__/UiAvatar.test.ts +36 -0
- package/src/primitives/UiAvatar/index.ts +3 -0
- package/src/primitives/UiAvatar/types.ts +17 -0
- package/src/primitives/UiBadge/UiBadge.stories.ts +373 -0
- package/src/primitives/UiBadge/UiBadge.vue +21 -0
- package/src/primitives/UiBadge/__tests__/UiBadge.test.ts +44 -0
- package/src/primitives/UiBadge/constants.ts +3 -0
- package/src/primitives/UiBadge/index.ts +2 -0
- package/src/primitives/UiBadge/types.ts +48 -0
- package/src/primitives/UiButton/UiButton.stories.ts +537 -0
- package/src/primitives/UiButton/UiButton.vue +72 -0
- package/src/primitives/UiButton/__tests__/UiButton.test.ts +133 -0
- package/src/primitives/UiButton/index.ts +2 -0
- package/src/primitives/UiButton/types.ts +87 -0
- package/src/primitives/UiCalendar/UiCalendar.stories.ts +797 -0
- package/src/primitives/UiCalendar/UiCalendar.vue +67 -0
- package/src/primitives/UiCalendar/__tests__/UiCalendar.test.ts +45 -0
- package/src/primitives/UiCalendar/index.ts +15 -0
- package/src/primitives/UiCalendar/types.ts +236 -0
- package/src/primitives/UiCard/UiCard.stories.ts +197 -0
- package/src/primitives/UiCard/UiCard.vue +13 -0
- package/src/primitives/UiCard/UiCardAction.vue +13 -0
- package/src/primitives/UiCard/UiCardContent.vue +13 -0
- package/src/primitives/UiCard/UiCardDescription.vue +13 -0
- package/src/primitives/UiCard/UiCardFooter.vue +13 -0
- package/src/primitives/UiCard/UiCardHeader.vue +13 -0
- package/src/primitives/UiCard/UiCardTitle.vue +13 -0
- package/src/primitives/UiCard/__tests__/UiCard.test.ts +19 -0
- package/src/primitives/UiCard/__tests__/UiCardAction.test.ts +19 -0
- package/src/primitives/UiCard/__tests__/UiCardContent.test.ts +19 -0
- package/src/primitives/UiCard/__tests__/UiCardDescription.test.ts +19 -0
- package/src/primitives/UiCard/__tests__/UiCardFooter.test.ts +19 -0
- package/src/primitives/UiCard/__tests__/UiCardHeader.test.ts +19 -0
- package/src/primitives/UiCard/__tests__/UiCardTitle.test.ts +19 -0
- package/src/primitives/UiCard/index.ts +7 -0
- package/src/primitives/UiCard/types.ts +10 -0
- package/src/primitives/UiCheckbox/UiCheckbox.stories.ts +231 -0
- package/src/primitives/UiCheckbox/UiCheckbox.vue +19 -0
- package/src/primitives/UiCheckbox/__tests__/UiCheckbox.test.ts +29 -0
- package/src/primitives/UiCheckbox/index.ts +2 -0
- package/src/primitives/UiCheckbox/types.ts +30 -0
- package/src/primitives/UiDrawer/UiDrawer.stories.ts +602 -0
- package/src/primitives/UiDrawer/UiDrawer.vue +19 -0
- package/src/primitives/UiDrawer/UiDrawerClose.vue +16 -0
- package/src/primitives/UiDrawer/UiDrawerContent.vue +29 -0
- package/src/primitives/UiDrawer/UiDrawerDescription.vue +16 -0
- package/src/primitives/UiDrawer/UiDrawerFooter.vue +16 -0
- package/src/primitives/UiDrawer/UiDrawerHeader.vue +16 -0
- package/src/primitives/UiDrawer/UiDrawerTitle.vue +16 -0
- package/src/primitives/UiDrawer/UiDrawerTrigger.vue +16 -0
- package/src/primitives/UiDrawer/__tests__/UiDrawer.test.ts +229 -0
- package/src/primitives/UiDrawer/index.ts +8 -0
- package/src/primitives/UiDrawer/types.ts +96 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenu.stories.ts +760 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenu.vue +25 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuCheckboxItem.vue +29 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuContent.vue +27 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuGroup.vue +13 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuItem.vue +26 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuLabel.vue +18 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuRadioGroup.vue +20 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuRadioItem.vue +26 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuSeparator.vue +11 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuShortcut.vue +13 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuSub.vue +26 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuSubContent.vue +23 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuSubTrigger.vue +24 -0
- package/src/primitives/UiDropdownMenu/UiDropdownMenuTrigger.vue +24 -0
- package/src/primitives/UiDropdownMenu/__tests__/UiDropdownMenu.test.ts +557 -0
- package/src/primitives/UiDropdownMenu/index.ts +16 -0
- package/src/primitives/UiDropdownMenu/types.ts +219 -0
- package/src/primitives/UiField/UiField.stories.ts +1496 -0
- package/src/primitives/UiField/UiField.vue +18 -0
- package/src/primitives/UiField/UiFieldContent.vue +13 -0
- package/src/primitives/UiField/UiFieldDescription.vue +13 -0
- package/src/primitives/UiField/UiFieldError.vue +20 -0
- package/src/primitives/UiField/UiFieldGroup.vue +13 -0
- package/src/primitives/UiField/UiFieldLabel.vue +16 -0
- package/src/primitives/UiField/UiFieldLegend.vue +13 -0
- package/src/primitives/UiField/UiFieldSeparator.vue +13 -0
- package/src/primitives/UiField/UiFieldSet.vue +13 -0
- package/src/primitives/UiField/UiFieldTitle.vue +13 -0
- package/src/primitives/UiField/__tests__/UiFieldError.test.ts +35 -0
- package/src/primitives/UiField/index.ts +10 -0
- package/src/primitives/UiField/types.ts +47 -0
- package/src/primitives/UiIcon/UiIcon.stories.ts +95 -0
- package/src/primitives/UiIcon/UiIcon.vue +14 -0
- package/src/primitives/UiIcon/__tests__/UiIcon.test.ts +24 -0
- package/src/primitives/UiIcon/index.ts +1 -0
- package/src/primitives/UiIcon/types.ts +23 -0
- package/src/primitives/UiIconButton/UiIconButton.stories.ts +446 -0
- package/src/primitives/UiIconButton/UiIconButton.vue +63 -0
- package/src/primitives/UiIconButton/__tests__/UiIconButton.test.ts +102 -0
- package/src/primitives/UiIconButton/index.ts +2 -0
- package/src/primitives/UiIconButton/types.ts +67 -0
- package/src/primitives/UiInput/UiInput.stories.ts +193 -0
- package/src/primitives/UiInput/UiInput.vue +19 -0
- package/src/primitives/UiInput/__tests__/UiInput.test.ts +38 -0
- package/src/primitives/UiInput/index.ts +2 -0
- package/src/primitives/UiInput/types.ts +31 -0
- package/src/primitives/UiPopover/UiPopover.stories.ts +394 -0
- package/src/primitives/UiPopover/UiPopover.vue +17 -0
- package/src/primitives/UiPopover/UiPopoverContent.vue +27 -0
- package/src/primitives/UiPopover/UiPopoverTrigger.vue +16 -0
- package/src/primitives/UiPopover/__tests__/UiPopover.test.ts +87 -0
- package/src/primitives/UiPopover/index.ts +5 -0
- package/src/primitives/UiPopover/types.ts +86 -0
- package/src/primitives/UiProgress/UiProgress.stories.ts +92 -0
- package/src/primitives/UiProgress/UiProgress.vue +25 -0
- package/src/primitives/UiProgress/__tests__/UiProgress.test.ts +46 -0
- package/src/primitives/UiProgress/index.ts +2 -0
- package/src/primitives/UiProgress/types.ts +16 -0
- package/src/primitives/UiRadioGroup/UiRadioGroup.stories.ts +291 -0
- package/src/primitives/UiRadioGroup/UiRadioGroup.vue +43 -0
- package/src/primitives/UiRadioGroup/UiRadioGroupItem.vue +18 -0
- package/src/primitives/UiRadioGroup/__tests__/UiRadioGroup.test.ts +404 -0
- package/src/primitives/UiRadioGroup/index.ts +4 -0
- package/src/primitives/UiRadioGroup/types.ts +66 -0
- package/src/primitives/UiRangeCalendar/UiRangeCalendar.stories.ts +609 -0
- package/src/primitives/UiRangeCalendar/UiRangeCalendar.vue +50 -0
- package/src/primitives/UiRangeCalendar/__tests__/UiRangeCalendar.test.ts +35 -0
- package/src/primitives/UiRangeCalendar/index.ts +13 -0
- package/src/primitives/UiRangeCalendar/types.ts +184 -0
- package/src/primitives/UiSelect/UiSelect.stories.ts +425 -0
- package/src/primitives/UiSelect/UiSelect.vue +47 -0
- package/src/primitives/UiSelect/UiSelectContent.vue +30 -0
- package/src/primitives/UiSelect/UiSelectGroup.vue +13 -0
- package/src/primitives/UiSelect/UiSelectItem.vue +19 -0
- package/src/primitives/UiSelect/UiSelectLabel.vue +13 -0
- package/src/primitives/UiSelect/UiSelectSeparator.vue +11 -0
- package/src/primitives/UiSelect/UiSelectTrigger.vue +30 -0
- package/src/primitives/UiSelect/UiSelectValue.vue +18 -0
- package/src/primitives/UiSelect/__tests__/UiSelect.test.ts +211 -0
- package/src/primitives/UiSelect/__tests__/UiSelectContent.test.ts +30 -0
- package/src/primitives/UiSelect/__tests__/UiSelectGroup.test.ts +85 -0
- package/src/primitives/UiSelect/__tests__/UiSelectItem.test.ts +79 -0
- package/src/primitives/UiSelect/__tests__/UiSelectLabel.test.ts +83 -0
- package/src/primitives/UiSelect/__tests__/UiSelectSeparator.test.ts +82 -0
- package/src/primitives/UiSelect/__tests__/UiSelectTrigger.test.ts +54 -0
- package/src/primitives/UiSelect/__tests__/UiSelectValue.test.ts +39 -0
- package/src/primitives/UiSelect/index.ts +10 -0
- package/src/primitives/UiSelect/types.ts +93 -0
- package/src/primitives/UiSlider/UiSlider.stories.ts +226 -0
- package/src/primitives/UiSlider/UiSlider.vue +44 -0
- package/src/primitives/UiSlider/__tests__/UiSlider.test.ts +76 -0
- package/src/primitives/UiSlider/index.ts +1 -0
- package/src/primitives/UiSlider/types.ts +101 -0
- package/src/primitives/UiSpinner/UiSpinner.stories.ts +143 -0
- package/src/primitives/UiSpinner/UiSpinner.vue +16 -0
- package/src/primitives/UiSpinner/__tests__/UiSpinner.test.ts +19 -0
- package/src/primitives/UiSpinner/index.ts +2 -0
- package/src/primitives/UiSpinner/types.ts +16 -0
- package/src/primitives/UiSwitch/UiSwitch.stories.ts +120 -0
- package/src/primitives/UiSwitch/UiSwitch.vue +21 -0
- package/src/primitives/UiSwitch/__tests__/UiSwitch.test.ts +47 -0
- package/src/primitives/UiSwitch/index.ts +2 -0
- package/src/primitives/UiSwitch/types.ts +25 -0
- package/src/primitives/UiTable/UiTable.stories.ts +505 -0
- package/src/primitives/UiTable/UiTable.vue +13 -0
- package/src/primitives/UiTable/UiTableBody.vue +13 -0
- package/src/primitives/UiTable/UiTableCaption.vue +13 -0
- package/src/primitives/UiTable/UiTableCell.vue +16 -0
- package/src/primitives/UiTable/UiTableEmpty.vue +18 -0
- package/src/primitives/UiTable/UiTableFooter.vue +13 -0
- package/src/primitives/UiTable/UiTableHead.vue +18 -0
- package/src/primitives/UiTable/UiTableHeader.vue +13 -0
- package/src/primitives/UiTable/UiTableRow.vue +18 -0
- package/src/primitives/UiTable/__tests__/UiTable.test.ts +19 -0
- package/src/primitives/UiTable/__tests__/UiTableBody.test.ts +19 -0
- package/src/primitives/UiTable/__tests__/UiTableCaption.test.ts +19 -0
- package/src/primitives/UiTable/__tests__/UiTableCell.test.ts +26 -0
- package/src/primitives/UiTable/__tests__/UiTableEmpty.test.ts +32 -0
- package/src/primitives/UiTable/__tests__/UiTableFooter.test.ts +19 -0
- package/src/primitives/UiTable/__tests__/UiTableHead.test.ts +43 -0
- package/src/primitives/UiTable/__tests__/UiTableHeader.test.ts +19 -0
- package/src/primitives/UiTable/__tests__/UiTableRow.test.ts +32 -0
- package/src/primitives/UiTable/index.ts +16 -0
- package/src/primitives/UiTable/types.ts +68 -0
- package/src/primitives/UiTabs/UiTabs.stories.ts +456 -0
- package/src/primitives/UiTabs/UiTabs.vue +31 -0
- package/src/primitives/UiTabs/UiTabsContent.vue +16 -0
- package/src/primitives/UiTabs/UiTabsList.vue +16 -0
- package/src/primitives/UiTabs/UiTabsTrigger.vue +16 -0
- package/src/primitives/UiTabs/__tests__/UiTabs.test.ts +122 -0
- package/src/primitives/UiTabs/index.ts +6 -0
- package/src/primitives/UiTabs/types.ts +68 -0
- package/src/primitives/UiTextarea/UiTextarea.stories.ts +107 -0
- package/src/primitives/UiTextarea/UiTextarea.vue +19 -0
- package/src/primitives/UiTextarea/__tests__/UiTextarea.test.ts +40 -0
- package/src/primitives/UiTextarea/index.ts +2 -0
- package/src/primitives/UiTextarea/types.ts +30 -0
- package/src/primitives/UiTooltip/UiTooltip.stories.ts +550 -0
- package/src/primitives/UiTooltip/UiTooltip.vue +42 -0
- package/src/primitives/UiTooltip/__tests__/UiTooltip.test.ts +78 -0
- package/src/primitives/UiTooltip/index.ts +2 -0
- package/src/primitives/UiTooltip/types.ts +53 -0
- package/src/primitives/index.ts +33 -0
- package/src/primitives/shadcn/accordion/Accordion.vue +15 -0
- package/src/primitives/shadcn/accordion/AccordionContent.vue +23 -0
- package/src/primitives/shadcn/accordion/AccordionItem.vue +24 -0
- package/src/primitives/shadcn/accordion/AccordionTrigger.vue +35 -0
- package/src/primitives/shadcn/accordion/index.ts +4 -0
- package/src/primitives/shadcn/alert/Alert.vue +17 -0
- package/src/primitives/shadcn/alert/AlertDescription.vue +22 -0
- package/src/primitives/shadcn/alert/AlertTitle.vue +17 -0
- package/src/primitives/shadcn/alert/index.ts +24 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialog.vue +15 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogAction.vue +18 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogCancel.vue +21 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogContent.vue +44 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogDescription.vue +21 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogFooter.vue +17 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogHeader.vue +17 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogTitle.vue +21 -0
- package/src/primitives/shadcn/alert-dialog/AlertDialogTrigger.vue +12 -0
- package/src/primitives/shadcn/alert-dialog/index.ts +9 -0
- package/src/primitives/shadcn/avatar/Avatar.vue +18 -0
- package/src/primitives/shadcn/avatar/AvatarFallback.vue +21 -0
- package/src/primitives/shadcn/avatar/AvatarImage.vue +12 -0
- package/src/primitives/shadcn/avatar/index.ts +3 -0
- package/src/primitives/shadcn/badge/Badge.vue +28 -0
- package/src/primitives/shadcn/badge/index.ts +24 -0
- package/src/primitives/shadcn/button/Button.vue +29 -0
- package/src/primitives/shadcn/button/index.ts +36 -0
- package/src/primitives/shadcn/calendar/Calendar.vue +206 -0
- package/src/primitives/shadcn/calendar/CalendarCell.vue +28 -0
- package/src/primitives/shadcn/calendar/CalendarCellTrigger.vue +44 -0
- package/src/primitives/shadcn/calendar/CalendarGrid.vue +23 -0
- package/src/primitives/shadcn/calendar/CalendarGridBody.vue +12 -0
- package/src/primitives/shadcn/calendar/CalendarGridHead.vue +13 -0
- package/src/primitives/shadcn/calendar/CalendarGridRow.vue +23 -0
- package/src/primitives/shadcn/calendar/CalendarHeadCell.vue +23 -0
- package/src/primitives/shadcn/calendar/CalendarHeader.vue +23 -0
- package/src/primitives/shadcn/calendar/CalendarHeading.vue +30 -0
- package/src/primitives/shadcn/calendar/CalendarNextButton.vue +33 -0
- package/src/primitives/shadcn/calendar/CalendarPrevButton.vue +33 -0
- package/src/primitives/shadcn/calendar/index.ts +14 -0
- package/src/primitives/shadcn/card/Card.vue +22 -0
- package/src/primitives/shadcn/card/CardAction.vue +17 -0
- package/src/primitives/shadcn/card/CardContent.vue +14 -0
- package/src/primitives/shadcn/card/CardDescription.vue +14 -0
- package/src/primitives/shadcn/card/CardFooter.vue +14 -0
- package/src/primitives/shadcn/card/CardHeader.vue +22 -0
- package/src/primitives/shadcn/card/CardTitle.vue +14 -0
- package/src/primitives/shadcn/card/index.ts +7 -0
- package/src/primitives/shadcn/checkbox/Checkbox.vue +38 -0
- package/src/primitives/shadcn/checkbox/index.ts +1 -0
- package/src/primitives/shadcn/drawer/Drawer.vue +15 -0
- package/src/primitives/shadcn/drawer/DrawerClose.vue +12 -0
- package/src/primitives/shadcn/drawer/DrawerContent.vue +52 -0
- package/src/primitives/shadcn/drawer/DrawerDescription.vue +20 -0
- package/src/primitives/shadcn/drawer/DrawerFooter.vue +17 -0
- package/src/primitives/shadcn/drawer/DrawerHeader.vue +17 -0
- package/src/primitives/shadcn/drawer/DrawerTitle.vue +20 -0
- package/src/primitives/shadcn/drawer/DrawerTrigger.vue +12 -0
- package/src/primitives/shadcn/drawer/index.ts +8 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenu.vue +15 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuCheckboxItem.vue +41 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuContent.vue +40 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuGroup.vue +12 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuItem.vue +41 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuLabel.vue +25 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuRadioGroup.vue +15 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuRadioItem.vue +38 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuSeparator.vue +23 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuShortcut.vue +17 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuSub.vue +15 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuSubContent.vue +29 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuSubTrigger.vue +31 -0
- package/src/primitives/shadcn/dropdown-menu/DropdownMenuTrigger.vue +14 -0
- package/src/primitives/shadcn/dropdown-menu/index.ts +16 -0
- package/src/primitives/shadcn/field/Field.vue +22 -0
- package/src/primitives/shadcn/field/FieldContent.vue +17 -0
- package/src/primitives/shadcn/field/FieldDescription.vue +24 -0
- package/src/primitives/shadcn/field/FieldError.vue +69 -0
- package/src/primitives/shadcn/field/FieldGroup.vue +22 -0
- package/src/primitives/shadcn/field/FieldLabel.vue +28 -0
- package/src/primitives/shadcn/field/FieldLegend.vue +26 -0
- package/src/primitives/shadcn/field/FieldSeparator.vue +28 -0
- package/src/primitives/shadcn/field/FieldSet.vue +23 -0
- package/src/primitives/shadcn/field/FieldTitle.vue +22 -0
- package/src/primitives/shadcn/field/index.ts +39 -0
- package/src/primitives/shadcn/icon/Icon.vue +38 -0
- package/src/primitives/shadcn/icon/index.ts +1 -0
- package/src/primitives/shadcn/index.ts +3 -0
- package/src/primitives/shadcn/input/Input.vue +35 -0
- package/src/primitives/shadcn/input/index.ts +1 -0
- package/src/primitives/shadcn/label/Label.vue +28 -0
- package/src/primitives/shadcn/label/index.ts +1 -0
- package/src/primitives/shadcn/native-select/NativeSelect.vue +56 -0
- package/src/primitives/shadcn/native-select/NativeSelectOptGroup.vue +18 -0
- package/src/primitives/shadcn/native-select/NativeSelectOption.vue +18 -0
- package/src/primitives/shadcn/native-select/index.ts +3 -0
- package/src/primitives/shadcn/popover/Popover.vue +19 -0
- package/src/primitives/shadcn/popover/PopoverContent.vue +41 -0
- package/src/primitives/shadcn/popover/PopoverTrigger.vue +11 -0
- package/src/primitives/shadcn/popover/index.ts +4 -0
- package/src/primitives/shadcn/progress/Progress.vue +30 -0
- package/src/primitives/shadcn/progress/index.ts +1 -0
- package/src/primitives/shadcn/radio-group/RadioGroup.vue +25 -0
- package/src/primitives/shadcn/radio-group/RadioGroupItem.vue +38 -0
- package/src/primitives/shadcn/radio-group/index.ts +2 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendar.vue +73 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarCell.vue +28 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarCellTrigger.vue +46 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarGrid.vue +23 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarGridBody.vue +12 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarGridHead.vue +12 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarGridRow.vue +23 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarHeadCell.vue +23 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarHeader.vue +23 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarHeading.vue +30 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarNextButton.vue +34 -0
- package/src/primitives/shadcn/range-calendar/RangeCalendarPrevButton.vue +34 -0
- package/src/primitives/shadcn/range-calendar/index.ts +12 -0
- package/src/primitives/shadcn/select/Select.vue +15 -0
- package/src/primitives/shadcn/select/SelectContent.vue +55 -0
- package/src/primitives/shadcn/select/SelectGroup.vue +12 -0
- package/src/primitives/shadcn/select/SelectItem.vue +39 -0
- package/src/primitives/shadcn/select/SelectItemText.vue +12 -0
- package/src/primitives/shadcn/select/SelectLabel.vue +17 -0
- package/src/primitives/shadcn/select/SelectScrollDownButton.vue +26 -0
- package/src/primitives/shadcn/select/SelectScrollUpButton.vue +26 -0
- package/src/primitives/shadcn/select/SelectSeparator.vue +19 -0
- package/src/primitives/shadcn/select/SelectTrigger.vue +37 -0
- package/src/primitives/shadcn/select/SelectValue.vue +12 -0
- package/src/primitives/shadcn/select/index.ts +11 -0
- package/src/primitives/shadcn/separator/Separator.vue +27 -0
- package/src/primitives/shadcn/separator/index.ts +1 -0
- package/src/primitives/shadcn/slider/Slider.vue +45 -0
- package/src/primitives/shadcn/slider/index.ts +1 -0
- package/src/primitives/shadcn/spinner/Spinner.vue +18 -0
- package/src/primitives/shadcn/spinner/index.ts +1 -0
- package/src/primitives/shadcn/switch/Switch.vue +40 -0
- package/src/primitives/shadcn/switch/index.ts +1 -0
- package/src/primitives/shadcn/table/Table.vue +16 -0
- package/src/primitives/shadcn/table/TableBody.vue +14 -0
- package/src/primitives/shadcn/table/TableCaption.vue +14 -0
- package/src/primitives/shadcn/table/TableCell.vue +26 -0
- package/src/primitives/shadcn/table/TableEmpty.vue +29 -0
- package/src/primitives/shadcn/table/TableFooter.vue +17 -0
- package/src/primitives/shadcn/table/TableHead.vue +28 -0
- package/src/primitives/shadcn/table/TableHeader.vue +14 -0
- package/src/primitives/shadcn/table/TableRow.vue +21 -0
- package/src/primitives/shadcn/table/index.ts +9 -0
- package/src/primitives/shadcn/table/utils.ts +8 -0
- package/src/primitives/shadcn/tabs/Tabs.vue +24 -0
- package/src/primitives/shadcn/tabs/TabsContent.vue +21 -0
- package/src/primitives/shadcn/tabs/TabsList.vue +26 -0
- package/src/primitives/shadcn/tabs/TabsTrigger.vue +28 -0
- package/src/primitives/shadcn/tabs/index.ts +4 -0
- package/src/primitives/shadcn/textarea/Textarea.vue +33 -0
- package/src/primitives/shadcn/textarea/index.ts +1 -0
- package/src/primitives/shadcn/tooltip/Tooltip.vue +15 -0
- package/src/primitives/shadcn/tooltip/TooltipContent.vue +40 -0
- package/src/primitives/shadcn/tooltip/TooltipProvider.vue +12 -0
- package/src/primitives/shadcn/tooltip/TooltipTrigger.vue +12 -0
- package/src/primitives/shadcn/tooltip/index.ts +4 -0
- package/src/styles/global.css +1 -0
- package/src/templates/UiTemplatePlaceholder/UiTemplatePlaceholder.vue +9 -0
- package/src/templates/UiTemplatePlaceholder/index.ts +1 -0
- package/src/templates/UiTemplatePlaceholder/types.ts +8 -0
- package/src/templates/index.ts +6 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { Calendar } from 'lucide-vue-next';
|
|
3
|
+
import { DateFormatter, getLocalTimeZone } from '@internationalized/date';
|
|
4
|
+
import type { DateRange, DateValue } from 'reka-ui';
|
|
5
|
+
import { computed, defineAsyncComponent, ref } from 'vue';
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
import { UiButton } from '@/primitives/UiButton';
|
|
8
|
+
import { UiPopover, UiPopoverContent, UiPopoverTrigger } from '@/primitives/UiPopover';
|
|
9
|
+
import type { UiDatePickerProps } from './types';
|
|
10
|
+
|
|
11
|
+
defineOptions({
|
|
12
|
+
name: 'UiDatePicker',
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Lazy-load calendar components to reduce bundle size.
|
|
16
|
+
// Using v-if/v-else ensures only the needed component is loaded,
|
|
17
|
+
// while maintaining full TypeScript type safety.
|
|
18
|
+
const UiCalendar = defineAsyncComponent(() =>
|
|
19
|
+
import('@/primitives/UiCalendar').then((m) => m.UiCalendar),
|
|
20
|
+
);
|
|
21
|
+
const UiRangeCalendar = defineAsyncComponent(() =>
|
|
22
|
+
import('@/primitives/UiRangeCalendar').then((m) => m.UiRangeCalendar),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const props = withDefaults(defineProps<UiDatePickerProps>(), {
|
|
26
|
+
mode: 'single',
|
|
27
|
+
disabled: false,
|
|
28
|
+
readonly: false,
|
|
29
|
+
locale: 'en',
|
|
30
|
+
weekStartsOn: 0,
|
|
31
|
+
numberOfMonths: 1,
|
|
32
|
+
pagedNavigation: false,
|
|
33
|
+
weekdayFormat: 'narrow',
|
|
34
|
+
fixedWeeks: false,
|
|
35
|
+
preventDeselect: false,
|
|
36
|
+
closeOnSelect: true,
|
|
37
|
+
dateFormat: () => ({ dateStyle: 'long' }),
|
|
38
|
+
rangeSeparator: ' - ',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const modelValue = defineModel<UiDatePickerProps['modelValue']>();
|
|
42
|
+
const open = defineModel<boolean>('open');
|
|
43
|
+
|
|
44
|
+
// Internal state for uncontrolled mode (when defaultValue is provided without v-model)
|
|
45
|
+
const internalValue = ref<UiDatePickerProps['modelValue']>(props.defaultValue);
|
|
46
|
+
const internalOpen = ref(false);
|
|
47
|
+
|
|
48
|
+
// Uncontrolled mode: defaultValue provided, use internal state
|
|
49
|
+
// Controlled mode: no defaultValue, use v-model
|
|
50
|
+
const isUncontrolled = props.defaultValue !== undefined;
|
|
51
|
+
|
|
52
|
+
const effectiveValue = computed(() => (isUncontrolled ? internalValue.value : modelValue.value));
|
|
53
|
+
const effectiveOpen = computed({
|
|
54
|
+
get: () => open.value ?? internalOpen.value,
|
|
55
|
+
set: (value: boolean) => {
|
|
56
|
+
open.value = value;
|
|
57
|
+
internalOpen.value = value;
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const isDateRange = (value: unknown): value is DateRange => {
|
|
62
|
+
return value !== null && typeof value === 'object' && 'start' in value && 'end' in value;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Type-safe computed values for each mode (avoids type assertions in template)
|
|
66
|
+
// Cast needed because TypeScript can't narrow the DateValue | DateRange union
|
|
67
|
+
const singleValue = computed((): DateValue | undefined => {
|
|
68
|
+
const value = effectiveValue.value as DateValue | DateRange | undefined;
|
|
69
|
+
return props.mode === 'single' && value && !isDateRange(value) ? value : undefined;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const rangeValue = computed((): DateRange | undefined => {
|
|
73
|
+
const value = effectiveValue.value as DateValue | DateRange | undefined;
|
|
74
|
+
return props.mode === 'range' && value && isDateRange(value) ? value : undefined;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const dateFormatter = computed(() => new DateFormatter(props.locale, props.dateFormat));
|
|
78
|
+
|
|
79
|
+
const computedPlaceholder = computed(() => {
|
|
80
|
+
if (props.placeholder) return props.placeholder;
|
|
81
|
+
return props.mode === 'range' ? 'Pick a date range' : 'Pick a date';
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const displayValue = computed(() => {
|
|
85
|
+
if (props.mode === 'range') {
|
|
86
|
+
const range = rangeValue.value;
|
|
87
|
+
if (!range?.start) return computedPlaceholder.value;
|
|
88
|
+
|
|
89
|
+
const startStr = dateFormatter.value.format(range.start.toDate(getLocalTimeZone()));
|
|
90
|
+
if (!range.end) return startStr;
|
|
91
|
+
|
|
92
|
+
const endStr = dateFormatter.value.format(range.end.toDate(getLocalTimeZone()));
|
|
93
|
+
return `${startStr}${props.rangeSeparator}${endStr}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const single = singleValue.value;
|
|
97
|
+
if (!single) return computedPlaceholder.value;
|
|
98
|
+
return dateFormatter.value.format(single.toDate(getLocalTimeZone()));
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
const updateValue = (value: UiDatePickerProps['modelValue']) => {
|
|
102
|
+
if (isUncontrolled) {
|
|
103
|
+
internalValue.value = value;
|
|
104
|
+
} else {
|
|
105
|
+
modelValue.value = value;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleSingleDateUpdate = (value: DateValue | DateValue[] | undefined) => {
|
|
110
|
+
// Handle array case (shouldn't happen in single mode, but type-safe)
|
|
111
|
+
const newValue = Array.isArray(value) ? value[0] : value;
|
|
112
|
+
updateValue(newValue);
|
|
113
|
+
|
|
114
|
+
if (props.closeOnSelect) {
|
|
115
|
+
effectiveOpen.value = false;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleRangeDateUpdate = (value: DateRange | undefined) => {
|
|
120
|
+
updateValue(value);
|
|
121
|
+
// Close only when both start and end dates are selected
|
|
122
|
+
if (props.closeOnSelect && value?.start && value?.end) {
|
|
123
|
+
effectiveOpen.value = false;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
</script>
|
|
127
|
+
|
|
128
|
+
<template>
|
|
129
|
+
<UiPopover v-model:open="effectiveOpen">
|
|
130
|
+
<UiPopoverTrigger as-child>
|
|
131
|
+
<UiButton
|
|
132
|
+
variant="outline"
|
|
133
|
+
:class="
|
|
134
|
+
cn(
|
|
135
|
+
props.mode === 'range' ? 'w-[300px]' : 'w-[240px]',
|
|
136
|
+
'justify-start text-left font-normal',
|
|
137
|
+
!effectiveValue && 'text-muted-foreground',
|
|
138
|
+
props.class,
|
|
139
|
+
)
|
|
140
|
+
"
|
|
141
|
+
:disabled="props.disabled"
|
|
142
|
+
:aria-label="props.ariaLabel"
|
|
143
|
+
>
|
|
144
|
+
<Calendar class="mr-2 size-4 shrink-0" />
|
|
145
|
+
<span class="truncate">{{ displayValue }}</span>
|
|
146
|
+
</UiButton>
|
|
147
|
+
</UiPopoverTrigger>
|
|
148
|
+
<UiPopoverContent class="w-auto p-0" align="start">
|
|
149
|
+
<Suspense>
|
|
150
|
+
<UiCalendar
|
|
151
|
+
v-if="props.mode === 'single'"
|
|
152
|
+
:model-value="singleValue"
|
|
153
|
+
:min-value="props.minValue"
|
|
154
|
+
:max-value="props.maxValue"
|
|
155
|
+
:disabled="props.disabled"
|
|
156
|
+
:readonly="props.readonly"
|
|
157
|
+
:locale="props.locale"
|
|
158
|
+
:week-starts-on="props.weekStartsOn"
|
|
159
|
+
:number-of-months="props.numberOfMonths"
|
|
160
|
+
:weekday-format="props.weekdayFormat"
|
|
161
|
+
:fixed-weeks="props.fixedWeeks"
|
|
162
|
+
:layout="props.layout"
|
|
163
|
+
:prevent-deselect="props.preventDeselect"
|
|
164
|
+
:calendar-label="props.calendarLabel"
|
|
165
|
+
:is-date-disabled="props.isDateDisabled"
|
|
166
|
+
:is-date-unavailable="props.isDateUnavailable"
|
|
167
|
+
initial-focus
|
|
168
|
+
@update:model-value="handleSingleDateUpdate"
|
|
169
|
+
/>
|
|
170
|
+
<UiRangeCalendar
|
|
171
|
+
v-else
|
|
172
|
+
:model-value="rangeValue"
|
|
173
|
+
:min-value="props.minValue"
|
|
174
|
+
:max-value="props.maxValue"
|
|
175
|
+
:disabled="props.disabled"
|
|
176
|
+
:readonly="props.readonly"
|
|
177
|
+
:locale="props.locale"
|
|
178
|
+
:week-starts-on="props.weekStartsOn"
|
|
179
|
+
:number-of-months="props.numberOfMonths"
|
|
180
|
+
:paged-navigation="props.pagedNavigation"
|
|
181
|
+
:weekday-format="props.weekdayFormat"
|
|
182
|
+
:fixed-weeks="props.fixedWeeks"
|
|
183
|
+
:prevent-deselect="props.preventDeselect"
|
|
184
|
+
:calendar-label="props.calendarLabel"
|
|
185
|
+
:is-date-disabled="props.isDateDisabled"
|
|
186
|
+
:is-date-unavailable="props.isDateUnavailable"
|
|
187
|
+
initial-focus
|
|
188
|
+
@update:model-value="handleRangeDateUpdate"
|
|
189
|
+
/>
|
|
190
|
+
</Suspense>
|
|
191
|
+
</UiPopoverContent>
|
|
192
|
+
</UiPopover>
|
|
193
|
+
</template>
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { CalendarDate } from '@internationalized/date';
|
|
2
|
+
import { render } from '@testing-library/vue';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import type { DateRange } from 'reka-ui';
|
|
5
|
+
import { describe, expect, test } from 'vitest';
|
|
6
|
+
import UiDatePicker from '../UiDatePicker.vue';
|
|
7
|
+
|
|
8
|
+
describe('UiDatePicker', () => {
|
|
9
|
+
const testDate = new CalendarDate(2025, 1, 15);
|
|
10
|
+
|
|
11
|
+
test('renders with custom class on trigger button', () => {
|
|
12
|
+
const { getByRole } = render(UiDatePicker, {
|
|
13
|
+
props: { class: 'w-full custom-picker' },
|
|
14
|
+
});
|
|
15
|
+
const button = getByRole('button');
|
|
16
|
+
expect(button).toHaveClass('w-full', 'custom-picker');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('displays placeholder when no date is selected', () => {
|
|
20
|
+
const { getByRole } = render(UiDatePicker, {
|
|
21
|
+
props: { placeholder: 'Select a date' },
|
|
22
|
+
});
|
|
23
|
+
expect(getByRole('button')).toHaveTextContent('Select a date');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('displays formatted date when modelValue is set', () => {
|
|
27
|
+
const { getByRole } = render(UiDatePicker, {
|
|
28
|
+
props: { modelValue: testDate, locale: 'en' },
|
|
29
|
+
});
|
|
30
|
+
expect(getByRole('button')).toHaveTextContent('January 15, 2025');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('applies disabled state to trigger button', () => {
|
|
34
|
+
const { getByRole } = render(UiDatePicker, {
|
|
35
|
+
props: { disabled: true },
|
|
36
|
+
});
|
|
37
|
+
expect(getByRole('button')).toBeDisabled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('emits update:modelValue with a DateValue when date is selected', async () => {
|
|
41
|
+
const { getByRole, findAllByRole, emitted } = render(UiDatePicker, {
|
|
42
|
+
props: { modelValue: testDate },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Open the popover
|
|
46
|
+
await userEvent.click(getByRole('button'));
|
|
47
|
+
|
|
48
|
+
// Find and click a date cell (use findAllByRole to wait for async component)
|
|
49
|
+
const cells = await findAllByRole('gridcell');
|
|
50
|
+
const clickableCell = cells.find(
|
|
51
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
52
|
+
);
|
|
53
|
+
const dateButton = clickableCell?.querySelector('button');
|
|
54
|
+
expect(dateButton).toBeInTheDocument();
|
|
55
|
+
|
|
56
|
+
await userEvent.click(dateButton!);
|
|
57
|
+
|
|
58
|
+
const emittedEvents = emitted('update:modelValue');
|
|
59
|
+
expect(emittedEvents).toHaveLength(1);
|
|
60
|
+
const emittedValue = emittedEvents[0][0];
|
|
61
|
+
expect(emittedValue).toHaveProperty('year');
|
|
62
|
+
expect(emittedValue).toHaveProperty('month');
|
|
63
|
+
expect(emittedValue).toHaveProperty('day');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('displays defaultValue immediately in uncontrolled mode', () => {
|
|
67
|
+
const { getByRole } = render(UiDatePicker, {
|
|
68
|
+
props: { defaultValue: testDate, locale: 'en' },
|
|
69
|
+
});
|
|
70
|
+
// Should display the default value, not the placeholder
|
|
71
|
+
expect(getByRole('button')).toHaveTextContent('January 15, 2025');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('updates display after selection in uncontrolled mode', async () => {
|
|
75
|
+
const { getByRole, findAllByRole, emitted } = render(UiDatePicker, {
|
|
76
|
+
props: { defaultValue: testDate, locale: 'en' },
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Initially shows the default value
|
|
80
|
+
expect(getByRole('button')).toHaveTextContent('January 15, 2025');
|
|
81
|
+
|
|
82
|
+
// Open and select a different date (use findAllByRole to wait for async component)
|
|
83
|
+
await userEvent.click(getByRole('button'));
|
|
84
|
+
const cells = await findAllByRole('gridcell');
|
|
85
|
+
const clickableCell = cells.find(
|
|
86
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
87
|
+
);
|
|
88
|
+
await userEvent.click(clickableCell!.querySelector('button')!);
|
|
89
|
+
|
|
90
|
+
// Display should update to show the newly selected date (not the placeholder)
|
|
91
|
+
const buttonText = getByRole('button').textContent;
|
|
92
|
+
expect(buttonText).not.toBe('Pick a date');
|
|
93
|
+
expect(buttonText).not.toBe('January 15, 2025'); // Should be different from initial
|
|
94
|
+
|
|
95
|
+
// In uncontrolled mode, update:modelValue should NOT be emitted
|
|
96
|
+
expect(emitted('update:modelValue')).toBeUndefined();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('closes popover after date selection when closeOnSelect is true', async () => {
|
|
100
|
+
const { getByRole, findAllByRole, queryByRole } = render(UiDatePicker, {
|
|
101
|
+
props: { closeOnSelect: true },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Open the popover
|
|
105
|
+
await userEvent.click(getByRole('button'));
|
|
106
|
+
expect(queryByRole('dialog')).toBeInTheDocument();
|
|
107
|
+
|
|
108
|
+
// Select a date (use findAllByRole to wait for async component)
|
|
109
|
+
const cells = await findAllByRole('gridcell');
|
|
110
|
+
const clickableCell = cells.find(
|
|
111
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
112
|
+
);
|
|
113
|
+
await userEvent.click(clickableCell!.querySelector('button')!);
|
|
114
|
+
|
|
115
|
+
// Popover should be closed
|
|
116
|
+
expect(queryByRole('dialog')).not.toBeInTheDocument();
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('keeps popover open after date selection when closeOnSelect is false', async () => {
|
|
120
|
+
const { getByRole, findAllByRole, queryByRole } = render(UiDatePicker, {
|
|
121
|
+
props: { closeOnSelect: false },
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// Open the popover
|
|
125
|
+
await userEvent.click(getByRole('button'));
|
|
126
|
+
expect(queryByRole('dialog')).toBeInTheDocument();
|
|
127
|
+
|
|
128
|
+
// Select a date (use findAllByRole to wait for async component)
|
|
129
|
+
const cells = await findAllByRole('gridcell');
|
|
130
|
+
const clickableCell = cells.find(
|
|
131
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
132
|
+
);
|
|
133
|
+
await userEvent.click(clickableCell!.querySelector('button')!);
|
|
134
|
+
|
|
135
|
+
// Popover should remain open
|
|
136
|
+
expect(queryByRole('dialog')).toBeInTheDocument();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('formats date according to locale', () => {
|
|
140
|
+
const { getByRole } = render(UiDatePicker, {
|
|
141
|
+
props: { modelValue: testDate, locale: 'de' },
|
|
142
|
+
});
|
|
143
|
+
// German format: "15. Januar 2025"
|
|
144
|
+
expect(getByRole('button')).toHaveTextContent('15. Januar 2025');
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test('defaults to "Pick a date" placeholder in single mode', () => {
|
|
148
|
+
const { getByRole } = render(UiDatePicker, {
|
|
149
|
+
props: { mode: 'single' },
|
|
150
|
+
});
|
|
151
|
+
expect(getByRole('button')).toHaveTextContent('Pick a date');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test('defaults to "Pick a date range" placeholder in range mode', () => {
|
|
155
|
+
const { getByRole } = render(UiDatePicker, {
|
|
156
|
+
props: { mode: 'range' },
|
|
157
|
+
});
|
|
158
|
+
expect(getByRole('button')).toHaveTextContent('Pick a date range');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('uses custom placeholder in range mode', () => {
|
|
162
|
+
const { getByRole } = render(UiDatePicker, {
|
|
163
|
+
props: { mode: 'range', placeholder: 'Select travel dates' },
|
|
164
|
+
});
|
|
165
|
+
expect(getByRole('button')).toHaveTextContent('Select travel dates');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
test('displays formatted date range when modelValue is set in range mode', () => {
|
|
169
|
+
const dateRange: DateRange = {
|
|
170
|
+
start: new CalendarDate(2025, 1, 15),
|
|
171
|
+
end: new CalendarDate(2025, 1, 20),
|
|
172
|
+
};
|
|
173
|
+
const { getByRole } = render(UiDatePicker, {
|
|
174
|
+
props: { mode: 'range', modelValue: dateRange, locale: 'en' },
|
|
175
|
+
});
|
|
176
|
+
expect(getByRole('button')).toHaveTextContent('January 15, 2025 - January 20, 2025');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('displays partial date range when only start date is selected', () => {
|
|
180
|
+
const dateRange: DateRange = {
|
|
181
|
+
start: new CalendarDate(2025, 1, 15),
|
|
182
|
+
end: undefined,
|
|
183
|
+
};
|
|
184
|
+
const { getByRole } = render(UiDatePicker, {
|
|
185
|
+
props: { mode: 'range', modelValue: dateRange, locale: 'en' },
|
|
186
|
+
});
|
|
187
|
+
expect(getByRole('button')).toHaveTextContent('January 15, 2025');
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('emits update:modelValue with a DateRange when range is selected', async () => {
|
|
191
|
+
const initialRange: DateRange = { start: undefined, end: undefined };
|
|
192
|
+
const { getByRole, findAllByRole, emitted } = render(UiDatePicker, {
|
|
193
|
+
props: { mode: 'range', modelValue: initialRange },
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// Open the popover
|
|
197
|
+
await userEvent.click(getByRole('button'));
|
|
198
|
+
|
|
199
|
+
// Find and click two date cells (use findAllByRole to wait for async component)
|
|
200
|
+
const cells = await findAllByRole('gridcell');
|
|
201
|
+
const clickableCells = cells.filter(
|
|
202
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
// Click first date (start)
|
|
206
|
+
await userEvent.click(clickableCells[5]?.querySelector('button')!);
|
|
207
|
+
|
|
208
|
+
// Click second date (end)
|
|
209
|
+
await userEvent.click(clickableCells[10]?.querySelector('button')!);
|
|
210
|
+
|
|
211
|
+
const emittedEvents = emitted('update:modelValue');
|
|
212
|
+
expect(emittedEvents.length).toBeGreaterThanOrEqual(1);
|
|
213
|
+
const lastEmittedValue = emittedEvents[emittedEvents.length - 1][0];
|
|
214
|
+
expect(lastEmittedValue).toHaveProperty('start');
|
|
215
|
+
expect(lastEmittedValue).toHaveProperty('end');
|
|
216
|
+
expect(lastEmittedValue.start).toHaveProperty('year');
|
|
217
|
+
expect(lastEmittedValue.end).toHaveProperty('year');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('keeps popover open in range mode until both dates are selected when closeOnSelect is true', async () => {
|
|
221
|
+
const { getByRole, findAllByRole, queryByRole } = render(UiDatePicker, {
|
|
222
|
+
props: { mode: 'range', closeOnSelect: true },
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// Open the popover
|
|
226
|
+
await userEvent.click(getByRole('button'));
|
|
227
|
+
expect(queryByRole('dialog')).toBeInTheDocument();
|
|
228
|
+
|
|
229
|
+
// Select first date (use findAllByRole to wait for async component)
|
|
230
|
+
const cells = await findAllByRole('gridcell');
|
|
231
|
+
const clickableCells = cells.filter(
|
|
232
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
233
|
+
);
|
|
234
|
+
await userEvent.click(clickableCells[5]?.querySelector('button')!);
|
|
235
|
+
|
|
236
|
+
// Popover should still be open (only start date selected)
|
|
237
|
+
expect(queryByRole('dialog')).toBeInTheDocument();
|
|
238
|
+
|
|
239
|
+
// Select second date
|
|
240
|
+
await userEvent.click(clickableCells[10]?.querySelector('button')!);
|
|
241
|
+
|
|
242
|
+
// Now popover should close (both dates selected)
|
|
243
|
+
expect(queryByRole('dialog')).not.toBeInTheDocument();
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('lazy loading optimization', () => {
|
|
247
|
+
test('lazily loads UiCalendar in single mode when popover opens', async () => {
|
|
248
|
+
const { getByRole, queryByRole, findByRole } = render(UiDatePicker, {
|
|
249
|
+
props: { mode: 'single', modelValue: new CalendarDate(2025, 1, 15) },
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// Before opening, popover content is not rendered
|
|
253
|
+
expect(queryByRole('dialog')).not.toBeInTheDocument();
|
|
254
|
+
|
|
255
|
+
// Open the popover
|
|
256
|
+
await userEvent.click(getByRole('button'));
|
|
257
|
+
|
|
258
|
+
// Calendar should be rendered after async load (findByRole waits for element)
|
|
259
|
+
await findByRole('dialog');
|
|
260
|
+
await findByRole('grid');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test('lazily loads UiRangeCalendar in range mode when popover opens', async () => {
|
|
264
|
+
const { getByRole, queryByRole, findByRole } = render(UiDatePicker, {
|
|
265
|
+
props: { mode: 'range' },
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
// Before opening, popover content is not rendered
|
|
269
|
+
expect(queryByRole('dialog')).not.toBeInTheDocument();
|
|
270
|
+
|
|
271
|
+
// Open the popover
|
|
272
|
+
await userEvent.click(getByRole('button'));
|
|
273
|
+
|
|
274
|
+
// RangeCalendar should be rendered after async load (findByRole waits for element)
|
|
275
|
+
await findByRole('dialog');
|
|
276
|
+
await findByRole('grid');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test('single mode calendar is fully functional after lazy load', async () => {
|
|
280
|
+
const { getByRole, findAllByRole, emitted } = render(UiDatePicker, {
|
|
281
|
+
props: { mode: 'single', modelValue: new CalendarDate(2025, 1, 15) },
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
// Open popover and wait for async component
|
|
285
|
+
await userEvent.click(getByRole('button'));
|
|
286
|
+
|
|
287
|
+
// Wait for calendar grid cells to load (findAllByRole waits for elements)
|
|
288
|
+
const cells = await findAllByRole('gridcell');
|
|
289
|
+
const clickableCell = cells.find(
|
|
290
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
291
|
+
);
|
|
292
|
+
await userEvent.click(clickableCell!.querySelector('button')!);
|
|
293
|
+
|
|
294
|
+
// Should emit the selected date
|
|
295
|
+
const emittedEvents = emitted('update:modelValue');
|
|
296
|
+
expect(emittedEvents).toHaveLength(1);
|
|
297
|
+
expect(emittedEvents[0][0]).toHaveProperty('year');
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('range mode calendar is fully functional after lazy load', async () => {
|
|
301
|
+
const { getByRole, findAllByRole, emitted } = render(UiDatePicker, {
|
|
302
|
+
props: { mode: 'range' },
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Open popover and wait for async component
|
|
306
|
+
await userEvent.click(getByRole('button'));
|
|
307
|
+
|
|
308
|
+
// Wait for calendar grid cells to load (findAllByRole waits for elements)
|
|
309
|
+
const cells = await findAllByRole('gridcell');
|
|
310
|
+
const clickableCells = cells.filter(
|
|
311
|
+
(cell) => !cell.querySelector('button')?.hasAttribute('data-disabled'),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
await userEvent.click(clickableCells[5]?.querySelector('button')!);
|
|
315
|
+
await userEvent.click(clickableCells[10]?.querySelector('button')!);
|
|
316
|
+
|
|
317
|
+
// Should emit the date range
|
|
318
|
+
const emittedEvents = emitted('update:modelValue');
|
|
319
|
+
expect(emittedEvents.length).toBeGreaterThanOrEqual(1);
|
|
320
|
+
const lastEmitted = emittedEvents[emittedEvents.length - 1][0];
|
|
321
|
+
expect(lastEmitted).toHaveProperty('start');
|
|
322
|
+
expect(lastEmitted).toHaveProperty('end');
|
|
323
|
+
});
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { default as UiDatePicker } from './UiDatePicker.vue';
|
|
2
|
+
|
|
3
|
+
export type {
|
|
4
|
+
UiDatePickerProps,
|
|
5
|
+
UiDatePickerMode,
|
|
6
|
+
UiDatePickerLayout,
|
|
7
|
+
UiDatePickerWeekdayFormat,
|
|
8
|
+
UiDatePickerWeekStartsOn,
|
|
9
|
+
DateRange,
|
|
10
|
+
DateValue,
|
|
11
|
+
Matcher,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
export { CalendarDate, getLocalTimeZone, today } from '@internationalized/date';
|