@etus/ui 0.1.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/dist/chunk-HRNDJU7D.js +11 -0
- package/dist/chunk-HRNDJU7D.js.map +1 -0
- package/dist/index.d.ts +21250 -0
- package/dist/index.js +44886 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/utils.d.ts +5 -0
- package/dist/lib/utils.js +7 -0
- package/dist/lib/utils.js.map +1 -0
- package/package.json +145 -0
- package/src/components/CLAUDE.md +66 -0
- package/src/components/advanced/AssetManager/AssetManager.test.tsx +624 -0
- package/src/components/advanced/AssetManager/AssetManager.tsx +928 -0
- package/src/components/advanced/AssetManager/AssetManager.types.ts +248 -0
- package/src/components/advanced/AssetManager/AssetManager.variants.ts +284 -0
- package/src/components/advanced/AssetManager/README.md +246 -0
- package/src/components/advanced/AssetManager/index.ts +4 -0
- package/src/components/advanced/CLAUDE.md +77 -0
- package/src/components/advanced/Calendar/Calendar.test.tsx +718 -0
- package/src/components/advanced/Calendar/Calendar.tsx +264 -0
- package/src/components/advanced/Calendar/Calendar.types.ts +62 -0
- package/src/components/advanced/Calendar/Calendar.variants.ts +154 -0
- package/src/components/advanced/Calendar/README.md +389 -0
- package/src/components/advanced/Calendar/index.ts +3 -0
- package/src/components/advanced/Command/Command.test.tsx +1014 -0
- package/src/components/advanced/Command/Command.tsx +330 -0
- package/src/components/advanced/Command/Command.types.ts +238 -0
- package/src/components/advanced/Command/Command.variants.ts +300 -0
- package/src/components/advanced/Command/README.md +1259 -0
- package/src/components/advanced/Command/index.ts +45 -0
- package/src/components/advanced/DragAndDrop/DragAndDrop.test.tsx +573 -0
- package/src/components/advanced/DragAndDrop/DragAndDrop.tsx +495 -0
- package/src/components/advanced/DragAndDrop/DragAndDrop.types.ts +237 -0
- package/src/components/advanced/DragAndDrop/DragAndDrop.variants.ts +156 -0
- package/src/components/advanced/DragAndDrop/README.md +284 -0
- package/src/components/advanced/DragAndDrop/index.ts +4 -0
- package/src/components/advanced/ErrorBoundary/ErrorBoundary.test.tsx +372 -0
- package/src/components/advanced/ErrorBoundary/ErrorBoundary.tsx +184 -0
- package/src/components/advanced/ErrorBoundary/ErrorBoundary.types.ts +73 -0
- package/src/components/advanced/ErrorBoundary/ErrorBoundary.variants.ts +153 -0
- package/src/components/advanced/ErrorBoundary/README.md +296 -0
- package/src/components/advanced/ErrorBoundary/index.ts +18 -0
- package/src/components/advanced/EventCalendar/EventCalendar.test.tsx +535 -0
- package/src/components/advanced/EventCalendar/EventCalendar.tsx +1066 -0
- package/src/components/advanced/EventCalendar/EventCalendar.types.ts +232 -0
- package/src/components/advanced/EventCalendar/EventCalendar.variants.ts +222 -0
- package/src/components/advanced/EventCalendar/README.md +330 -0
- package/src/components/advanced/EventCalendar/index.ts +35 -0
- package/src/components/advanced/FileDropzone/FileDropzone.test.tsx +344 -0
- package/src/components/advanced/FileDropzone/FileDropzone.tsx +188 -0
- package/src/components/advanced/FileDropzone/FileDropzone.types.ts +47 -0
- package/src/components/advanced/FileDropzone/FileDropzone.variants.ts +46 -0
- package/src/components/advanced/FileDropzone/README.md +359 -0
- package/src/components/advanced/FileDropzone/index.ts +7 -0
- package/src/components/advanced/FilePreview/FilePreview.test.tsx +503 -0
- package/src/components/advanced/FilePreview/FilePreview.tsx +385 -0
- package/src/components/advanced/FilePreview/FilePreview.types.ts +90 -0
- package/src/components/advanced/FilePreview/FilePreview.variants.ts +234 -0
- package/src/components/advanced/FilePreview/README.md +318 -0
- package/src/components/advanced/FilePreview/index.ts +25 -0
- package/src/components/advanced/FileUpload/FileUpload.test.tsx +676 -0
- package/src/components/advanced/FileUpload/FileUpload.tsx +845 -0
- package/src/components/advanced/FileUpload/FileUpload.types.ts +270 -0
- package/src/components/advanced/FileUpload/FileUpload.variants.ts +216 -0
- package/src/components/advanced/FileUpload/README.md +380 -0
- package/src/components/advanced/FileUpload/index.ts +39 -0
- package/src/components/advanced/FilterBuilder/FilterBuilder.test.tsx +644 -0
- package/src/components/advanced/FilterBuilder/FilterBuilder.tsx +881 -0
- package/src/components/advanced/FilterBuilder/FilterBuilder.types.ts +249 -0
- package/src/components/advanced/FilterBuilder/FilterBuilder.variants.ts +307 -0
- package/src/components/advanced/FilterBuilder/README.md +383 -0
- package/src/components/advanced/FilterBuilder/index.ts +48 -0
- package/src/components/advanced/MarkdownEditor/MarkdownEditor.test.tsx +468 -0
- package/src/components/advanced/MarkdownEditor/MarkdownEditor.tsx +556 -0
- package/src/components/advanced/MarkdownEditor/MarkdownEditor.types.ts +108 -0
- package/src/components/advanced/MarkdownEditor/MarkdownEditor.variants.ts +62 -0
- package/src/components/advanced/MarkdownEditor/README.md +352 -0
- package/src/components/advanced/MarkdownEditor/index.ts +15 -0
- package/src/components/advanced/Portal/Portal.test.tsx +406 -0
- package/src/components/advanced/Portal/Portal.tsx +77 -0
- package/src/components/advanced/Portal/Portal.types.ts +28 -0
- package/src/components/advanced/Portal/Portal.variants.ts +95 -0
- package/src/components/advanced/Portal/README.md +326 -0
- package/src/components/advanced/Portal/index.ts +9 -0
- package/src/components/advanced/RichTextEditor/README.md +383 -0
- package/src/components/advanced/RichTextEditor/RichTextEditor.test.tsx +394 -0
- package/src/components/advanced/RichTextEditor/RichTextEditor.tsx +651 -0
- package/src/components/advanced/RichTextEditor/RichTextEditor.types.ts +77 -0
- package/src/components/advanced/RichTextEditor/RichTextEditor.variants.ts +151 -0
- package/src/components/advanced/RichTextEditor/index.ts +24 -0
- package/src/components/advanced/Search/README.md +391 -0
- package/src/components/advanced/Search/Search.test.tsx +567 -0
- package/src/components/advanced/Search/Search.tsx +829 -0
- package/src/components/advanced/Search/Search.types.ts +243 -0
- package/src/components/advanced/Search/Search.variants.ts +358 -0
- package/src/components/advanced/Search/index.ts +52 -0
- package/src/components/advanced/SortableList/README.md +448 -0
- package/src/components/advanced/SortableList/SortableList.test.tsx +317 -0
- package/src/components/advanced/SortableList/SortableList.tsx +392 -0
- package/src/components/advanced/SortableList/SortableList.types.ts +122 -0
- package/src/components/advanced/SortableList/SortableList.variants.ts +141 -0
- package/src/components/advanced/SortableList/index.ts +33 -0
- package/src/components/advanced/VersionControl/README.md +440 -0
- package/src/components/advanced/VersionControl/VersionControl.test.tsx +517 -0
- package/src/components/advanced/VersionControl/VersionControl.tsx +758 -0
- package/src/components/advanced/VersionControl/VersionControl.types.ts +176 -0
- package/src/components/advanced/VersionControl/VersionControl.variants.ts +203 -0
- package/src/components/advanced/VersionControl/index.ts +33 -0
- package/src/components/advanced/index.ts +17 -0
- package/src/components/data-display/Accordion/Accordion.test.tsx +519 -0
- package/src/components/data-display/Accordion/Accordion.tsx +164 -0
- package/src/components/data-display/Accordion/Accordion.types.ts +37 -0
- package/src/components/data-display/Accordion/Accordion.variants.ts +112 -0
- package/src/components/data-display/Accordion/README.md +976 -0
- package/src/components/data-display/Accordion/index.ts +31 -0
- package/src/components/data-display/AreaChart/AreaChart.test.tsx +529 -0
- package/src/components/data-display/AreaChart/AreaChart.tsx +318 -0
- package/src/components/data-display/AreaChart/AreaChart.types.ts +169 -0
- package/src/components/data-display/AreaChart/AreaChart.variants.ts +45 -0
- package/src/components/data-display/AreaChart/README.md +229 -0
- package/src/components/data-display/AreaChart/index.ts +2 -0
- package/src/components/data-display/Banner/Banner.test.tsx +648 -0
- package/src/components/data-display/Banner/Banner.tsx +111 -0
- package/src/components/data-display/Banner/Banner.types.ts +51 -0
- package/src/components/data-display/Banner/Banner.variants.ts +37 -0
- package/src/components/data-display/Banner/README.md +351 -0
- package/src/components/data-display/Banner/index.ts +2 -0
- package/src/components/data-display/BarChart/BarChart.test.tsx +536 -0
- package/src/components/data-display/BarChart/BarChart.tsx +305 -0
- package/src/components/data-display/BarChart/BarChart.types.ts +145 -0
- package/src/components/data-display/BarChart/BarChart.variants.ts +61 -0
- package/src/components/data-display/BarChart/README.md +205 -0
- package/src/components/data-display/BarChart/index.ts +2 -0
- package/src/components/data-display/BarList/BarList.tsx +172 -0
- package/src/components/data-display/BarList/BarList.types.ts +69 -0
- package/src/components/data-display/BarList/BarList.variants.ts +63 -0
- package/src/components/data-display/BarList/index.ts +2 -0
- package/src/components/data-display/CLAUDE.md +90 -0
- package/src/components/data-display/Callout/Callout.test.tsx +516 -0
- package/src/components/data-display/Callout/Callout.tsx +112 -0
- package/src/components/data-display/Callout/Callout.types.ts +37 -0
- package/src/components/data-display/Callout/Callout.variants.ts +48 -0
- package/src/components/data-display/Callout/README.md +341 -0
- package/src/components/data-display/Callout/index.ts +2 -0
- package/src/components/data-display/Carousel/Carousel.test.tsx +386 -0
- package/src/components/data-display/Carousel/Carousel.tsx +241 -0
- package/src/components/data-display/Carousel/Carousel.types.ts +20 -0
- package/src/components/data-display/Carousel/Carousel.variants.ts +125 -0
- package/src/components/data-display/Carousel/README.md +711 -0
- package/src/components/data-display/Carousel/index.ts +1 -0
- package/src/components/data-display/CategoryBar/CategoryBar.tsx +220 -0
- package/src/components/data-display/CategoryBar/CategoryBar.types.ts +80 -0
- package/src/components/data-display/CategoryBar/CategoryBar.variants.ts +85 -0
- package/src/components/data-display/CategoryBar/index.ts +2 -0
- package/src/components/data-display/Chart/Chart.test.tsx +432 -0
- package/src/components/data-display/Chart/Chart.tsx +476 -0
- package/src/components/data-display/Chart/Chart.types.ts +24 -0
- package/src/components/data-display/Chart/Chart.variants.ts +106 -0
- package/src/components/data-display/Chart/README.md +674 -0
- package/src/components/data-display/Chart/index.ts +1 -0
- package/src/components/data-display/ChartCard/ChartCard.test.tsx +327 -0
- package/src/components/data-display/ChartCard/ChartCard.tsx +201 -0
- package/src/components/data-display/ChartCard/ChartCard.types.ts +68 -0
- package/src/components/data-display/ChartCard/ChartCard.variants.ts +63 -0
- package/src/components/data-display/ChartCard/index.ts +17 -0
- package/src/components/data-display/ChartLegend/ChartLegend.test.tsx +165 -0
- package/src/components/data-display/ChartLegend/ChartLegend.types.ts +88 -0
- package/src/components/data-display/ChartLegend/README.md +317 -0
- package/src/components/data-display/ChartLegend/index.ts +22 -0
- package/src/components/data-display/ComboChart/ComboChart.tsx +380 -0
- package/src/components/data-display/ComboChart/ComboChart.types.ts +224 -0
- package/src/components/data-display/ComboChart/index.ts +2 -0
- package/src/components/data-display/DashboardCard/DashboardCard.test.tsx +289 -0
- package/src/components/data-display/DashboardCard/DashboardCard.tsx +285 -0
- package/src/components/data-display/DashboardCard/DashboardCard.types.ts +74 -0
- package/src/components/data-display/DashboardCard/DashboardCard.variants.ts +67 -0
- package/src/components/data-display/DashboardCard/index.ts +25 -0
- package/src/components/data-display/DashboardFilterbar/DashboardFilterbar.tsx +151 -0
- package/src/components/data-display/DashboardFilterbar/DashboardFilterbar.types.ts +39 -0
- package/src/components/data-display/DashboardFilterbar/DashboardFilterbar.variants.ts +30 -0
- package/src/components/data-display/DashboardFilterbar/index.ts +10 -0
- package/src/components/data-display/DataTable/DataTable.test.tsx +654 -0
- package/src/components/data-display/DataTable/DataTable.tsx +529 -0
- package/src/components/data-display/DataTable/DataTable.types.ts +190 -0
- package/src/components/data-display/DataTable/DataTable.variants.ts +79 -0
- package/src/components/data-display/DataTable/README.md +447 -0
- package/src/components/data-display/DataTable/index.ts +2 -0
- package/src/components/data-display/DeltaBar/DeltaBar.tsx +100 -0
- package/src/components/data-display/DeltaBar/DeltaBar.types.ts +57 -0
- package/src/components/data-display/DeltaBar/DeltaBar.variants.ts +40 -0
- package/src/components/data-display/DeltaBar/index.ts +2 -0
- package/src/components/data-display/EmptyState/EmptyState.test.tsx +368 -0
- package/src/components/data-display/EmptyState/EmptyState.tsx +91 -0
- package/src/components/data-display/EmptyState/EmptyState.types.ts +9 -0
- package/src/components/data-display/EmptyState/EmptyState.variants.ts +28 -0
- package/src/components/data-display/EmptyState/README.md +746 -0
- package/src/components/data-display/EmptyState/index.ts +1 -0
- package/src/components/data-display/Feed/Feed.test.tsx +668 -0
- package/src/components/data-display/Feed/Feed.tsx +290 -0
- package/src/components/data-display/Feed/Feed.types.ts +104 -0
- package/src/components/data-display/Feed/Feed.variants.ts +64 -0
- package/src/components/data-display/Feed/README.md +326 -0
- package/src/components/data-display/Feed/index.ts +17 -0
- package/src/components/data-display/FunnelChart/FunnelChart.tsx +177 -0
- package/src/components/data-display/FunnelChart/FunnelChart.types.ts +145 -0
- package/src/components/data-display/FunnelChart/index.ts +2 -0
- package/src/components/data-display/GaugeChart/GaugeChart.test.tsx +129 -0
- package/src/components/data-display/GaugeChart/GaugeChart.tsx +332 -0
- package/src/components/data-display/GaugeChart/GaugeChart.types.ts +53 -0
- package/src/components/data-display/GaugeChart/GaugeChart.variants.ts +56 -0
- package/src/components/data-display/GaugeChart/README.md +305 -0
- package/src/components/data-display/GaugeChart/index.ts +3 -0
- package/src/components/data-display/Heatmap/Heatmap.test.tsx +630 -0
- package/src/components/data-display/Heatmap/Heatmap.tsx +569 -0
- package/src/components/data-display/Heatmap/Heatmap.types.ts +120 -0
- package/src/components/data-display/Heatmap/Heatmap.variants.ts +72 -0
- package/src/components/data-display/Heatmap/README.md +321 -0
- package/src/components/data-display/Heatmap/index.ts +2 -0
- package/src/components/data-display/HoverCard/HoverCard.test.tsx +314 -0
- package/src/components/data-display/HoverCard/HoverCard.tsx +40 -0
- package/src/components/data-display/HoverCard/HoverCard.types.ts +21 -0
- package/src/components/data-display/HoverCard/HoverCard.variants.ts +29 -0
- package/src/components/data-display/HoverCard/README.md +624 -0
- package/src/components/data-display/HoverCard/index.ts +1 -0
- package/src/components/data-display/ImageGallery/ImageGallery.test.tsx +519 -0
- package/src/components/data-display/ImageGallery/ImageGallery.tsx +733 -0
- package/src/components/data-display/ImageGallery/ImageGallery.types.ts +109 -0
- package/src/components/data-display/ImageGallery/ImageGallery.variants.ts +58 -0
- package/src/components/data-display/ImageGallery/README.md +352 -0
- package/src/components/data-display/ImageGallery/index.ts +9 -0
- package/src/components/data-display/Item/Item.test.tsx +476 -0
- package/src/components/data-display/Item/Item.tsx +195 -0
- package/src/components/data-display/Item/Item.types.ts +23 -0
- package/src/components/data-display/Item/Item.variants.ts +51 -0
- package/src/components/data-display/Item/README.md +759 -0
- package/src/components/data-display/Item/index.ts +1 -0
- package/src/components/data-display/KPICard/KPICard.test.tsx +445 -0
- package/src/components/data-display/KPICard/KPICard.tsx +203 -0
- package/src/components/data-display/KPICard/KPICard.types.ts +32 -0
- package/src/components/data-display/KPICard/KPICard.variants.ts +64 -0
- package/src/components/data-display/KPICard/README.md +380 -0
- package/src/components/data-display/KPICard/index.ts +7 -0
- package/src/components/data-display/Lightbox/Lightbox.test.tsx +574 -0
- package/src/components/data-display/Lightbox/Lightbox.tsx +466 -0
- package/src/components/data-display/Lightbox/Lightbox.types.ts +53 -0
- package/src/components/data-display/Lightbox/Lightbox.variants.ts +99 -0
- package/src/components/data-display/Lightbox/README.md +397 -0
- package/src/components/data-display/Lightbox/index.ts +2 -0
- package/src/components/data-display/LineChart/LineChart.test.tsx +654 -0
- package/src/components/data-display/LineChart/LineChart.tsx +295 -0
- package/src/components/data-display/LineChart/LineChart.types.ts +168 -0
- package/src/components/data-display/LineChart/LineChart.variants.ts +52 -0
- package/src/components/data-display/LineChart/README.md +242 -0
- package/src/components/data-display/LineChart/index.ts +8 -0
- package/src/components/data-display/List/List.test.tsx +756 -0
- package/src/components/data-display/List/List.tsx +455 -0
- package/src/components/data-display/List/List.types.ts +67 -0
- package/src/components/data-display/List/List.variants.ts +69 -0
- package/src/components/data-display/List/README.md +374 -0
- package/src/components/data-display/List/index.ts +9 -0
- package/src/components/data-display/MarkerBar/MarkerBar.tsx +130 -0
- package/src/components/data-display/MarkerBar/MarkerBar.types.ts +67 -0
- package/src/components/data-display/MarkerBar/MarkerBar.variants.ts +75 -0
- package/src/components/data-display/MarkerBar/index.ts +3 -0
- package/src/components/data-display/PieChart/PieChart.test.tsx +655 -0
- package/src/components/data-display/PieChart/PieChart.tsx +327 -0
- package/src/components/data-display/PieChart/PieChart.types.ts +134 -0
- package/src/components/data-display/PieChart/PieChart.variants.ts +49 -0
- package/src/components/data-display/PieChart/README.md +261 -0
- package/src/components/data-display/PieChart/index.ts +2 -0
- package/src/components/data-display/ScatterChart/ScatterChart.tsx +310 -0
- package/src/components/data-display/ScatterChart/ScatterChart.types.ts +272 -0
- package/src/components/data-display/ScatterChart/index.ts +2 -0
- package/src/components/data-display/SingleStat/README.md +363 -0
- package/src/components/data-display/SingleStat/SingleStat.test.tsx +223 -0
- package/src/components/data-display/SingleStat/SingleStat.tsx +251 -0
- package/src/components/data-display/SingleStat/SingleStat.types.ts +71 -0
- package/src/components/data-display/SingleStat/SingleStat.variants.ts +51 -0
- package/src/components/data-display/SingleStat/index.ts +6 -0
- package/src/components/data-display/Sparkline/README.md +321 -0
- package/src/components/data-display/Sparkline/Sparkline.test.tsx +276 -0
- package/src/components/data-display/Sparkline/Sparkline.tsx +971 -0
- package/src/components/data-display/Sparkline/Sparkline.types.ts +147 -0
- package/src/components/data-display/Sparkline/Sparkline.variants.ts +52 -0
- package/src/components/data-display/Sparkline/index.ts +9 -0
- package/src/components/data-display/Table/README.md +821 -0
- package/src/components/data-display/Table/Table.test.tsx +732 -0
- package/src/components/data-display/Table/Table.tsx +123 -0
- package/src/components/data-display/Table/Table.types.ts +20 -0
- package/src/components/data-display/Table/Table.variants.ts +123 -0
- package/src/components/data-display/Table/index.ts +1 -0
- package/src/components/data-display/Timeline/README.md +366 -0
- package/src/components/data-display/Timeline/Timeline.test.tsx +701 -0
- package/src/components/data-display/Timeline/Timeline.tsx +328 -0
- package/src/components/data-display/Timeline/Timeline.types.ts +59 -0
- package/src/components/data-display/Timeline/Timeline.variants.ts +152 -0
- package/src/components/data-display/Timeline/index.ts +15 -0
- package/src/components/data-display/Tracker/Tracker.tsx +105 -0
- package/src/components/data-display/Tracker/Tracker.types.ts +45 -0
- package/src/components/data-display/Tracker/Tracker.variants.ts +24 -0
- package/src/components/data-display/Tracker/index.ts +2 -0
- package/src/components/data-display/VirtualTable/README.md +383 -0
- package/src/components/data-display/VirtualTable/VirtualTable.test.tsx +295 -0
- package/src/components/data-display/VirtualTable/VirtualTable.tsx +354 -0
- package/src/components/data-display/VirtualTable/VirtualTable.types.ts +62 -0
- package/src/components/data-display/VirtualTable/VirtualTable.variants.ts +61 -0
- package/src/components/data-display/VirtualTable/index.ts +2 -0
- package/src/components/data-display/index.ts +35 -0
- package/src/components/feedback/Alert/Alert.test.tsx +614 -0
- package/src/components/feedback/Alert/Alert.tsx +48 -0
- package/src/components/feedback/Alert/Alert.types.ts +42 -0
- package/src/components/feedback/Alert/Alert.variants.ts +20 -0
- package/src/components/feedback/Alert/README.md +522 -0
- package/src/components/feedback/Alert/index.ts +3 -0
- package/src/components/feedback/AlertDialog/AlertDialog.test.tsx +1237 -0
- package/src/components/feedback/AlertDialog/AlertDialog.tsx +160 -0
- package/src/components/feedback/AlertDialog/AlertDialog.types.ts +139 -0
- package/src/components/feedback/AlertDialog/README.md +723 -0
- package/src/components/feedback/AlertDialog/index.ts +2 -0
- package/src/components/feedback/CLAUDE.md +87 -0
- package/src/components/feedback/ConfirmModal/ConfirmModal.test.tsx +404 -0
- package/src/components/feedback/ConfirmModal/ConfirmModal.tsx +139 -0
- package/src/components/feedback/ConfirmModal/ConfirmModal.types.ts +117 -0
- package/src/components/feedback/ConfirmModal/README.md +546 -0
- package/src/components/feedback/ConfirmModal/index.ts +2 -0
- package/src/components/feedback/Dialog/Dialog.test.tsx +1070 -0
- package/src/components/feedback/Dialog/Dialog.tsx +137 -0
- package/src/components/feedback/Dialog/Dialog.types.ts +119 -0
- package/src/components/feedback/Dialog/README.md +770 -0
- package/src/components/feedback/Dialog/index.ts +2 -0
- package/src/components/feedback/ErrorPage/ErrorPage.test.tsx +338 -0
- package/src/components/feedback/ErrorPage/ErrorPage.tsx +232 -0
- package/src/components/feedback/ErrorPage/ErrorPage.types.ts +95 -0
- package/src/components/feedback/ErrorPage/README.md +573 -0
- package/src/components/feedback/ErrorPage/index.ts +6 -0
- package/src/components/feedback/FocusTrap/FocusTrap.test.tsx +378 -0
- package/src/components/feedback/FocusTrap/FocusTrap.tsx +115 -0
- package/src/components/feedback/FocusTrap/FocusTrap.types.ts +119 -0
- package/src/components/feedback/FocusTrap/README.md +571 -0
- package/src/components/feedback/FocusTrap/index.ts +2 -0
- package/src/components/feedback/Message/Message.test.tsx +143 -0
- package/src/components/feedback/Message/Message.tsx +66 -0
- package/src/components/feedback/Message/Message.types.ts +44 -0
- package/src/components/feedback/Message/Message.variants.ts +24 -0
- package/src/components/feedback/Message/README.md +522 -0
- package/src/components/feedback/Message/index.ts +2 -0
- package/src/components/feedback/Modal/Modal.test.tsx +475 -0
- package/src/components/feedback/Modal/Modal.tsx +168 -0
- package/src/components/feedback/Modal/Modal.types.ts +91 -0
- package/src/components/feedback/Modal/README.md +517 -0
- package/src/components/feedback/Modal/index.ts +2 -0
- package/src/components/feedback/Notification/Notification.test.tsx +908 -0
- package/src/components/feedback/Notification/Notification.tsx +222 -0
- package/src/components/feedback/Notification/Notification.types.ts +90 -0
- package/src/components/feedback/Notification/Notification.variants.ts +54 -0
- package/src/components/feedback/Notification/README.md +602 -0
- package/src/components/feedback/Notification/index.ts +11 -0
- package/src/components/feedback/Popover/Popover.test.tsx +901 -0
- package/src/components/feedback/Popover/Popover.tsx +60 -0
- package/src/components/feedback/Popover/Popover.types.ts +158 -0
- package/src/components/feedback/Popover/Popover.variants.ts +27 -0
- package/src/components/feedback/Popover/README.md +700 -0
- package/src/components/feedback/Popover/index.ts +2 -0
- package/src/components/feedback/Toast/README.md +613 -0
- package/src/components/feedback/Toast/Toast.test.tsx +1116 -0
- package/src/components/feedback/Toast/Toast.tsx +44 -0
- package/src/components/feedback/Toast/Toast.types.ts +156 -0
- package/src/components/feedback/Toast/index.ts +1 -0
- package/src/components/feedback/Tooltip/README.md +671 -0
- package/src/components/feedback/Tooltip/Tooltip.test.tsx +413 -0
- package/src/components/feedback/Tooltip/Tooltip.tsx +110 -0
- package/src/components/feedback/Tooltip/Tooltip.types.ts +138 -0
- package/src/components/feedback/Tooltip/Tooltip.variants.ts +54 -0
- package/src/components/feedback/Tooltip/index.ts +3 -0
- package/src/components/feedback/index.ts +13 -0
- package/src/components/forms/Autocomplete/Autocomplete.test.tsx +2351 -0
- package/src/components/forms/Autocomplete/Autocomplete.tsx +696 -0
- package/src/components/forms/Autocomplete/Autocomplete.types.ts +211 -0
- package/src/components/forms/Autocomplete/Autocomplete.variants.ts +154 -0
- package/src/components/forms/Autocomplete/README.md +919 -0
- package/src/components/forms/Autocomplete/index.ts +7 -0
- package/src/components/forms/CLAUDE.md +124 -0
- package/src/components/forms/Checkbox/Checkbox.test.tsx +475 -0
- package/src/components/forms/Checkbox/Checkbox.tsx +35 -0
- package/src/components/forms/Checkbox/Checkbox.types.ts +24 -0
- package/src/components/forms/Checkbox/Checkbox.variants.ts +46 -0
- package/src/components/forms/Checkbox/README.md +861 -0
- package/src/components/forms/Checkbox/index.ts +3 -0
- package/src/components/forms/CheckboxGroup/CheckboxGroup.test.tsx +338 -0
- package/src/components/forms/CheckboxGroup/CheckboxGroup.tsx +212 -0
- package/src/components/forms/CheckboxGroup/CheckboxGroup.types.ts +150 -0
- package/src/components/forms/CheckboxGroup/CheckboxGroup.variants.ts +85 -0
- package/src/components/forms/CheckboxGroup/README.md +124 -0
- package/src/components/forms/CheckboxGroup/index.ts +21 -0
- package/src/components/forms/ColorPicker/ColorPicker.test.tsx +916 -0
- package/src/components/forms/ColorPicker/ColorPicker.tsx +282 -0
- package/src/components/forms/ColorPicker/ColorPicker.types.ts +99 -0
- package/src/components/forms/ColorPicker/ColorPicker.variants.ts +84 -0
- package/src/components/forms/ColorPicker/README.md +809 -0
- package/src/components/forms/ColorPicker/index.ts +14 -0
- package/src/components/forms/Combobox/Combobox.test.tsx +975 -0
- package/src/components/forms/Combobox/Combobox.tsx +194 -0
- package/src/components/forms/Combobox/Combobox.types.ts +113 -0
- package/src/components/forms/Combobox/Combobox.variants.ts +108 -0
- package/src/components/forms/Combobox/README.md +923 -0
- package/src/components/forms/Combobox/index.ts +7 -0
- package/src/components/forms/DatePicker/DatePicker.test.tsx +1181 -0
- package/src/components/forms/DatePicker/DatePicker.tsx +503 -0
- package/src/components/forms/DatePicker/DatePicker.types.ts +196 -0
- package/src/components/forms/DatePicker/DatePicker.variants.ts +38 -0
- package/src/components/forms/DatePicker/README.md +821 -0
- package/src/components/forms/DatePicker/index.ts +8 -0
- package/src/components/forms/DateRangePicker/DateRangeInput.tsx +139 -0
- package/src/components/forms/DateRangePicker/DateRangePicker.test.tsx +1684 -0
- package/src/components/forms/DateRangePicker/DateRangePicker.tsx +375 -0
- package/src/components/forms/DateRangePicker/DateRangePicker.types.ts +145 -0
- package/src/components/forms/DateRangePicker/DateRangePicker.variants.ts +133 -0
- package/src/components/forms/DateRangePicker/DateRangePresets.tsx +44 -0
- package/src/components/forms/DateRangePicker/index.ts +25 -0
- package/src/components/forms/DateRangePicker/presets.ts +104 -0
- package/src/components/forms/EmailInput/EmailInput.test.tsx +562 -0
- package/src/components/forms/EmailInput/EmailInput.tsx +59 -0
- package/src/components/forms/EmailInput/EmailInput.types.ts +46 -0
- package/src/components/forms/EmailInput/EmailInput.variants.ts +30 -0
- package/src/components/forms/EmailInput/README.md +708 -0
- package/src/components/forms/EmailInput/index.ts +6 -0
- package/src/components/forms/ErrorMessage/ErrorMessage.test.tsx +457 -0
- package/src/components/forms/ErrorMessage/ErrorMessage.tsx +128 -0
- package/src/components/forms/ErrorMessage/ErrorMessage.types.ts +54 -0
- package/src/components/forms/ErrorMessage/ErrorMessage.variants.ts +78 -0
- package/src/components/forms/ErrorMessage/README.md +855 -0
- package/src/components/forms/ErrorMessage/index.ts +4 -0
- package/src/components/forms/Field/Field.test.tsx +811 -0
- package/src/components/forms/Field/Field.tsx +195 -0
- package/src/components/forms/Field/Field.types.ts +94 -0
- package/src/components/forms/Field/Field.variants.ts +114 -0
- package/src/components/forms/Field/README.md +931 -0
- package/src/components/forms/Field/index.ts +3 -0
- package/src/components/forms/FloatLabel/FloatLabel.test.tsx +248 -0
- package/src/components/forms/FloatLabel/FloatLabel.tsx +110 -0
- package/src/components/forms/FloatLabel/FloatLabel.types.ts +37 -0
- package/src/components/forms/FloatLabel/index.ts +2 -0
- package/src/components/forms/Form/Form.test.tsx +1167 -0
- package/src/components/forms/Form/Form.tsx +170 -0
- package/src/components/forms/Form/Form.types.ts +126 -0
- package/src/components/forms/Form/Form.variants.ts +81 -0
- package/src/components/forms/Form/README.md +787 -0
- package/src/components/forms/Form/index.ts +3 -0
- package/src/components/forms/FormValidation/FormValidation.test.tsx +376 -0
- package/src/components/forms/FormValidation/FormValidation.tsx +99 -0
- package/src/components/forms/FormValidation/FormValidation.types.ts +37 -0
- package/src/components/forms/FormValidation/FormValidation.variants.ts +24 -0
- package/src/components/forms/FormValidation/README.md +592 -0
- package/src/components/forms/FormValidation/index.ts +4 -0
- package/src/components/forms/HelpText/HelpText.test.tsx +558 -0
- package/src/components/forms/HelpText/HelpText.tsx +111 -0
- package/src/components/forms/HelpText/HelpText.types.ts +51 -0
- package/src/components/forms/HelpText/HelpText.variants.ts +54 -0
- package/src/components/forms/HelpText/README.md +739 -0
- package/src/components/forms/HelpText/index.ts +4 -0
- package/src/components/forms/IftaLabel/IftaLabel.test.tsx +193 -0
- package/src/components/forms/IftaLabel/IftaLabel.tsx +72 -0
- package/src/components/forms/IftaLabel/IftaLabel.types.ts +18 -0
- package/src/components/forms/IftaLabel/index.ts +2 -0
- package/src/components/forms/InputGroup/InputGroup.test.tsx +782 -0
- package/src/components/forms/InputGroup/InputGroup.tsx +128 -0
- package/src/components/forms/InputGroup/InputGroup.types.ts +66 -0
- package/src/components/forms/InputGroup/InputGroup.variants.ts +102 -0
- package/src/components/forms/InputGroup/README.md +845 -0
- package/src/components/forms/InputGroup/index.ts +24 -0
- package/src/components/forms/InputOTP/InputOTP.test.tsx +793 -0
- package/src/components/forms/InputOTP/InputOTP.tsx +90 -0
- package/src/components/forms/InputOTP/InputOTP.types.ts +74 -0
- package/src/components/forms/InputOTP/InputOTP.variants.ts +64 -0
- package/src/components/forms/InputOTP/README.md +1149 -0
- package/src/components/forms/InputOTP/index.ts +18 -0
- package/src/components/forms/InputOTPField/InputOTPField.test.tsx +220 -0
- package/src/components/forms/InputOTPField/InputOTPField.tsx +148 -0
- package/src/components/forms/InputOTPField/InputOTPField.types.ts +91 -0
- package/src/components/forms/InputOTPField/InputOTPField.variants.ts +83 -0
- package/src/components/forms/InputOTPField/README.md +195 -0
- package/src/components/forms/InputOTPField/index.ts +12 -0
- package/src/components/forms/MultiSelect/MultiSelect.test.tsx +1036 -0
- package/src/components/forms/MultiSelect/MultiSelect.tsx +291 -0
- package/src/components/forms/MultiSelect/MultiSelect.types.ts +147 -0
- package/src/components/forms/MultiSelect/MultiSelect.variants.ts +132 -0
- package/src/components/forms/MultiSelect/README.md +897 -0
- package/src/components/forms/MultiSelect/index.ts +7 -0
- package/src/components/forms/NativeSelect/NativeSelect.test.tsx +856 -0
- package/src/components/forms/NativeSelect/NativeSelect.tsx +75 -0
- package/src/components/forms/NativeSelect/NativeSelect.types.ts +17 -0
- package/src/components/forms/NativeSelect/NativeSelect.variants.ts +106 -0
- package/src/components/forms/NativeSelect/README.md +1033 -0
- package/src/components/forms/NativeSelect/index.ts +3 -0
- package/src/components/forms/NumberInput/NumberInput.test.tsx +636 -0
- package/src/components/forms/NumberInput/NumberInput.tsx +131 -0
- package/src/components/forms/NumberInput/NumberInput.types.ts +45 -0
- package/src/components/forms/NumberInput/NumberInput.variants.ts +85 -0
- package/src/components/forms/NumberInput/README.md +766 -0
- package/src/components/forms/NumberInput/index.ts +9 -0
- package/src/components/forms/PasswordInput/PasswordInput.test.tsx +525 -0
- package/src/components/forms/PasswordInput/PasswordInput.tsx +67 -0
- package/src/components/forms/PasswordInput/PasswordInput.types.ts +35 -0
- package/src/components/forms/PasswordInput/PasswordInput.variants.ts +34 -0
- package/src/components/forms/PasswordInput/README.md +698 -0
- package/src/components/forms/PasswordInput/index.ts +6 -0
- package/src/components/forms/PaymentInput/PaymentInput.test.tsx +252 -0
- package/src/components/forms/PaymentInput/PaymentInput.tsx +178 -0
- package/src/components/forms/PaymentInput/PaymentInput.types.ts +69 -0
- package/src/components/forms/PaymentInput/PaymentInput.variants.ts +81 -0
- package/src/components/forms/PaymentInput/README.md +263 -0
- package/src/components/forms/PaymentInput/card-utils.ts +219 -0
- package/src/components/forms/PaymentInput/index.ts +23 -0
- package/src/components/forms/PhoneInput/PhoneInput.test.tsx +270 -0
- package/src/components/forms/PhoneInput/PhoneInput.tsx +215 -0
- package/src/components/forms/PhoneInput/PhoneInput.types.ts +74 -0
- package/src/components/forms/PhoneInput/PhoneInput.variants.ts +103 -0
- package/src/components/forms/PhoneInput/README.md +258 -0
- package/src/components/forms/PhoneInput/countries.ts +134 -0
- package/src/components/forms/PhoneInput/index.ts +22 -0
- package/src/components/forms/RadioButton/README.md +832 -0
- package/src/components/forms/RadioButton/RadioButton.test.tsx +583 -0
- package/src/components/forms/RadioButton/RadioButton.tsx +82 -0
- package/src/components/forms/RadioButton/RadioButton.types.ts +23 -0
- package/src/components/forms/RadioButton/RadioButton.variants.ts +80 -0
- package/src/components/forms/RadioButton/index.ts +15 -0
- package/src/components/forms/RadioCardGroup/RadioCardGroup.tsx +130 -0
- package/src/components/forms/RadioCardGroup/RadioCardGroup.types.ts +66 -0
- package/src/components/forms/RadioCardGroup/RadioCardGroup.variants.ts +131 -0
- package/src/components/forms/RadioCardGroup/index.ts +3 -0
- package/src/components/forms/RadioGroup/README.md +908 -0
- package/src/components/forms/RadioGroup/RadioGroup.test.tsx +764 -0
- package/src/components/forms/RadioGroup/RadioGroup.tsx +75 -0
- package/src/components/forms/RadioGroup/RadioGroup.types.ts +36 -0
- package/src/components/forms/RadioGroup/RadioGroup.variants.ts +58 -0
- package/src/components/forms/RadioGroup/index.ts +15 -0
- package/src/components/forms/Rating/README.md +729 -0
- package/src/components/forms/Rating/Rating.test.tsx +729 -0
- package/src/components/forms/Rating/Rating.tsx +258 -0
- package/src/components/forms/Rating/Rating.types.ts +89 -0
- package/src/components/forms/Rating/Rating.variants.ts +56 -0
- package/src/components/forms/Rating/index.ts +8 -0
- package/src/components/forms/SearchInput/README.md +729 -0
- package/src/components/forms/SearchInput/SearchInput.test.tsx +579 -0
- package/src/components/forms/SearchInput/SearchInput.tsx +103 -0
- package/src/components/forms/SearchInput/SearchInput.types.ts +40 -0
- package/src/components/forms/SearchInput/SearchInput.variants.ts +51 -0
- package/src/components/forms/SearchInput/index.ts +8 -0
- package/src/components/forms/Select/README.md +1286 -0
- package/src/components/forms/Select/Select.test.tsx +1136 -0
- package/src/components/forms/Select/Select.tsx +170 -0
- package/src/components/forms/Select/Select.types.ts +75 -0
- package/src/components/forms/Select/Select.variants.ts +133 -0
- package/src/components/forms/Select/index.ts +3 -0
- package/src/components/forms/Slider/README.md +1246 -0
- package/src/components/forms/Slider/Slider.test.tsx +731 -0
- package/src/components/forms/Slider/Slider.tsx +235 -0
- package/src/components/forms/Slider/Slider.types.ts +90 -0
- package/src/components/forms/Slider/Slider.variants.ts +239 -0
- package/src/components/forms/Slider/index.ts +34 -0
- package/src/components/forms/SuccessMessage/README.md +534 -0
- package/src/components/forms/SuccessMessage/SuccessMessage.test.tsx +257 -0
- package/src/components/forms/SuccessMessage/SuccessMessage.tsx +43 -0
- package/src/components/forms/SuccessMessage/SuccessMessage.types.ts +11 -0
- package/src/components/forms/SuccessMessage/SuccessMessage.variants.ts +15 -0
- package/src/components/forms/SuccessMessage/index.ts +4 -0
- package/src/components/forms/Switch/README.md +785 -0
- package/src/components/forms/Switch/Switch.test.tsx +636 -0
- package/src/components/forms/Switch/Switch.tsx +28 -0
- package/src/components/forms/Switch/Switch.types.ts +24 -0
- package/src/components/forms/Switch/Switch.variants.ts +49 -0
- package/src/components/forms/Switch/index.ts +3 -0
- package/src/components/forms/TagsInput/README.md +216 -0
- package/src/components/forms/TagsInput/TagsInput.test.tsx +308 -0
- package/src/components/forms/TagsInput/TagsInput.tsx +189 -0
- package/src/components/forms/TagsInput/TagsInput.types.ts +85 -0
- package/src/components/forms/TagsInput/TagsInput.variants.ts +58 -0
- package/src/components/forms/TagsInput/index.ts +12 -0
- package/src/components/forms/TextInput/README.md +871 -0
- package/src/components/forms/TextInput/TextInput.test.tsx +484 -0
- package/src/components/forms/TextInput/TextInput.tsx +76 -0
- package/src/components/forms/TextInput/TextInput.types.ts +61 -0
- package/src/components/forms/TextInput/TextInput.variants.ts +117 -0
- package/src/components/forms/TextInput/index.ts +14 -0
- package/src/components/forms/Textarea/README.md +905 -0
- package/src/components/forms/Textarea/Textarea.test.tsx +482 -0
- package/src/components/forms/Textarea/Textarea.tsx +24 -0
- package/src/components/forms/Textarea/Textarea.types.ts +39 -0
- package/src/components/forms/Textarea/Textarea.variants.ts +48 -0
- package/src/components/forms/Textarea/index.ts +3 -0
- package/src/components/forms/TextareaField/README.md +172 -0
- package/src/components/forms/TextareaField/TextareaField.test.tsx +260 -0
- package/src/components/forms/TextareaField/TextareaField.tsx +73 -0
- package/src/components/forms/TextareaField/TextareaField.types.ts +37 -0
- package/src/components/forms/TextareaField/TextareaField.variants.ts +83 -0
- package/src/components/forms/TextareaField/index.ts +12 -0
- package/src/components/forms/TimePicker/README.md +750 -0
- package/src/components/forms/TimePicker/TimePicker.test.tsx +780 -0
- package/src/components/forms/TimePicker/TimePicker.tsx +383 -0
- package/src/components/forms/TimePicker/TimePicker.types.ts +94 -0
- package/src/components/forms/TimePicker/TimePicker.variants.ts +83 -0
- package/src/components/forms/TimePicker/index.ts +14 -0
- package/src/components/forms/ToggleGroup/README.md +870 -0
- package/src/components/forms/ToggleGroup/ToggleGroup.test.tsx +941 -0
- package/src/components/forms/ToggleGroup/ToggleGroup.tsx +5 -0
- package/src/components/forms/ToggleGroup/ToggleGroup.types.ts +17 -0
- package/src/components/forms/ToggleGroup/ToggleGroup.variants.ts +42 -0
- package/src/components/forms/ToggleGroup/index.ts +9 -0
- package/src/components/forms/URLInput/README.md +701 -0
- package/src/components/forms/URLInput/URLInput.test.tsx +602 -0
- package/src/components/forms/URLInput/URLInput.tsx +71 -0
- package/src/components/forms/URLInput/URLInput.types.ts +51 -0
- package/src/components/forms/URLInput/URLInput.variants.ts +52 -0
- package/src/components/forms/URLInput/index.ts +8 -0
- package/src/components/forms/index.ts +44 -0
- package/src/components/index.ts +26 -0
- package/src/components/layout/AppContent/AppContent.test.tsx +34 -0
- package/src/components/layout/AppContent/AppContent.tsx +23 -0
- package/src/components/layout/AppContent/AppContent.variants.ts +23 -0
- package/src/components/layout/AppContent/index.ts +2 -0
- package/src/components/layout/AppHeader/AppHeader.test.tsx +26 -0
- package/src/components/layout/AppHeader/AppHeader.tsx +17 -0
- package/src/components/layout/AppHeader/AppHeader.variants.ts +21 -0
- package/src/components/layout/AppHeader/AppHeaderActions.tsx +13 -0
- package/src/components/layout/AppHeader/AppHeaderNav.tsx +13 -0
- package/src/components/layout/AppHeader/AppHeaderTitle.tsx +13 -0
- package/src/components/layout/AppHeader/index.ts +5 -0
- package/src/components/layout/AppRail/AppRail.test.tsx +50 -0
- package/src/components/layout/AppRail/AppRail.tsx +24 -0
- package/src/components/layout/AppRail/AppRail.variants.ts +31 -0
- package/src/components/layout/AppRail/AppRailFooter.tsx +13 -0
- package/src/components/layout/AppRail/AppRailGroup.tsx +13 -0
- package/src/components/layout/AppRail/AppRailHeader.tsx +13 -0
- package/src/components/layout/AppRail/AppRailItem.tsx +57 -0
- package/src/components/layout/AppRail/index.ts +5 -0
- package/src/components/layout/AppShell/AppShell.context.tsx +34 -0
- package/src/components/layout/AppShell/AppShell.test.tsx +61 -0
- package/src/components/layout/AppShell/AppShell.tsx +91 -0
- package/src/components/layout/AppShell/AppShell.variants.ts +21 -0
- package/src/components/layout/AppShell/index.ts +2 -0
- package/src/components/layout/AppSidebar/AppSidebar.test.tsx +69 -0
- package/src/components/layout/AppSidebar/AppSidebar.tsx +58 -0
- package/src/components/layout/AppSidebar/AppSidebar.variants.ts +41 -0
- package/src/components/layout/AppSidebar/AppSidebarContent.tsx +18 -0
- package/src/components/layout/AppSidebar/AppSidebarFooter.tsx +13 -0
- package/src/components/layout/AppSidebar/AppSidebarGroup.tsx +22 -0
- package/src/components/layout/AppSidebar/AppSidebarHeader.tsx +13 -0
- package/src/components/layout/AppSidebar/AppSidebarItem.tsx +54 -0
- package/src/components/layout/AppSidebar/AppSidebarTrigger.tsx +25 -0
- package/src/components/layout/AppSidebar/index.ts +7 -0
- package/src/components/layout/CLAUDE.md +112 -0
- package/src/components/layout/Card/Card.test.tsx +339 -0
- package/src/components/layout/Card/Card.tsx +102 -0
- package/src/components/layout/Card/Card.types.ts +52 -0
- package/src/components/layout/Card/Card.variants.ts +85 -0
- package/src/components/layout/Card/README.md +994 -0
- package/src/components/layout/Card/index.ts +3 -0
- package/src/components/layout/Collapsible/Collapsible.test.tsx +491 -0
- package/src/components/layout/Collapsible/Collapsible.tsx +50 -0
- package/src/components/layout/Collapsible/Collapsible.types.ts +26 -0
- package/src/components/layout/Collapsible/Collapsible.variants.ts +52 -0
- package/src/components/layout/Collapsible/README.md +885 -0
- package/src/components/layout/Collapsible/index.ts +3 -0
- package/src/components/layout/Container/Container.test.tsx +283 -0
- package/src/components/layout/Container/Container.tsx +25 -0
- package/src/components/layout/Container/Container.types.ts +36 -0
- package/src/components/layout/Container/Container.variants.ts +59 -0
- package/src/components/layout/Container/README.md +700 -0
- package/src/components/layout/Container/index.ts +3 -0
- package/src/components/layout/Flex/Flex.test.tsx +545 -0
- package/src/components/layout/Flex/Flex.tsx +52 -0
- package/src/components/layout/Flex/Flex.types.ts +61 -0
- package/src/components/layout/Flex/Flex.variants.ts +98 -0
- package/src/components/layout/Flex/README.md +812 -0
- package/src/components/layout/Flex/index.ts +3 -0
- package/src/components/layout/Grid/Grid.test.tsx +601 -0
- package/src/components/layout/Grid/Grid.tsx +61 -0
- package/src/components/layout/Grid/Grid.types.ts +47 -0
- package/src/components/layout/Grid/Grid.variants.ts +100 -0
- package/src/components/layout/Grid/README.md +762 -0
- package/src/components/layout/Grid/index.ts +3 -0
- package/src/components/layout/PageContent/PageContent.tsx +13 -0
- package/src/components/layout/PageContent/index.ts +1 -0
- package/src/components/layout/PageHeader/PageHeader.tsx +43 -0
- package/src/components/layout/PageHeader/index.ts +1 -0
- package/src/components/layout/PageSection/PageSection.tsx +28 -0
- package/src/components/layout/PageSection/index.ts +1 -0
- package/src/components/layout/Panel/Panel.test.tsx +427 -0
- package/src/components/layout/Panel/Panel.tsx +75 -0
- package/src/components/layout/Panel/Panel.types.ts +59 -0
- package/src/components/layout/Panel/Panel.variants.ts +94 -0
- package/src/components/layout/Panel/README.md +861 -0
- package/src/components/layout/Panel/index.ts +3 -0
- package/src/components/layout/ResizablePanels/README.md +369 -0
- package/src/components/layout/ResizablePanels/ResizablePanels.test.tsx +506 -0
- package/src/components/layout/ResizablePanels/ResizablePanels.tsx +64 -0
- package/src/components/layout/ResizablePanels/ResizablePanels.types.ts +41 -0
- package/src/components/layout/ResizablePanels/ResizablePanels.variants.ts +67 -0
- package/src/components/layout/ResizablePanels/index.ts +3 -0
- package/src/components/layout/ResponsiveContainer/README.md +267 -0
- package/src/components/layout/ResponsiveContainer/ResponsiveContainer.test.tsx +386 -0
- package/src/components/layout/ResponsiveContainer/ResponsiveContainer.tsx +54 -0
- package/src/components/layout/ResponsiveContainer/ResponsiveContainer.types.ts +43 -0
- package/src/components/layout/ResponsiveContainer/ResponsiveContainer.variants.ts +106 -0
- package/src/components/layout/ResponsiveContainer/index.ts +3 -0
- package/src/components/layout/ScrollArea/README.md +278 -0
- package/src/components/layout/ScrollArea/ScrollArea.test.tsx +352 -0
- package/src/components/layout/ScrollArea/ScrollArea.tsx +61 -0
- package/src/components/layout/ScrollArea/ScrollArea.types.ts +36 -0
- package/src/components/layout/ScrollArea/ScrollArea.variants.ts +85 -0
- package/src/components/layout/ScrollArea/index.ts +3 -0
- package/src/components/layout/Section/README.md +315 -0
- package/src/components/layout/Section/Section.test.tsx +293 -0
- package/src/components/layout/Section/Section.tsx +60 -0
- package/src/components/layout/Section/Section.types.ts +38 -0
- package/src/components/layout/Section/Section.variants.ts +45 -0
- package/src/components/layout/Section/index.ts +3 -0
- package/src/components/layout/Sheet/README.md +533 -0
- package/src/components/layout/Sheet/Sheet.test.tsx +702 -0
- package/src/components/layout/Sheet/Sheet.tsx +142 -0
- package/src/components/layout/Sheet/Sheet.types.ts +92 -0
- package/src/components/layout/Sheet/Sheet.variants.ts +98 -0
- package/src/components/layout/Sheet/index.ts +3 -0
- package/src/components/layout/Stack/README.md +346 -0
- package/src/components/layout/Stack/Stack.test.tsx +492 -0
- package/src/components/layout/Stack/Stack.tsx +58 -0
- package/src/components/layout/Stack/Stack.types.ts +58 -0
- package/src/components/layout/Stack/Stack.variants.ts +77 -0
- package/src/components/layout/Stack/index.ts +3 -0
- package/src/components/layout/index.ts +21 -0
- package/src/components/navigation/Breadcrumb/Breadcrumb.test.tsx +875 -0
- package/src/components/navigation/Breadcrumb/Breadcrumb.tsx +183 -0
- package/src/components/navigation/Breadcrumb/Breadcrumb.types.ts +49 -0
- package/src/components/navigation/Breadcrumb/Breadcrumb.variants.ts +63 -0
- package/src/components/navigation/Breadcrumb/README.md +608 -0
- package/src/components/navigation/Breadcrumb/index.ts +24 -0
- package/src/components/navigation/CLAUDE.md +118 -0
- package/src/components/navigation/ContextMenu/ContextMenu.test.tsx +881 -0
- package/src/components/navigation/ContextMenu/ContextMenu.tsx +304 -0
- package/src/components/navigation/ContextMenu/ContextMenu.types.ts +119 -0
- package/src/components/navigation/ContextMenu/ContextMenu.variants.ts +167 -0
- package/src/components/navigation/ContextMenu/README.md +660 -0
- package/src/components/navigation/ContextMenu/index.ts +34 -0
- package/src/components/navigation/Drawer/Drawer.test.tsx +721 -0
- package/src/components/navigation/Drawer/Drawer.tsx +178 -0
- package/src/components/navigation/Drawer/Drawer.types.ts +66 -0
- package/src/components/navigation/Drawer/Drawer.variants.ts +121 -0
- package/src/components/navigation/Drawer/README.md +723 -0
- package/src/components/navigation/Drawer/index.ts +24 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.test.tsx +881 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.tsx +305 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.types.ts +98 -0
- package/src/components/navigation/DropdownMenu/DropdownMenu.variants.ts +168 -0
- package/src/components/navigation/DropdownMenu/README.md +676 -0
- package/src/components/navigation/DropdownMenu/index.ts +33 -0
- package/src/components/navigation/Header/Header.test.tsx +418 -0
- package/src/components/navigation/Header/Header.tsx +133 -0
- package/src/components/navigation/Header/Header.types.ts +41 -0
- package/src/components/navigation/Header/Header.variants.ts +91 -0
- package/src/components/navigation/Header/README.md +567 -0
- package/src/components/navigation/Header/index.ts +14 -0
- package/src/components/navigation/Menu/Menu.test.tsx +658 -0
- package/src/components/navigation/Menu/Menu.tsx +247 -0
- package/src/components/navigation/Menu/Menu.types.ts +51 -0
- package/src/components/navigation/Menu/Menu.variants.ts +105 -0
- package/src/components/navigation/Menu/README.md +599 -0
- package/src/components/navigation/Menu/index.ts +17 -0
- package/src/components/navigation/Menubar/Menubar.test.tsx +1028 -0
- package/src/components/navigation/Menubar/Menubar.tsx +308 -0
- package/src/components/navigation/Menubar/Menubar.types.ts +104 -0
- package/src/components/navigation/Menubar/Menubar.variants.ts +182 -0
- package/src/components/navigation/Menubar/README.md +641 -0
- package/src/components/navigation/Menubar/index.ts +36 -0
- package/src/components/navigation/MobileSidebar/MobileSidebar.test.tsx +255 -0
- package/src/components/navigation/MobileSidebar/MobileSidebar.tsx +187 -0
- package/src/components/navigation/MobileSidebar/MobileSidebar.types.ts +62 -0
- package/src/components/navigation/MobileSidebar/MobileSidebar.variants.ts +79 -0
- package/src/components/navigation/MobileSidebar/index.ts +17 -0
- package/src/components/navigation/Navbar/Navbar.test.tsx +621 -0
- package/src/components/navigation/Navbar/Navbar.tsx +238 -0
- package/src/components/navigation/Navbar/Navbar.types.ts +69 -0
- package/src/components/navigation/Navbar/Navbar.variants.ts +176 -0
- package/src/components/navigation/Navbar/README.md +670 -0
- package/src/components/navigation/Navbar/index.ts +20 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.test.tsx +701 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.tsx +211 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.types.ts +52 -0
- package/src/components/navigation/NavigationMenu/NavigationMenu.variants.ts +122 -0
- package/src/components/navigation/NavigationMenu/README.md +697 -0
- package/src/components/navigation/NavigationMenu/index.ts +23 -0
- package/src/components/navigation/Pagination/Pagination.test.tsx +619 -0
- package/src/components/navigation/Pagination/Pagination.tsx +185 -0
- package/src/components/navigation/Pagination/Pagination.types.ts +42 -0
- package/src/components/navigation/Pagination/Pagination.variants.ts +36 -0
- package/src/components/navigation/Pagination/README.md +635 -0
- package/src/components/navigation/Pagination/index.ts +17 -0
- package/src/components/navigation/Sidebar/README.md +628 -0
- package/src/components/navigation/Sidebar/Sidebar.test.tsx +1169 -0
- package/src/components/navigation/Sidebar/Sidebar.tsx +761 -0
- package/src/components/navigation/Sidebar/Sidebar.types.ts +168 -0
- package/src/components/navigation/Sidebar/Sidebar.variants.ts +302 -0
- package/src/components/navigation/Sidebar/index.ts +59 -0
- package/src/components/navigation/SkipLinks/README.md +512 -0
- package/src/components/navigation/SkipLinks/SkipLinks.test.tsx +445 -0
- package/src/components/navigation/SkipLinks/SkipLinks.tsx +103 -0
- package/src/components/navigation/SkipLinks/SkipLinks.types.ts +39 -0
- package/src/components/navigation/SkipLinks/SkipLinks.variants.ts +102 -0
- package/src/components/navigation/SkipLinks/index.ts +12 -0
- package/src/components/navigation/TabNavigation/TabNavigation.tsx +196 -0
- package/src/components/navigation/TabNavigation/TabNavigation.types.ts +85 -0
- package/src/components/navigation/TabNavigation/TabNavigation.variants.ts +95 -0
- package/src/components/navigation/TabNavigation/index.ts +10 -0
- package/src/components/navigation/TabPanel/README.md +437 -0
- package/src/components/navigation/TabPanel/TabPanel.test.tsx +566 -0
- package/src/components/navigation/TabPanel/TabPanel.tsx +95 -0
- package/src/components/navigation/TabPanel/TabPanel.types.ts +29 -0
- package/src/components/navigation/TabPanel/TabPanel.variants.ts +61 -0
- package/src/components/navigation/TabPanel/index.ts +13 -0
- package/src/components/navigation/Tabs/README.md +669 -0
- package/src/components/navigation/Tabs/Tabs.test.tsx +500 -0
- package/src/components/navigation/Tabs/Tabs.tsx +112 -0
- package/src/components/navigation/Tabs/Tabs.types.ts +38 -0
- package/src/components/navigation/Tabs/Tabs.variants.ts +80 -0
- package/src/components/navigation/Tabs/index.ts +14 -0
- package/src/components/navigation/Toolbar/README.md +650 -0
- package/src/components/navigation/Toolbar/Toolbar.test.tsx +620 -0
- package/src/components/navigation/Toolbar/Toolbar.tsx +133 -0
- package/src/components/navigation/Toolbar/Toolbar.types.ts +42 -0
- package/src/components/navigation/Toolbar/Toolbar.variants.ts +95 -0
- package/src/components/navigation/Toolbar/index.ts +17 -0
- package/src/components/navigation/UserProfileDropdown/UserProfileDropdown.test.tsx +257 -0
- package/src/components/navigation/UserProfileDropdown/UserProfileDropdown.tsx +267 -0
- package/src/components/navigation/UserProfileDropdown/UserProfileDropdown.types.ts +84 -0
- package/src/components/navigation/UserProfileDropdown/UserProfileDropdown.variants.ts +77 -0
- package/src/components/navigation/UserProfileDropdown/index.ts +17 -0
- package/src/components/navigation/WorkspaceDropdown/WorkspaceDropdown.tsx +190 -0
- package/src/components/navigation/WorkspaceDropdown/WorkspaceDropdown.types.ts +56 -0
- package/src/components/navigation/WorkspaceDropdown/WorkspaceDropdown.variants.ts +82 -0
- package/src/components/navigation/WorkspaceDropdown/index.ts +20 -0
- package/src/components/navigation/index.ts +17 -0
- package/src/components/primitives/ArrowAnimated/ArrowAnimated.test.tsx +240 -0
- package/src/components/primitives/ArrowAnimated/ArrowAnimated.tsx +96 -0
- package/src/components/primitives/ArrowAnimated/ArrowAnimated.types.ts +16 -0
- package/src/components/primitives/ArrowAnimated/ArrowAnimated.variants.ts +43 -0
- package/src/components/primitives/ArrowAnimated/index.ts +9 -0
- package/src/components/primitives/AspectRatio/AspectRatio.test.tsx +306 -0
- package/src/components/primitives/AspectRatio/AspectRatio.tsx +11 -0
- package/src/components/primitives/AspectRatio/AspectRatio.types.ts +57 -0
- package/src/components/primitives/AspectRatio/AspectRatio.variants.ts +110 -0
- package/src/components/primitives/AspectRatio/README.md +914 -0
- package/src/components/primitives/AspectRatio/index.ts +7 -0
- package/src/components/primitives/Avatar/Avatar.test.tsx +1311 -0
- package/src/components/primitives/Avatar/Avatar.tsx +159 -0
- package/src/components/primitives/Avatar/Avatar.types.ts +70 -0
- package/src/components/primitives/Avatar/AvatarAddButton.tsx +59 -0
- package/src/components/primitives/Avatar/AvatarBadge.tsx +84 -0
- package/src/components/primitives/Avatar/AvatarGroup.test.tsx +1377 -0
- package/src/components/primitives/Avatar/AvatarGroup.tsx +101 -0
- package/src/components/primitives/Avatar/AvatarLabelGroup.tsx +160 -0
- package/src/components/primitives/Avatar/AvatarSkeleton.tsx +76 -0
- package/src/components/primitives/Avatar/AvatarStatus.tsx +71 -0
- package/src/components/primitives/Avatar/README.md +1174 -0
- package/src/components/primitives/Avatar/index.ts +8 -0
- package/src/components/primitives/Badge/Badge.test.tsx +1146 -0
- package/src/components/primitives/Badge/Badge.tsx +389 -0
- package/src/components/primitives/Badge/Badge.types.ts +102 -0
- package/src/components/primitives/Badge/README.md +814 -0
- package/src/components/primitives/Badge/index.ts +2 -0
- package/src/components/primitives/Button/Button.test.tsx +626 -0
- package/src/components/primitives/Button/Button.tsx +299 -0
- package/src/components/primitives/Button/Button.types.ts +137 -0
- package/src/components/primitives/Button/README.md +1112 -0
- package/src/components/primitives/Button/index.ts +8 -0
- package/src/components/primitives/ButtonGroup/ButtonGroup.test.tsx +945 -0
- package/src/components/primitives/ButtonGroup/ButtonGroup.tsx +224 -0
- package/src/components/primitives/ButtonGroup/ButtonGroup.types.ts +133 -0
- package/src/components/primitives/ButtonGroup/README.md +945 -0
- package/src/components/primitives/ButtonGroup/index.ts +12 -0
- package/src/components/primitives/CLAUDE.md +108 -0
- package/src/components/primitives/ConfirmButton/ConfirmButton.test.tsx +929 -0
- package/src/components/primitives/ConfirmButton/ConfirmButton.tsx +298 -0
- package/src/components/primitives/ConfirmButton/ConfirmButton.types.ts +116 -0
- package/src/components/primitives/ConfirmButton/README.md +1059 -0
- package/src/components/primitives/ConfirmButton/index.ts +6 -0
- package/src/components/primitives/Divider/Divider.test.tsx +304 -0
- package/src/components/primitives/Divider/Divider.tsx +209 -0
- package/src/components/primitives/Divider/Divider.types.ts +74 -0
- package/src/components/primitives/Divider/README.md +839 -0
- package/src/components/primitives/Divider/index.ts +8 -0
- package/src/components/primitives/Heading/Heading.test.tsx +611 -0
- package/src/components/primitives/Heading/Heading.tsx +120 -0
- package/src/components/primitives/Heading/Heading.types.ts +61 -0
- package/src/components/primitives/Heading/README.md +820 -0
- package/src/components/primitives/Heading/index.ts +2 -0
- package/src/components/primitives/Icon/Icon.test.tsx +466 -0
- package/src/components/primitives/Icon/Icon.tsx +74 -0
- package/src/components/primitives/Icon/Icon.types.ts +61 -0
- package/src/components/primitives/Icon/README.md +759 -0
- package/src/components/primitives/Icon/index.ts +8 -0
- package/src/components/primitives/Image/Image.test.tsx +734 -0
- package/src/components/primitives/Image/Image.tsx +239 -0
- package/src/components/primitives/Image/Image.types.ts +75 -0
- package/src/components/primitives/Image/README.md +863 -0
- package/src/components/primitives/Image/index.ts +8 -0
- package/src/components/primitives/Kbd/Kbd.test.tsx +432 -0
- package/src/components/primitives/Kbd/Kbd.tsx +53 -0
- package/src/components/primitives/Kbd/Kbd.types.ts +55 -0
- package/src/components/primitives/Kbd/Kbd.variants.ts +33 -0
- package/src/components/primitives/Kbd/README.md +659 -0
- package/src/components/primitives/Kbd/index.ts +3 -0
- package/src/components/primitives/Label/Label.test.tsx +311 -0
- package/src/components/primitives/Label/Label.tsx +46 -0
- package/src/components/primitives/Label/Label.types.ts +28 -0
- package/src/components/primitives/Label/README.md +816 -0
- package/src/components/primitives/Label/index.ts +2 -0
- package/src/components/primitives/Link/Link.test.tsx +764 -0
- package/src/components/primitives/Link/Link.tsx +143 -0
- package/src/components/primitives/Link/Link.types.ts +85 -0
- package/src/components/primitives/Link/README.md +954 -0
- package/src/components/primitives/Link/index.ts +8 -0
- package/src/components/primitives/Paragraph/Paragraph.test.tsx +463 -0
- package/src/components/primitives/Paragraph/Paragraph.tsx +119 -0
- package/src/components/primitives/Paragraph/Paragraph.types.ts +81 -0
- package/src/components/primitives/Paragraph/README.md +812 -0
- package/src/components/primitives/Paragraph/index.ts +2 -0
- package/src/components/primitives/ProgressBar/ProgressBar.test.tsx +598 -0
- package/src/components/primitives/ProgressBar/ProgressBar.tsx +268 -0
- package/src/components/primitives/ProgressBar/ProgressBar.types.ts +127 -0
- package/src/components/primitives/ProgressBar/ProgressBar.variants.ts +111 -0
- package/src/components/primitives/ProgressBar/README.md +772 -0
- package/src/components/primitives/ProgressBar/index.ts +3 -0
- package/src/components/primitives/ProgressCircle/ProgressCircle.test.tsx +219 -0
- package/src/components/primitives/ProgressCircle/ProgressCircle.tsx +264 -0
- package/src/components/primitives/ProgressCircle/ProgressCircle.types.ts +152 -0
- package/src/components/primitives/ProgressCircle/ProgressCircle.variants.ts +140 -0
- package/src/components/primitives/ProgressCircle/README.md +763 -0
- package/src/components/primitives/ProgressCircle/index.ts +3 -0
- package/src/components/primitives/SkeletonLoader/README.md +792 -0
- package/src/components/primitives/SkeletonLoader/SkeletonLoader.test.tsx +382 -0
- package/src/components/primitives/SkeletonLoader/SkeletonLoader.tsx +45 -0
- package/src/components/primitives/SkeletonLoader/SkeletonLoader.types.ts +56 -0
- package/src/components/primitives/SkeletonLoader/SkeletonLoader.variants.ts +87 -0
- package/src/components/primitives/SkeletonLoader/index.ts +3 -0
- package/src/components/primitives/Spacer/README.md +804 -0
- package/src/components/primitives/Spacer/Spacer.test.tsx +363 -0
- package/src/components/primitives/Spacer/Spacer.tsx +147 -0
- package/src/components/primitives/Spacer/Spacer.types.ts +72 -0
- package/src/components/primitives/Spacer/index.ts +9 -0
- package/src/components/primitives/Spinner/README.md +767 -0
- package/src/components/primitives/Spinner/Spinner.test.tsx +289 -0
- package/src/components/primitives/Spinner/Spinner.tsx +101 -0
- package/src/components/primitives/Spinner/Spinner.types.ts +62 -0
- package/src/components/primitives/Spinner/Spinner.variants.ts +50 -0
- package/src/components/primitives/Spinner/index.ts +3 -0
- package/src/components/primitives/SplitButton/README.md +1142 -0
- package/src/components/primitives/SplitButton/SplitButton.test.tsx +1020 -0
- package/src/components/primitives/SplitButton/SplitButton.tsx +215 -0
- package/src/components/primitives/SplitButton/SplitButton.types.ts +114 -0
- package/src/components/primitives/SplitButton/index.ts +8 -0
- package/src/components/primitives/StatusIndicator/README.md +730 -0
- package/src/components/primitives/StatusIndicator/StatusIndicator.test.tsx +446 -0
- package/src/components/primitives/StatusIndicator/StatusIndicator.tsx +100 -0
- package/src/components/primitives/StatusIndicator/StatusIndicator.types.ts +39 -0
- package/src/components/primitives/StatusIndicator/index.ts +2 -0
- package/src/components/primitives/Tag/README.md +793 -0
- package/src/components/primitives/Tag/Tag.test.tsx +410 -0
- package/src/components/primitives/Tag/Tag.tsx +302 -0
- package/src/components/primitives/Tag/Tag.types.ts +112 -0
- package/src/components/primitives/Tag/Tag.variants.ts +144 -0
- package/src/components/primitives/Tag/TagGroup.tsx +89 -0
- package/src/components/primitives/Tag/index.ts +15 -0
- package/src/components/primitives/Text/README.md +849 -0
- package/src/components/primitives/Text/Text.test.tsx +371 -0
- package/src/components/primitives/Text/Text.tsx +89 -0
- package/src/components/primitives/Text/Text.types.ts +21 -0
- package/src/components/primitives/Text/index.ts +2 -0
- package/src/components/primitives/Thumbnail/README.md +800 -0
- package/src/components/primitives/Thumbnail/Thumbnail.test.tsx +657 -0
- package/src/components/primitives/Thumbnail/Thumbnail.tsx +141 -0
- package/src/components/primitives/Thumbnail/Thumbnail.types.ts +102 -0
- package/src/components/primitives/Thumbnail/index.ts +9 -0
- package/src/components/primitives/Toggle/README.md +232 -0
- package/src/components/primitives/Toggle/Toggle.test.tsx +630 -0
- package/src/components/primitives/Toggle/Toggle.tsx +174 -0
- package/src/components/primitives/Toggle/Toggle.types.ts +161 -0
- package/src/components/primitives/Toggle/index.ts +2 -0
- package/src/components/primitives/index.ts +26 -0
- package/src/components/workflow/ApprovalFlow/ApprovalFlow.test.tsx +607 -0
- package/src/components/workflow/ApprovalFlow/ApprovalFlow.tsx +522 -0
- package/src/components/workflow/ApprovalFlow/ApprovalFlow.types.ts +233 -0
- package/src/components/workflow/ApprovalFlow/ApprovalFlow.variants.ts +207 -0
- package/src/components/workflow/ApprovalFlow/README.md +450 -0
- package/src/components/workflow/ApprovalFlow/index.ts +38 -0
- package/src/components/workflow/ApprovalStatus/ApprovalStatus.test.tsx +316 -0
- package/src/components/workflow/ApprovalStatus/ApprovalStatus.tsx +342 -0
- package/src/components/workflow/ApprovalStatus/ApprovalStatus.types.ts +68 -0
- package/src/components/workflow/ApprovalStatus/ApprovalStatus.variants.ts +60 -0
- package/src/components/workflow/ApprovalStatus/README.md +359 -0
- package/src/components/workflow/ApprovalStatus/index.ts +7 -0
- package/src/components/workflow/CLAUDE.md +118 -0
- package/src/components/workflow/CommentSystem/CommentSystem.test.tsx +901 -0
- package/src/components/workflow/CommentSystem/CommentSystem.tsx +1014 -0
- package/src/components/workflow/CommentSystem/CommentSystem.types.ts +290 -0
- package/src/components/workflow/CommentSystem/CommentSystem.variants.ts +307 -0
- package/src/components/workflow/CommentSystem/README.md +478 -0
- package/src/components/workflow/CommentSystem/index.ts +37 -0
- package/src/components/workflow/Dashboard/Dashboard.test.tsx +586 -0
- package/src/components/workflow/Dashboard/Dashboard.tsx +447 -0
- package/src/components/workflow/Dashboard/Dashboard.types.ts +232 -0
- package/src/components/workflow/Dashboard/Dashboard.variants.ts +241 -0
- package/src/components/workflow/Dashboard/README.md +445 -0
- package/src/components/workflow/Dashboard/index.ts +39 -0
- package/src/components/workflow/DashboardBuilder/DashboardBuilder.test.tsx +460 -0
- package/src/components/workflow/DashboardBuilder/DashboardBuilder.tsx +944 -0
- package/src/components/workflow/DashboardBuilder/DashboardBuilder.types.ts +389 -0
- package/src/components/workflow/DashboardBuilder/DashboardBuilder.variants.ts +264 -0
- package/src/components/workflow/DashboardBuilder/README.md +489 -0
- package/src/components/workflow/DashboardBuilder/index.ts +61 -0
- package/src/components/workflow/KanbanBoard/KanbanBoard.test.tsx +445 -0
- package/src/components/workflow/KanbanBoard/KanbanBoard.tsx +806 -0
- package/src/components/workflow/KanbanBoard/KanbanBoard.types.ts +183 -0
- package/src/components/workflow/KanbanBoard/KanbanBoard.variants.ts +311 -0
- package/src/components/workflow/KanbanBoard/README.md +495 -0
- package/src/components/workflow/KanbanBoard/index.ts +40 -0
- package/src/components/workflow/ProgressSteps/ProgressSteps.test.tsx +318 -0
- package/src/components/workflow/ProgressSteps/ProgressSteps.tsx +350 -0
- package/src/components/workflow/ProgressSteps/ProgressSteps.types.ts +84 -0
- package/src/components/workflow/ProgressSteps/ProgressSteps.variants.ts +315 -0
- package/src/components/workflow/ProgressSteps/README.md +412 -0
- package/src/components/workflow/ProgressSteps/index.ts +20 -0
- package/src/components/workflow/ReportGenerator/README.md +530 -0
- package/src/components/workflow/ReportGenerator/ReportGenerator.test.tsx +723 -0
- package/src/components/workflow/ReportGenerator/ReportGenerator.tsx +889 -0
- package/src/components/workflow/ReportGenerator/ReportGenerator.types.ts +321 -0
- package/src/components/workflow/ReportGenerator/ReportGenerator.variants.ts +378 -0
- package/src/components/workflow/ReportGenerator/index.ts +44 -0
- package/src/components/workflow/Stepper/README.md +390 -0
- package/src/components/workflow/Stepper/Stepper.test.tsx +376 -0
- package/src/components/workflow/Stepper/Stepper.tsx +301 -0
- package/src/components/workflow/Stepper/Stepper.types.ts +73 -0
- package/src/components/workflow/Stepper/Stepper.variants.ts +194 -0
- package/src/components/workflow/Stepper/index.ts +16 -0
- package/src/components/workflow/TaskCard/README.md +343 -0
- package/src/components/workflow/TaskCard/TaskCard.test.tsx +375 -0
- package/src/components/workflow/TaskCard/TaskCard.tsx +312 -0
- package/src/components/workflow/TaskCard/TaskCard.types.ts +55 -0
- package/src/components/workflow/TaskCard/TaskCard.variants.ts +64 -0
- package/src/components/workflow/TaskCard/index.ts +14 -0
- package/src/components/workflow/TaskList/README.md +317 -0
- package/src/components/workflow/TaskList/TaskList.test.tsx +318 -0
- package/src/components/workflow/TaskList/TaskList.tsx +556 -0
- package/src/components/workflow/TaskList/TaskList.types.ts +78 -0
- package/src/components/workflow/TaskList/TaskList.variants.ts +143 -0
- package/src/components/workflow/TaskList/index.ts +19 -0
- package/src/components/workflow/TaskStatus/README.md +358 -0
- package/src/components/workflow/TaskStatus/TaskStatus.test.tsx +299 -0
- package/src/components/workflow/TaskStatus/TaskStatus.tsx +246 -0
- package/src/components/workflow/TaskStatus/TaskStatus.types.ts +65 -0
- package/src/components/workflow/TaskStatus/TaskStatus.variants.ts +145 -0
- package/src/components/workflow/TaskStatus/index.ts +24 -0
- package/src/components/workflow/Wizard/README.md +440 -0
- package/src/components/workflow/Wizard/Wizard.test.tsx +432 -0
- package/src/components/workflow/Wizard/Wizard.tsx +279 -0
- package/src/components/workflow/Wizard/Wizard.types.ts +61 -0
- package/src/components/workflow/Wizard/Wizard.variants.ts +105 -0
- package/src/components/workflow/Wizard/index.ts +9 -0
- package/src/components/workflow/index.ts +14 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/use-media-query.ts +24 -0
- package/src/hooks/use-mobile.ts +30 -0
- package/src/hooks/use-on-window-resize.ts +27 -0
- package/src/index.ts +22 -0
- package/src/lib/chart-colors.ts +195 -0
- package/src/lib/utils.ts +6 -0
- package/src/test/setup.ts +125 -0
- package/src/test/vitest.d.ts +13 -0
|
@@ -0,0 +1,2351 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion, @typescript-eslint/require-await -- Test file: non-null assertions are safe with known test data */
|
|
2
|
+
import type { AutocompleteOption } from "./Autocomplete.types"
|
|
3
|
+
|
|
4
|
+
// Autocomplete/Autocomplete.test.tsx
|
|
5
|
+
import { act, render, screen, waitFor, within } from "@testing-library/react"
|
|
6
|
+
import userEvent from "@testing-library/user-event"
|
|
7
|
+
import { axe, toHaveNoViolations } from "jest-axe"
|
|
8
|
+
import * as React from "react"
|
|
9
|
+
import { describe, expect, it, vi } from "vitest"
|
|
10
|
+
|
|
11
|
+
import { Autocomplete } from "./Autocomplete"
|
|
12
|
+
|
|
13
|
+
expect.extend(toHaveNoViolations)
|
|
14
|
+
|
|
15
|
+
// Default test options
|
|
16
|
+
const defaultOptions: AutocompleteOption[] = [
|
|
17
|
+
{ value: "apple", label: "Apple" },
|
|
18
|
+
{ value: "banana", label: "Banana" },
|
|
19
|
+
{ value: "orange", label: "Orange" },
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
// ============================================
|
|
23
|
+
// RENDERING TESTS
|
|
24
|
+
// ============================================
|
|
25
|
+
describe("Autocomplete", () => {
|
|
26
|
+
describe("Rendering", () => {
|
|
27
|
+
it("renders correctly", () => {
|
|
28
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
29
|
+
expect(screen.getByRole("combobox")).toBeInTheDocument()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("renders with custom className", () => {
|
|
33
|
+
render(
|
|
34
|
+
<Autocomplete
|
|
35
|
+
aria-label="Test autocomplete"
|
|
36
|
+
className="custom-class"
|
|
37
|
+
options={defaultOptions}
|
|
38
|
+
/>
|
|
39
|
+
)
|
|
40
|
+
expect(screen.getByRole("combobox").closest('[data-slot="autocomplete"]')).toHaveClass(
|
|
41
|
+
"custom-class"
|
|
42
|
+
)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("renders with placeholder", () => {
|
|
46
|
+
render(
|
|
47
|
+
<Autocomplete
|
|
48
|
+
aria-label="Test autocomplete"
|
|
49
|
+
options={defaultOptions}
|
|
50
|
+
placeholder="Type to search..."
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
expect(screen.getByPlaceholderText("Type to search...")).toBeInTheDocument()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it("renders with default value", () => {
|
|
57
|
+
render(
|
|
58
|
+
<Autocomplete
|
|
59
|
+
aria-label="Test autocomplete"
|
|
60
|
+
defaultValue="apple"
|
|
61
|
+
options={defaultOptions}
|
|
62
|
+
/>
|
|
63
|
+
)
|
|
64
|
+
expect(screen.getByRole("combobox")).toHaveValue("Apple")
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it("renders with controlled value", () => {
|
|
68
|
+
render(
|
|
69
|
+
<Autocomplete
|
|
70
|
+
aria-label="Test autocomplete"
|
|
71
|
+
options={defaultOptions}
|
|
72
|
+
value="banana"
|
|
73
|
+
onChange={() => {}}
|
|
74
|
+
/>
|
|
75
|
+
)
|
|
76
|
+
expect(screen.getByRole("combobox")).toHaveValue("Banana")
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it("has data-slot attribute", () => {
|
|
80
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
81
|
+
expect(
|
|
82
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
83
|
+
).toBeInTheDocument()
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
it("passes through data-* attributes", () => {
|
|
87
|
+
render(
|
|
88
|
+
<Autocomplete
|
|
89
|
+
aria-label="Test autocomplete"
|
|
90
|
+
data-custom="value"
|
|
91
|
+
options={defaultOptions}
|
|
92
|
+
/>
|
|
93
|
+
)
|
|
94
|
+
expect(
|
|
95
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
96
|
+
).toHaveAttribute("data-custom", "value")
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// ============================================
|
|
101
|
+
// INPUT TYPING TESTS
|
|
102
|
+
// ============================================
|
|
103
|
+
describe("Input Typing", () => {
|
|
104
|
+
it("updates input value when typing", async () => {
|
|
105
|
+
const user = userEvent.setup()
|
|
106
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
107
|
+
|
|
108
|
+
const input = screen.getByRole("combobox")
|
|
109
|
+
await user.type(input, "app")
|
|
110
|
+
|
|
111
|
+
expect(input).toHaveValue("app")
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("opens dropdown when typing meets minChars", async () => {
|
|
115
|
+
const user = userEvent.setup()
|
|
116
|
+
render(
|
|
117
|
+
<Autocomplete aria-label="Test autocomplete" minChars={2} options={defaultOptions} />
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
const input = screen.getByRole("combobox")
|
|
121
|
+
await user.type(input, "a")
|
|
122
|
+
|
|
123
|
+
// Should not open with just 1 char when minChars is 2
|
|
124
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
125
|
+
|
|
126
|
+
await user.type(input, "p")
|
|
127
|
+
|
|
128
|
+
// Should open after 2 chars
|
|
129
|
+
await waitFor(() => {
|
|
130
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
it("does not open dropdown when below minChars", async () => {
|
|
135
|
+
const user = userEvent.setup()
|
|
136
|
+
render(
|
|
137
|
+
<Autocomplete aria-label="Test autocomplete" minChars={3} options={defaultOptions} />
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
const input = screen.getByRole("combobox")
|
|
141
|
+
await user.type(input, "ap")
|
|
142
|
+
|
|
143
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// ============================================
|
|
148
|
+
// LOADING STATE TESTS
|
|
149
|
+
// ============================================
|
|
150
|
+
describe("Loading State", () => {
|
|
151
|
+
it("shows loading spinner when loading is true", async () => {
|
|
152
|
+
const user = userEvent.setup()
|
|
153
|
+
render(
|
|
154
|
+
<Autocomplete aria-label="Test autocomplete" loading={true} options={[]} />
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const input = screen.getByRole("combobox")
|
|
158
|
+
await user.type(input, "test")
|
|
159
|
+
|
|
160
|
+
await waitFor(() => {
|
|
161
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
// Should show loading message
|
|
165
|
+
expect(screen.getByText("Loading...")).toBeInTheDocument()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it("uses custom loading message", async () => {
|
|
169
|
+
const user = userEvent.setup()
|
|
170
|
+
render(
|
|
171
|
+
<Autocomplete
|
|
172
|
+
aria-label="Test autocomplete"
|
|
173
|
+
loading={true}
|
|
174
|
+
loadingMessage="Fetching results..."
|
|
175
|
+
options={[]}
|
|
176
|
+
/>
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
const input = screen.getByRole("combobox")
|
|
180
|
+
await user.type(input, "test")
|
|
181
|
+
|
|
182
|
+
await waitFor(() => {
|
|
183
|
+
expect(screen.getByText("Fetching results...")).toBeInTheDocument()
|
|
184
|
+
})
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it("has data-loading attribute when loading", () => {
|
|
188
|
+
render(
|
|
189
|
+
<Autocomplete aria-label="Test autocomplete" loading={true} options={defaultOptions} />
|
|
190
|
+
)
|
|
191
|
+
expect(
|
|
192
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
193
|
+
).toHaveAttribute("data-loading")
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
it("shows spinner in input when loading", () => {
|
|
197
|
+
render(
|
|
198
|
+
<Autocomplete aria-label="Test autocomplete" loading={true} options={defaultOptions} />
|
|
199
|
+
)
|
|
200
|
+
expect(screen.getByRole("combobox").closest('[data-slot="autocomplete"]')?.querySelector('[data-slot="autocomplete-spinner"]')).toBeInTheDocument()
|
|
201
|
+
})
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
// ============================================
|
|
205
|
+
// DEBOUNCE TESTS
|
|
206
|
+
// ============================================
|
|
207
|
+
describe("Debounce", () => {
|
|
208
|
+
it("debounces onInputChange calls", () => {
|
|
209
|
+
vi.useFakeTimers()
|
|
210
|
+
const handleInputChange = vi.fn()
|
|
211
|
+
render(
|
|
212
|
+
<Autocomplete
|
|
213
|
+
aria-label="Test autocomplete"
|
|
214
|
+
debounceMs={300}
|
|
215
|
+
options={defaultOptions}
|
|
216
|
+
onInputChange={handleInputChange}
|
|
217
|
+
/>
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
const input = screen.getByRole("combobox")
|
|
221
|
+
|
|
222
|
+
// Type characters using fireEvent for fake timers compatibility
|
|
223
|
+
act(() => {
|
|
224
|
+
input.focus()
|
|
225
|
+
// Simulate typing
|
|
226
|
+
;["a", "ap", "app"].forEach((val) => {
|
|
227
|
+
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set?.call(input, val)
|
|
228
|
+
input.dispatchEvent(new Event("input", { bubbles: true }))
|
|
229
|
+
})
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
// Should not have been called yet (still debouncing)
|
|
233
|
+
expect(handleInputChange).not.toHaveBeenCalled()
|
|
234
|
+
|
|
235
|
+
// Fast-forward past debounce time
|
|
236
|
+
act(() => {
|
|
237
|
+
vi.advanceTimersByTime(300)
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
// Should have been called with final value
|
|
241
|
+
expect(handleInputChange).toHaveBeenCalledWith("app")
|
|
242
|
+
|
|
243
|
+
vi.useRealTimers()
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it("respects custom debounceMs", () => {
|
|
247
|
+
vi.useFakeTimers()
|
|
248
|
+
const handleInputChange = vi.fn()
|
|
249
|
+
render(
|
|
250
|
+
<Autocomplete
|
|
251
|
+
aria-label="Test autocomplete"
|
|
252
|
+
debounceMs={500}
|
|
253
|
+
options={defaultOptions}
|
|
254
|
+
onInputChange={handleInputChange}
|
|
255
|
+
/>
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
const input = screen.getByRole("combobox")
|
|
259
|
+
|
|
260
|
+
act(() => {
|
|
261
|
+
input.focus()
|
|
262
|
+
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set?.call(input, "test")
|
|
263
|
+
input.dispatchEvent(new Event("input", { bubbles: true }))
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
// Not called after 300ms
|
|
267
|
+
act(() => {
|
|
268
|
+
vi.advanceTimersByTime(300)
|
|
269
|
+
})
|
|
270
|
+
expect(handleInputChange).not.toHaveBeenCalled()
|
|
271
|
+
|
|
272
|
+
// Called after 500ms total
|
|
273
|
+
act(() => {
|
|
274
|
+
vi.advanceTimersByTime(200)
|
|
275
|
+
})
|
|
276
|
+
expect(handleInputChange).toHaveBeenCalledWith("test")
|
|
277
|
+
|
|
278
|
+
vi.useRealTimers()
|
|
279
|
+
})
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
// ============================================
|
|
283
|
+
// MIN CHARS TESTS
|
|
284
|
+
// ============================================
|
|
285
|
+
describe("MinChars", () => {
|
|
286
|
+
it("uses default minChars of 1", async () => {
|
|
287
|
+
const user = userEvent.setup()
|
|
288
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
289
|
+
|
|
290
|
+
const input = screen.getByRole("combobox")
|
|
291
|
+
await user.type(input, "a")
|
|
292
|
+
|
|
293
|
+
await waitFor(() => {
|
|
294
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
295
|
+
})
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
it("respects custom minChars", async () => {
|
|
299
|
+
const user = userEvent.setup()
|
|
300
|
+
render(
|
|
301
|
+
<Autocomplete aria-label="Test autocomplete" minChars={3} options={defaultOptions} />
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
const input = screen.getByRole("combobox")
|
|
305
|
+
await user.type(input, "ap")
|
|
306
|
+
|
|
307
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
308
|
+
|
|
309
|
+
await user.type(input, "p")
|
|
310
|
+
|
|
311
|
+
await waitFor(() => {
|
|
312
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
it("hides dropdown when input length drops below minChars", async () => {
|
|
317
|
+
const user = userEvent.setup()
|
|
318
|
+
render(
|
|
319
|
+
<Autocomplete aria-label="Test autocomplete" minChars={2} options={defaultOptions} />
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
const input = screen.getByRole("combobox")
|
|
323
|
+
await user.type(input, "ap")
|
|
324
|
+
|
|
325
|
+
await waitFor(() => {
|
|
326
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
327
|
+
})
|
|
328
|
+
|
|
329
|
+
await user.clear(input)
|
|
330
|
+
await user.type(input, "a")
|
|
331
|
+
|
|
332
|
+
await waitFor(() => {
|
|
333
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
334
|
+
})
|
|
335
|
+
})
|
|
336
|
+
})
|
|
337
|
+
|
|
338
|
+
// ============================================
|
|
339
|
+
// SELECTION TESTS
|
|
340
|
+
// ============================================
|
|
341
|
+
describe("Selection", () => {
|
|
342
|
+
it("selects option when clicked", async () => {
|
|
343
|
+
const user = userEvent.setup()
|
|
344
|
+
const handleChange = vi.fn()
|
|
345
|
+
render(
|
|
346
|
+
<Autocomplete
|
|
347
|
+
aria-label="Test autocomplete"
|
|
348
|
+
options={defaultOptions}
|
|
349
|
+
onChange={handleChange}
|
|
350
|
+
/>
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
const input = screen.getByRole("combobox")
|
|
354
|
+
await user.type(input, "a")
|
|
355
|
+
|
|
356
|
+
await waitFor(() => {
|
|
357
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
await user.click(screen.getByText("Apple"))
|
|
361
|
+
|
|
362
|
+
expect(handleChange).toHaveBeenCalledWith("apple")
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
it("updates input value after selection", async () => {
|
|
366
|
+
const user = userEvent.setup()
|
|
367
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
368
|
+
|
|
369
|
+
const input = screen.getByRole("combobox")
|
|
370
|
+
await user.type(input, "a")
|
|
371
|
+
|
|
372
|
+
await waitFor(() => {
|
|
373
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
await user.click(screen.getByText("Apple"))
|
|
377
|
+
|
|
378
|
+
expect(input).toHaveValue("Apple")
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
it("closes dropdown after selection", async () => {
|
|
382
|
+
const user = userEvent.setup()
|
|
383
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
384
|
+
|
|
385
|
+
const input = screen.getByRole("combobox")
|
|
386
|
+
await user.type(input, "a")
|
|
387
|
+
|
|
388
|
+
await waitFor(() => {
|
|
389
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
await user.click(screen.getByText("Apple"))
|
|
393
|
+
|
|
394
|
+
await waitFor(() => {
|
|
395
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
396
|
+
})
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
it("shows check icon on selected item", async () => {
|
|
400
|
+
const user = userEvent.setup()
|
|
401
|
+
render(
|
|
402
|
+
<Autocomplete
|
|
403
|
+
aria-label="Test autocomplete"
|
|
404
|
+
defaultValue="apple"
|
|
405
|
+
options={defaultOptions}
|
|
406
|
+
/>
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
const input = screen.getByRole("combobox")
|
|
410
|
+
await user.clear(input)
|
|
411
|
+
await user.type(input, "a")
|
|
412
|
+
|
|
413
|
+
await waitFor(() => {
|
|
414
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
const selectedItem = document.querySelector(
|
|
418
|
+
'[data-slot="autocomplete-item"][data-selected="true"]'
|
|
419
|
+
)
|
|
420
|
+
expect(selectedItem).toBeInTheDocument()
|
|
421
|
+
expect(selectedItem?.querySelector("svg")).toHaveClass("opacity-100")
|
|
422
|
+
})
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
// ============================================
|
|
426
|
+
// KEYBOARD NAVIGATION TESTS
|
|
427
|
+
// ============================================
|
|
428
|
+
describe("Keyboard Navigation", () => {
|
|
429
|
+
it("navigates options with arrow keys", async () => {
|
|
430
|
+
const user = userEvent.setup()
|
|
431
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
432
|
+
|
|
433
|
+
const input = screen.getByRole("combobox")
|
|
434
|
+
await user.type(input, "a")
|
|
435
|
+
|
|
436
|
+
await waitFor(() => {
|
|
437
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
438
|
+
})
|
|
439
|
+
|
|
440
|
+
await user.keyboard("{ArrowDown}")
|
|
441
|
+
|
|
442
|
+
await waitFor(() => {
|
|
443
|
+
const highlighted = document.querySelector('[data-highlighted="true"]')
|
|
444
|
+
expect(highlighted).toBeInTheDocument()
|
|
445
|
+
})
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
it("selects option with Enter key", async () => {
|
|
449
|
+
const user = userEvent.setup()
|
|
450
|
+
const handleChange = vi.fn()
|
|
451
|
+
render(
|
|
452
|
+
<Autocomplete
|
|
453
|
+
aria-label="Test autocomplete"
|
|
454
|
+
options={defaultOptions}
|
|
455
|
+
onChange={handleChange}
|
|
456
|
+
/>
|
|
457
|
+
)
|
|
458
|
+
|
|
459
|
+
const input = screen.getByRole("combobox")
|
|
460
|
+
await user.type(input, "a")
|
|
461
|
+
|
|
462
|
+
await waitFor(() => {
|
|
463
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
464
|
+
})
|
|
465
|
+
|
|
466
|
+
await user.keyboard("{ArrowDown}{Enter}")
|
|
467
|
+
|
|
468
|
+
expect(handleChange).toHaveBeenCalled()
|
|
469
|
+
})
|
|
470
|
+
|
|
471
|
+
it("closes dropdown with Escape key", async () => {
|
|
472
|
+
const user = userEvent.setup()
|
|
473
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
474
|
+
|
|
475
|
+
const input = screen.getByRole("combobox")
|
|
476
|
+
await user.type(input, "a")
|
|
477
|
+
|
|
478
|
+
await waitFor(() => {
|
|
479
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
480
|
+
})
|
|
481
|
+
|
|
482
|
+
await user.keyboard("{Escape}")
|
|
483
|
+
|
|
484
|
+
await waitFor(() => {
|
|
485
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
486
|
+
})
|
|
487
|
+
})
|
|
488
|
+
|
|
489
|
+
it("navigates up with ArrowUp key", async () => {
|
|
490
|
+
const user = userEvent.setup()
|
|
491
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
492
|
+
|
|
493
|
+
const input = screen.getByRole("combobox")
|
|
494
|
+
await user.type(input, "a")
|
|
495
|
+
|
|
496
|
+
await waitFor(() => {
|
|
497
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
// Go down twice, then up once
|
|
501
|
+
await user.keyboard("{ArrowDown}{ArrowDown}{ArrowUp}")
|
|
502
|
+
|
|
503
|
+
await waitFor(() => {
|
|
504
|
+
const highlighted = document.querySelector('[data-highlighted="true"]')
|
|
505
|
+
expect(highlighted).toBeInTheDocument()
|
|
506
|
+
})
|
|
507
|
+
})
|
|
508
|
+
})
|
|
509
|
+
|
|
510
|
+
// ============================================
|
|
511
|
+
// STATES TESTS
|
|
512
|
+
// ============================================
|
|
513
|
+
describe("States", () => {
|
|
514
|
+
it("handles disabled state", () => {
|
|
515
|
+
render(
|
|
516
|
+
<Autocomplete disabled aria-label="Test autocomplete" options={defaultOptions} />
|
|
517
|
+
)
|
|
518
|
+
expect(screen.getByRole("combobox")).toBeDisabled()
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
it("applies disabled styling", () => {
|
|
522
|
+
render(
|
|
523
|
+
<Autocomplete disabled aria-label="Test autocomplete" options={defaultOptions} />
|
|
524
|
+
)
|
|
525
|
+
expect(screen.getByRole("combobox")).toHaveClass("disabled:opacity-50")
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
it("has data-disabled attribute when disabled", () => {
|
|
529
|
+
render(
|
|
530
|
+
<Autocomplete disabled aria-label="Test autocomplete" options={defaultOptions} />
|
|
531
|
+
)
|
|
532
|
+
expect(
|
|
533
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
534
|
+
).toHaveAttribute("data-disabled")
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
it("shows empty message when no results", async () => {
|
|
538
|
+
const user = userEvent.setup()
|
|
539
|
+
render(
|
|
540
|
+
<Autocomplete
|
|
541
|
+
aria-label="Test autocomplete"
|
|
542
|
+
emptyMessage="No items found"
|
|
543
|
+
loading={false}
|
|
544
|
+
options={[]}
|
|
545
|
+
/>
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
const input = screen.getByRole("combobox")
|
|
549
|
+
await user.type(input, "xyz")
|
|
550
|
+
|
|
551
|
+
await waitFor(() => {
|
|
552
|
+
expect(screen.getByText("No items found")).toBeInTheDocument()
|
|
553
|
+
})
|
|
554
|
+
})
|
|
555
|
+
})
|
|
556
|
+
|
|
557
|
+
// ============================================
|
|
558
|
+
// SIZE VARIANT TESTS
|
|
559
|
+
// ============================================
|
|
560
|
+
describe("Size Variants", () => {
|
|
561
|
+
it("applies default size (md)", () => {
|
|
562
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
563
|
+
expect(screen.getByRole("combobox")).toHaveClass("h-[var(--autocomplete-input-height-md)]")
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
it("applies sm size", () => {
|
|
567
|
+
render(
|
|
568
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} size="sm" />
|
|
569
|
+
)
|
|
570
|
+
expect(screen.getByRole("combobox")).toHaveClass("h-[var(--autocomplete-input-height-sm)]")
|
|
571
|
+
})
|
|
572
|
+
|
|
573
|
+
it("applies lg size", () => {
|
|
574
|
+
render(
|
|
575
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} size="lg" />
|
|
576
|
+
)
|
|
577
|
+
expect(screen.getByRole("combobox")).toHaveClass("h-[var(--autocomplete-input-height-lg)]")
|
|
578
|
+
})
|
|
579
|
+
|
|
580
|
+
it("has data-size attribute", () => {
|
|
581
|
+
render(
|
|
582
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} size="sm" />
|
|
583
|
+
)
|
|
584
|
+
expect(
|
|
585
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
586
|
+
).toHaveAttribute("data-size", "sm")
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
|
|
590
|
+
// ============================================
|
|
591
|
+
// VISUAL VARIANT TESTS
|
|
592
|
+
// ============================================
|
|
593
|
+
describe("Visual Variants", () => {
|
|
594
|
+
it("applies default variant", () => {
|
|
595
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
596
|
+
expect(screen.getByRole("combobox")).toHaveClass("border-input")
|
|
597
|
+
})
|
|
598
|
+
|
|
599
|
+
it("applies filled variant", () => {
|
|
600
|
+
render(
|
|
601
|
+
<Autocomplete
|
|
602
|
+
aria-label="Test autocomplete"
|
|
603
|
+
options={defaultOptions}
|
|
604
|
+
variant="filled"
|
|
605
|
+
/>
|
|
606
|
+
)
|
|
607
|
+
expect(screen.getByRole("combobox")).toHaveClass("bg-muted")
|
|
608
|
+
})
|
|
609
|
+
|
|
610
|
+
it("applies flushed variant", () => {
|
|
611
|
+
render(
|
|
612
|
+
<Autocomplete
|
|
613
|
+
aria-label="Test autocomplete"
|
|
614
|
+
options={defaultOptions}
|
|
615
|
+
variant="flushed"
|
|
616
|
+
/>
|
|
617
|
+
)
|
|
618
|
+
expect(screen.getByRole("combobox")).toHaveClass("rounded-none")
|
|
619
|
+
})
|
|
620
|
+
|
|
621
|
+
it("has data-variant attribute", () => {
|
|
622
|
+
render(
|
|
623
|
+
<Autocomplete
|
|
624
|
+
aria-label="Test autocomplete"
|
|
625
|
+
options={defaultOptions}
|
|
626
|
+
variant="filled"
|
|
627
|
+
/>
|
|
628
|
+
)
|
|
629
|
+
expect(
|
|
630
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
631
|
+
).toHaveAttribute("data-variant", "filled")
|
|
632
|
+
})
|
|
633
|
+
})
|
|
634
|
+
|
|
635
|
+
// ============================================
|
|
636
|
+
// ACCESSIBILITY TESTS
|
|
637
|
+
// ============================================
|
|
638
|
+
describe("Accessibility", () => {
|
|
639
|
+
it("has no accessibility violations", async () => {
|
|
640
|
+
const { container } = render(
|
|
641
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />
|
|
642
|
+
)
|
|
643
|
+
const results = await axe(container)
|
|
644
|
+
expect(results).toHaveNoViolations()
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
it("has no violations when disabled", async () => {
|
|
648
|
+
const { container } = render(
|
|
649
|
+
<Autocomplete disabled aria-label="Test autocomplete" options={defaultOptions} />
|
|
650
|
+
)
|
|
651
|
+
const results = await axe(container)
|
|
652
|
+
expect(results).toHaveNoViolations()
|
|
653
|
+
})
|
|
654
|
+
|
|
655
|
+
it("has no violations when loading", async () => {
|
|
656
|
+
const { container } = render(
|
|
657
|
+
<Autocomplete loading aria-label="Test autocomplete" options={defaultOptions} />
|
|
658
|
+
)
|
|
659
|
+
const results = await axe(container)
|
|
660
|
+
expect(results).toHaveNoViolations()
|
|
661
|
+
})
|
|
662
|
+
|
|
663
|
+
it("input has combobox role", () => {
|
|
664
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
665
|
+
expect(screen.getByRole("combobox")).toBeInTheDocument()
|
|
666
|
+
})
|
|
667
|
+
|
|
668
|
+
it("has aria-expanded attribute", () => {
|
|
669
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
670
|
+
expect(screen.getByRole("combobox")).toHaveAttribute("aria-expanded", "false")
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
it("aria-expanded updates when opened", async () => {
|
|
674
|
+
const user = userEvent.setup()
|
|
675
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
676
|
+
|
|
677
|
+
const input = screen.getByRole("combobox")
|
|
678
|
+
expect(input).toHaveAttribute("aria-expanded", "false")
|
|
679
|
+
|
|
680
|
+
await user.type(input, "a")
|
|
681
|
+
|
|
682
|
+
await waitFor(() => {
|
|
683
|
+
expect(input).toHaveAttribute("aria-expanded", "true")
|
|
684
|
+
})
|
|
685
|
+
})
|
|
686
|
+
|
|
687
|
+
it("has aria-haspopup attribute", () => {
|
|
688
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
689
|
+
expect(screen.getByRole("combobox")).toHaveAttribute("aria-haspopup", "listbox")
|
|
690
|
+
})
|
|
691
|
+
|
|
692
|
+
it("has aria-autocomplete attribute", () => {
|
|
693
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
694
|
+
expect(screen.getByRole("combobox")).toHaveAttribute("aria-autocomplete", "list")
|
|
695
|
+
})
|
|
696
|
+
|
|
697
|
+
it("options have role option", async () => {
|
|
698
|
+
const user = userEvent.setup()
|
|
699
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
700
|
+
|
|
701
|
+
const input = screen.getByRole("combobox")
|
|
702
|
+
await user.type(input, "a")
|
|
703
|
+
|
|
704
|
+
await waitFor(() => {
|
|
705
|
+
const options = screen.getAllByRole("option")
|
|
706
|
+
expect(options.length).toBe(3)
|
|
707
|
+
})
|
|
708
|
+
})
|
|
709
|
+
})
|
|
710
|
+
|
|
711
|
+
// ============================================
|
|
712
|
+
// DATA ATTRIBUTE TESTS
|
|
713
|
+
// ============================================
|
|
714
|
+
describe("Data Attributes", () => {
|
|
715
|
+
it("has data-slot on wrapper", () => {
|
|
716
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
717
|
+
expect(
|
|
718
|
+
screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
719
|
+
).toBeInTheDocument()
|
|
720
|
+
})
|
|
721
|
+
|
|
722
|
+
it("has data-slot on input", () => {
|
|
723
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
724
|
+
expect(screen.getByRole("combobox")).toHaveAttribute(
|
|
725
|
+
"data-slot",
|
|
726
|
+
"autocomplete-input"
|
|
727
|
+
)
|
|
728
|
+
})
|
|
729
|
+
|
|
730
|
+
it("has data-slot on list when opened", async () => {
|
|
731
|
+
const user = userEvent.setup()
|
|
732
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
733
|
+
|
|
734
|
+
const input = screen.getByRole("combobox")
|
|
735
|
+
await user.type(input, "a")
|
|
736
|
+
|
|
737
|
+
await waitFor(() => {
|
|
738
|
+
expect(document.querySelector('[data-slot="autocomplete-list"]')).toBeInTheDocument()
|
|
739
|
+
})
|
|
740
|
+
})
|
|
741
|
+
|
|
742
|
+
it("has data-slot on items", async () => {
|
|
743
|
+
const user = userEvent.setup()
|
|
744
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
745
|
+
|
|
746
|
+
const input = screen.getByRole("combobox")
|
|
747
|
+
await user.type(input, "a")
|
|
748
|
+
|
|
749
|
+
await waitFor(() => {
|
|
750
|
+
const items = document.querySelectorAll('[data-slot="autocomplete-item"]')
|
|
751
|
+
expect(items.length).toBe(3)
|
|
752
|
+
})
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
it("preserves custom data attributes", () => {
|
|
756
|
+
render(
|
|
757
|
+
<Autocomplete
|
|
758
|
+
aria-label="Test autocomplete"
|
|
759
|
+
data-field="search"
|
|
760
|
+
data-form="settings"
|
|
761
|
+
options={defaultOptions}
|
|
762
|
+
/>
|
|
763
|
+
)
|
|
764
|
+
const wrapper = screen.getByRole("combobox").closest('[data-slot="autocomplete"]')
|
|
765
|
+
expect(wrapper).toHaveAttribute("data-field", "search")
|
|
766
|
+
expect(wrapper).toHaveAttribute("data-form", "settings")
|
|
767
|
+
})
|
|
768
|
+
})
|
|
769
|
+
|
|
770
|
+
// ============================================
|
|
771
|
+
// DISABLED OPTIONS TESTS
|
|
772
|
+
// ============================================
|
|
773
|
+
describe("Disabled Options", () => {
|
|
774
|
+
it("renders disabled option correctly", async () => {
|
|
775
|
+
const user = userEvent.setup()
|
|
776
|
+
const options: AutocompleteOption[] = [
|
|
777
|
+
{ value: "enabled", label: "Enabled" },
|
|
778
|
+
{ value: "disabled", label: "Disabled", disabled: true },
|
|
779
|
+
]
|
|
780
|
+
render(<Autocomplete aria-label="Test autocomplete" options={options} />)
|
|
781
|
+
|
|
782
|
+
const input = screen.getByRole("combobox")
|
|
783
|
+
await user.type(input, "e")
|
|
784
|
+
|
|
785
|
+
await waitFor(() => {
|
|
786
|
+
const disabledItem = document.querySelector(
|
|
787
|
+
'[data-slot="autocomplete-item"][data-disabled="true"]'
|
|
788
|
+
)
|
|
789
|
+
expect(disabledItem).toBeInTheDocument()
|
|
790
|
+
expect(disabledItem).toHaveTextContent("Disabled")
|
|
791
|
+
})
|
|
792
|
+
})
|
|
793
|
+
|
|
794
|
+
it("does not select disabled option when clicked", async () => {
|
|
795
|
+
const user = userEvent.setup()
|
|
796
|
+
const handleChange = vi.fn()
|
|
797
|
+
const options: AutocompleteOption[] = [
|
|
798
|
+
{ value: "enabled", label: "Enabled" },
|
|
799
|
+
{ value: "disabled", label: "Disabled", disabled: true },
|
|
800
|
+
]
|
|
801
|
+
render(
|
|
802
|
+
<Autocomplete
|
|
803
|
+
aria-label="Test autocomplete"
|
|
804
|
+
options={options}
|
|
805
|
+
onChange={handleChange}
|
|
806
|
+
/>
|
|
807
|
+
)
|
|
808
|
+
|
|
809
|
+
const input = screen.getByRole("combobox")
|
|
810
|
+
await user.type(input, "e")
|
|
811
|
+
|
|
812
|
+
await waitFor(() => {
|
|
813
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
await user.click(screen.getByText("Disabled"))
|
|
817
|
+
|
|
818
|
+
expect(handleChange).not.toHaveBeenCalled()
|
|
819
|
+
})
|
|
820
|
+
})
|
|
821
|
+
|
|
822
|
+
// ============================================
|
|
823
|
+
// EDGE CASES TESTS
|
|
824
|
+
// ============================================
|
|
825
|
+
describe("Edge Cases", () => {
|
|
826
|
+
it("handles empty options array", async () => {
|
|
827
|
+
const user = userEvent.setup()
|
|
828
|
+
render(<Autocomplete aria-label="Empty autocomplete" options={[]} />)
|
|
829
|
+
|
|
830
|
+
const input = screen.getByRole("combobox")
|
|
831
|
+
await user.type(input, "test")
|
|
832
|
+
|
|
833
|
+
await waitFor(() => {
|
|
834
|
+
expect(screen.getByText("No results found.")).toBeInTheDocument()
|
|
835
|
+
})
|
|
836
|
+
})
|
|
837
|
+
|
|
838
|
+
it("handles rapid typing correctly", () => {
|
|
839
|
+
vi.useFakeTimers()
|
|
840
|
+
const handleInputChange = vi.fn()
|
|
841
|
+
render(
|
|
842
|
+
<Autocomplete
|
|
843
|
+
aria-label="Test autocomplete"
|
|
844
|
+
debounceMs={300}
|
|
845
|
+
options={defaultOptions}
|
|
846
|
+
onInputChange={handleInputChange}
|
|
847
|
+
/>
|
|
848
|
+
)
|
|
849
|
+
|
|
850
|
+
const input = screen.getByRole("combobox")
|
|
851
|
+
|
|
852
|
+
// Rapid typing using native events
|
|
853
|
+
act(() => {
|
|
854
|
+
input.focus()
|
|
855
|
+
const values = ["a", "ab", "abc", "abcd", "abcde", "abcdef"]
|
|
856
|
+
for (const val of values) {
|
|
857
|
+
Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value")?.set?.call(input, val)
|
|
858
|
+
input.dispatchEvent(new Event("input", { bubbles: true }))
|
|
859
|
+
}
|
|
860
|
+
})
|
|
861
|
+
|
|
862
|
+
// Fast-forward past debounce
|
|
863
|
+
act(() => {
|
|
864
|
+
vi.advanceTimersByTime(300)
|
|
865
|
+
})
|
|
866
|
+
|
|
867
|
+
// Should only be called once with final value
|
|
868
|
+
expect(handleInputChange).toHaveBeenCalledTimes(1)
|
|
869
|
+
expect(handleInputChange).toHaveBeenCalledWith("abcdef")
|
|
870
|
+
|
|
871
|
+
vi.useRealTimers()
|
|
872
|
+
})
|
|
873
|
+
|
|
874
|
+
it("handles special characters in values", async () => {
|
|
875
|
+
const user = userEvent.setup()
|
|
876
|
+
const handleChange = vi.fn()
|
|
877
|
+
const options: AutocompleteOption[] = [
|
|
878
|
+
{ value: "special-chars_123", label: "Special Chars" },
|
|
879
|
+
]
|
|
880
|
+
render(
|
|
881
|
+
<Autocomplete
|
|
882
|
+
aria-label="Test autocomplete"
|
|
883
|
+
options={options}
|
|
884
|
+
onChange={handleChange}
|
|
885
|
+
/>
|
|
886
|
+
)
|
|
887
|
+
|
|
888
|
+
const input = screen.getByRole("combobox")
|
|
889
|
+
await user.type(input, "s")
|
|
890
|
+
|
|
891
|
+
await waitFor(() => {
|
|
892
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
893
|
+
})
|
|
894
|
+
|
|
895
|
+
await user.click(screen.getByText("Special Chars"))
|
|
896
|
+
|
|
897
|
+
expect(handleChange).toHaveBeenCalledWith("special-chars_123")
|
|
898
|
+
})
|
|
899
|
+
|
|
900
|
+
it("handles many options", async () => {
|
|
901
|
+
const user = userEvent.setup()
|
|
902
|
+
const manyOptions: AutocompleteOption[] = Array.from({ length: 100 }, (_, i) => ({
|
|
903
|
+
value: `option-${String(i + 1)}`,
|
|
904
|
+
label: `Option ${String(i + 1)}`,
|
|
905
|
+
}))
|
|
906
|
+
render(<Autocomplete aria-label="Test autocomplete" options={manyOptions} />)
|
|
907
|
+
|
|
908
|
+
const input = screen.getByRole("combobox")
|
|
909
|
+
await user.type(input, "o")
|
|
910
|
+
|
|
911
|
+
await waitFor(() => {
|
|
912
|
+
const items = document.querySelectorAll('[data-slot="autocomplete-item"]')
|
|
913
|
+
expect(items.length).toBe(100)
|
|
914
|
+
})
|
|
915
|
+
})
|
|
916
|
+
|
|
917
|
+
it("closes dropdown when clicking outside", async () => {
|
|
918
|
+
const user = userEvent.setup()
|
|
919
|
+
render(
|
|
920
|
+
<div>
|
|
921
|
+
<button>Outside</button>
|
|
922
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />
|
|
923
|
+
</div>
|
|
924
|
+
)
|
|
925
|
+
|
|
926
|
+
const input = screen.getByRole("combobox")
|
|
927
|
+
await user.type(input, "a")
|
|
928
|
+
|
|
929
|
+
await waitFor(() => {
|
|
930
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
await user.click(screen.getByText("Outside"))
|
|
934
|
+
|
|
935
|
+
await waitFor(() => {
|
|
936
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
937
|
+
})
|
|
938
|
+
})
|
|
939
|
+
})
|
|
940
|
+
|
|
941
|
+
// ============================================
|
|
942
|
+
// TYPE SAFETY TESTS
|
|
943
|
+
// ============================================
|
|
944
|
+
describe("Type Safety", () => {
|
|
945
|
+
it("accepts valid size prop values", () => {
|
|
946
|
+
const sizes = ["sm", "md", "lg"] as const
|
|
947
|
+
sizes.forEach((size) => {
|
|
948
|
+
const { unmount } = render(
|
|
949
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} size={size} />
|
|
950
|
+
)
|
|
951
|
+
expect(screen.getByRole("combobox")).toBeInTheDocument()
|
|
952
|
+
unmount()
|
|
953
|
+
})
|
|
954
|
+
})
|
|
955
|
+
|
|
956
|
+
it("accepts valid variant prop values", () => {
|
|
957
|
+
const variants = ["default", "filled", "flushed"] as const
|
|
958
|
+
variants.forEach((variant) => {
|
|
959
|
+
const { unmount } = render(
|
|
960
|
+
<Autocomplete
|
|
961
|
+
aria-label="Test autocomplete"
|
|
962
|
+
options={defaultOptions}
|
|
963
|
+
variant={variant}
|
|
964
|
+
/>
|
|
965
|
+
)
|
|
966
|
+
expect(screen.getByRole("combobox")).toBeInTheDocument()
|
|
967
|
+
unmount()
|
|
968
|
+
})
|
|
969
|
+
})
|
|
970
|
+
})
|
|
971
|
+
|
|
972
|
+
// ============================================
|
|
973
|
+
// SHOW CLEAR TESTS
|
|
974
|
+
// ============================================
|
|
975
|
+
describe("showClear", () => {
|
|
976
|
+
describe("Rendering", () => {
|
|
977
|
+
it("does not render clear button by default", () => {
|
|
978
|
+
render(
|
|
979
|
+
<Autocomplete
|
|
980
|
+
aria-label="Test autocomplete"
|
|
981
|
+
defaultValue="apple"
|
|
982
|
+
options={defaultOptions}
|
|
983
|
+
/>
|
|
984
|
+
)
|
|
985
|
+
expect(
|
|
986
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
987
|
+
).not.toBeInTheDocument()
|
|
988
|
+
})
|
|
989
|
+
|
|
990
|
+
it("renders clear button when showClear is true and has value", () => {
|
|
991
|
+
render(
|
|
992
|
+
<Autocomplete
|
|
993
|
+
showClear
|
|
994
|
+
aria-label="Test autocomplete"
|
|
995
|
+
defaultValue="apple"
|
|
996
|
+
options={defaultOptions}
|
|
997
|
+
/>
|
|
998
|
+
)
|
|
999
|
+
expect(
|
|
1000
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1001
|
+
).toBeInTheDocument()
|
|
1002
|
+
})
|
|
1003
|
+
|
|
1004
|
+
it("does not render clear button when showClear is true but no value", () => {
|
|
1005
|
+
render(
|
|
1006
|
+
<Autocomplete
|
|
1007
|
+
showClear
|
|
1008
|
+
aria-label="Test autocomplete"
|
|
1009
|
+
options={defaultOptions}
|
|
1010
|
+
/>
|
|
1011
|
+
)
|
|
1012
|
+
expect(
|
|
1013
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1014
|
+
).not.toBeInTheDocument()
|
|
1015
|
+
})
|
|
1016
|
+
|
|
1017
|
+
it("does not render clear button when disabled", () => {
|
|
1018
|
+
render(
|
|
1019
|
+
<Autocomplete
|
|
1020
|
+
disabled
|
|
1021
|
+
showClear
|
|
1022
|
+
aria-label="Test autocomplete"
|
|
1023
|
+
defaultValue="apple"
|
|
1024
|
+
options={defaultOptions}
|
|
1025
|
+
/>
|
|
1026
|
+
)
|
|
1027
|
+
expect(
|
|
1028
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1029
|
+
).not.toBeInTheDocument()
|
|
1030
|
+
})
|
|
1031
|
+
|
|
1032
|
+
it("hides clear button during loading state", () => {
|
|
1033
|
+
render(
|
|
1034
|
+
<Autocomplete
|
|
1035
|
+
loading
|
|
1036
|
+
showClear
|
|
1037
|
+
aria-label="Test autocomplete"
|
|
1038
|
+
defaultValue="apple"
|
|
1039
|
+
options={defaultOptions}
|
|
1040
|
+
/>
|
|
1041
|
+
)
|
|
1042
|
+
expect(
|
|
1043
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1044
|
+
).not.toBeInTheDocument()
|
|
1045
|
+
})
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
describe("Interactions", () => {
|
|
1049
|
+
it("clears value when clear button is clicked", async () => {
|
|
1050
|
+
const user = userEvent.setup()
|
|
1051
|
+
render(
|
|
1052
|
+
<Autocomplete
|
|
1053
|
+
showClear
|
|
1054
|
+
aria-label="Test autocomplete"
|
|
1055
|
+
defaultValue="apple"
|
|
1056
|
+
options={defaultOptions}
|
|
1057
|
+
/>
|
|
1058
|
+
)
|
|
1059
|
+
|
|
1060
|
+
const input = screen.getByRole("combobox")
|
|
1061
|
+
expect(input).toHaveValue("Apple")
|
|
1062
|
+
|
|
1063
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1064
|
+
expect(clearButton).toBeInTheDocument()
|
|
1065
|
+
|
|
1066
|
+
await user.click(clearButton!)
|
|
1067
|
+
|
|
1068
|
+
expect(input).toHaveValue("")
|
|
1069
|
+
})
|
|
1070
|
+
|
|
1071
|
+
it("calls onChange with undefined when cleared", async () => {
|
|
1072
|
+
const user = userEvent.setup()
|
|
1073
|
+
const handleChange = vi.fn()
|
|
1074
|
+
render(
|
|
1075
|
+
<Autocomplete
|
|
1076
|
+
showClear
|
|
1077
|
+
aria-label="Test autocomplete"
|
|
1078
|
+
defaultValue="apple"
|
|
1079
|
+
options={defaultOptions}
|
|
1080
|
+
onChange={handleChange}
|
|
1081
|
+
/>
|
|
1082
|
+
)
|
|
1083
|
+
|
|
1084
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1085
|
+
await user.click(clearButton!)
|
|
1086
|
+
|
|
1087
|
+
expect(handleChange).toHaveBeenCalledWith(undefined)
|
|
1088
|
+
})
|
|
1089
|
+
|
|
1090
|
+
it("calls onClear callback when cleared", async () => {
|
|
1091
|
+
const user = userEvent.setup()
|
|
1092
|
+
const handleClear = vi.fn()
|
|
1093
|
+
render(
|
|
1094
|
+
<Autocomplete
|
|
1095
|
+
showClear
|
|
1096
|
+
aria-label="Test autocomplete"
|
|
1097
|
+
defaultValue="apple"
|
|
1098
|
+
options={defaultOptions}
|
|
1099
|
+
onClear={handleClear}
|
|
1100
|
+
/>
|
|
1101
|
+
)
|
|
1102
|
+
|
|
1103
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1104
|
+
await user.click(clearButton!)
|
|
1105
|
+
|
|
1106
|
+
expect(handleClear).toHaveBeenCalledTimes(1)
|
|
1107
|
+
})
|
|
1108
|
+
|
|
1109
|
+
it("focuses input after clearing", async () => {
|
|
1110
|
+
const user = userEvent.setup()
|
|
1111
|
+
render(
|
|
1112
|
+
<Autocomplete
|
|
1113
|
+
showClear
|
|
1114
|
+
aria-label="Test autocomplete"
|
|
1115
|
+
defaultValue="apple"
|
|
1116
|
+
options={defaultOptions}
|
|
1117
|
+
/>
|
|
1118
|
+
)
|
|
1119
|
+
|
|
1120
|
+
const input = screen.getByRole("combobox")
|
|
1121
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1122
|
+
await user.click(clearButton!)
|
|
1123
|
+
|
|
1124
|
+
expect(document.activeElement).toBe(input)
|
|
1125
|
+
})
|
|
1126
|
+
|
|
1127
|
+
it("hides clear button after clearing", async () => {
|
|
1128
|
+
const user = userEvent.setup()
|
|
1129
|
+
render(
|
|
1130
|
+
<Autocomplete
|
|
1131
|
+
showClear
|
|
1132
|
+
aria-label="Test autocomplete"
|
|
1133
|
+
defaultValue="apple"
|
|
1134
|
+
options={defaultOptions}
|
|
1135
|
+
/>
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1139
|
+
await user.click(clearButton!)
|
|
1140
|
+
|
|
1141
|
+
expect(
|
|
1142
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1143
|
+
).not.toBeInTheDocument()
|
|
1144
|
+
})
|
|
1145
|
+
})
|
|
1146
|
+
|
|
1147
|
+
describe("Accessibility", () => {
|
|
1148
|
+
it("clear button has aria-label", () => {
|
|
1149
|
+
render(
|
|
1150
|
+
<Autocomplete
|
|
1151
|
+
showClear
|
|
1152
|
+
aria-label="Test autocomplete"
|
|
1153
|
+
defaultValue="apple"
|
|
1154
|
+
options={defaultOptions}
|
|
1155
|
+
/>
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1159
|
+
expect(clearButton).toHaveAttribute("aria-label", "Clear")
|
|
1160
|
+
})
|
|
1161
|
+
|
|
1162
|
+
it("clear button is focusable with keyboard", async () => {
|
|
1163
|
+
const user = userEvent.setup()
|
|
1164
|
+
render(
|
|
1165
|
+
<Autocomplete
|
|
1166
|
+
showClear
|
|
1167
|
+
aria-label="Test autocomplete"
|
|
1168
|
+
defaultValue="apple"
|
|
1169
|
+
options={defaultOptions}
|
|
1170
|
+
/>
|
|
1171
|
+
)
|
|
1172
|
+
|
|
1173
|
+
const input = screen.getByRole("combobox")
|
|
1174
|
+
await user.click(input)
|
|
1175
|
+
await user.tab()
|
|
1176
|
+
|
|
1177
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1178
|
+
expect(document.activeElement).toBe(clearButton)
|
|
1179
|
+
})
|
|
1180
|
+
|
|
1181
|
+
it("clear button responds to Enter key", async () => {
|
|
1182
|
+
const user = userEvent.setup()
|
|
1183
|
+
const handleClear = vi.fn()
|
|
1184
|
+
render(
|
|
1185
|
+
<Autocomplete
|
|
1186
|
+
showClear
|
|
1187
|
+
aria-label="Test autocomplete"
|
|
1188
|
+
defaultValue="apple"
|
|
1189
|
+
options={defaultOptions}
|
|
1190
|
+
onClear={handleClear}
|
|
1191
|
+
/>
|
|
1192
|
+
)
|
|
1193
|
+
|
|
1194
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')!
|
|
1195
|
+
;(clearButton as HTMLElement).focus()
|
|
1196
|
+
await user.keyboard("{Enter}")
|
|
1197
|
+
|
|
1198
|
+
expect(handleClear).toHaveBeenCalledTimes(1)
|
|
1199
|
+
})
|
|
1200
|
+
|
|
1201
|
+
it("clear button responds to Space key", async () => {
|
|
1202
|
+
const user = userEvent.setup()
|
|
1203
|
+
const handleClear = vi.fn()
|
|
1204
|
+
render(
|
|
1205
|
+
<Autocomplete
|
|
1206
|
+
showClear
|
|
1207
|
+
aria-label="Test autocomplete"
|
|
1208
|
+
defaultValue="apple"
|
|
1209
|
+
options={defaultOptions}
|
|
1210
|
+
onClear={handleClear}
|
|
1211
|
+
/>
|
|
1212
|
+
)
|
|
1213
|
+
|
|
1214
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')!
|
|
1215
|
+
;(clearButton as HTMLElement).focus()
|
|
1216
|
+
await user.keyboard(" ")
|
|
1217
|
+
|
|
1218
|
+
expect(handleClear).toHaveBeenCalledTimes(1)
|
|
1219
|
+
})
|
|
1220
|
+
|
|
1221
|
+
it("has no accessibility violations with showClear", async () => {
|
|
1222
|
+
const { container } = render(
|
|
1223
|
+
<Autocomplete
|
|
1224
|
+
showClear
|
|
1225
|
+
aria-label="Test autocomplete"
|
|
1226
|
+
defaultValue="apple"
|
|
1227
|
+
options={defaultOptions}
|
|
1228
|
+
/>
|
|
1229
|
+
)
|
|
1230
|
+
const results = await axe(container)
|
|
1231
|
+
expect(results).toHaveNoViolations()
|
|
1232
|
+
})
|
|
1233
|
+
})
|
|
1234
|
+
|
|
1235
|
+
describe("Data Attributes", () => {
|
|
1236
|
+
it("clear button has data-slot attribute", () => {
|
|
1237
|
+
render(
|
|
1238
|
+
<Autocomplete
|
|
1239
|
+
showClear
|
|
1240
|
+
aria-label="Test autocomplete"
|
|
1241
|
+
defaultValue="apple"
|
|
1242
|
+
options={defaultOptions}
|
|
1243
|
+
/>
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
expect(
|
|
1247
|
+
document.querySelector('[data-slot="autocomplete-clear"]')
|
|
1248
|
+
).toBeInTheDocument()
|
|
1249
|
+
})
|
|
1250
|
+
})
|
|
1251
|
+
})
|
|
1252
|
+
|
|
1253
|
+
// ============================================
|
|
1254
|
+
// AUTO HIGHLIGHT TESTS
|
|
1255
|
+
// ============================================
|
|
1256
|
+
describe("autoHighlight", () => {
|
|
1257
|
+
it("does not highlight any option by default when autoHighlight is false", async () => {
|
|
1258
|
+
const user = userEvent.setup()
|
|
1259
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
1260
|
+
|
|
1261
|
+
const input = screen.getByRole("combobox")
|
|
1262
|
+
await user.type(input, "a")
|
|
1263
|
+
|
|
1264
|
+
await waitFor(() => {
|
|
1265
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1266
|
+
})
|
|
1267
|
+
|
|
1268
|
+
// No item should be highlighted initially
|
|
1269
|
+
expect(document.querySelector('[data-highlighted="true"]')).not.toBeInTheDocument()
|
|
1270
|
+
})
|
|
1271
|
+
|
|
1272
|
+
it("highlights first enabled option when dropdown opens and autoHighlight is true", async () => {
|
|
1273
|
+
const user = userEvent.setup()
|
|
1274
|
+
render(
|
|
1275
|
+
<Autocomplete autoHighlight aria-label="Test autocomplete" options={defaultOptions} />
|
|
1276
|
+
)
|
|
1277
|
+
|
|
1278
|
+
const input = screen.getByRole("combobox")
|
|
1279
|
+
await user.type(input, "a")
|
|
1280
|
+
|
|
1281
|
+
await waitFor(() => {
|
|
1282
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1283
|
+
})
|
|
1284
|
+
|
|
1285
|
+
// First option should be highlighted
|
|
1286
|
+
await waitFor(() => {
|
|
1287
|
+
const highlighted = document.querySelector('[data-highlighted="true"]')
|
|
1288
|
+
expect(highlighted).toBeInTheDocument()
|
|
1289
|
+
expect(highlighted).toHaveTextContent("Apple")
|
|
1290
|
+
})
|
|
1291
|
+
})
|
|
1292
|
+
|
|
1293
|
+
it("skips disabled options when auto-highlighting", async () => {
|
|
1294
|
+
const user = userEvent.setup()
|
|
1295
|
+
const options: AutocompleteOption[] = [
|
|
1296
|
+
{ value: "disabled1", label: "Disabled First", disabled: true },
|
|
1297
|
+
{ value: "enabled", label: "Enabled Option" },
|
|
1298
|
+
{ value: "disabled2", label: "Disabled Second", disabled: true },
|
|
1299
|
+
]
|
|
1300
|
+
render(
|
|
1301
|
+
<Autocomplete autoHighlight aria-label="Test autocomplete" options={options} />
|
|
1302
|
+
)
|
|
1303
|
+
|
|
1304
|
+
const input = screen.getByRole("combobox")
|
|
1305
|
+
await user.type(input, "e")
|
|
1306
|
+
|
|
1307
|
+
await waitFor(() => {
|
|
1308
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1309
|
+
})
|
|
1310
|
+
|
|
1311
|
+
// First enabled option should be highlighted
|
|
1312
|
+
await waitFor(() => {
|
|
1313
|
+
const highlighted = document.querySelector('[data-highlighted="true"]')
|
|
1314
|
+
expect(highlighted).toBeInTheDocument()
|
|
1315
|
+
expect(highlighted).toHaveTextContent("Enabled Option")
|
|
1316
|
+
})
|
|
1317
|
+
})
|
|
1318
|
+
|
|
1319
|
+
it("Enter key selects auto-highlighted option", async () => {
|
|
1320
|
+
const user = userEvent.setup()
|
|
1321
|
+
const handleChange = vi.fn()
|
|
1322
|
+
render(
|
|
1323
|
+
<Autocomplete
|
|
1324
|
+
autoHighlight
|
|
1325
|
+
aria-label="Test autocomplete"
|
|
1326
|
+
options={defaultOptions}
|
|
1327
|
+
onChange={handleChange}
|
|
1328
|
+
/>
|
|
1329
|
+
)
|
|
1330
|
+
|
|
1331
|
+
const input = screen.getByRole("combobox")
|
|
1332
|
+
await user.type(input, "a")
|
|
1333
|
+
|
|
1334
|
+
await waitFor(() => {
|
|
1335
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1336
|
+
expect(document.querySelector('[data-highlighted="true"]')).toBeInTheDocument()
|
|
1337
|
+
})
|
|
1338
|
+
|
|
1339
|
+
await user.keyboard("{Enter}")
|
|
1340
|
+
|
|
1341
|
+
expect(handleChange).toHaveBeenCalledWith("apple")
|
|
1342
|
+
})
|
|
1343
|
+
|
|
1344
|
+
it("handles empty options array gracefully", async () => {
|
|
1345
|
+
const user = userEvent.setup()
|
|
1346
|
+
render(
|
|
1347
|
+
<Autocomplete autoHighlight aria-label="Test autocomplete" options={[]} />
|
|
1348
|
+
)
|
|
1349
|
+
|
|
1350
|
+
const input = screen.getByRole("combobox")
|
|
1351
|
+
await user.type(input, "test")
|
|
1352
|
+
|
|
1353
|
+
await waitFor(() => {
|
|
1354
|
+
expect(screen.getByText("No results found.")).toBeInTheDocument()
|
|
1355
|
+
})
|
|
1356
|
+
|
|
1357
|
+
// No crash, no highlighted element
|
|
1358
|
+
expect(document.querySelector('[data-highlighted="true"]')).not.toBeInTheDocument()
|
|
1359
|
+
})
|
|
1360
|
+
|
|
1361
|
+
it("handles all disabled options gracefully", async () => {
|
|
1362
|
+
const user = userEvent.setup()
|
|
1363
|
+
const options: AutocompleteOption[] = [
|
|
1364
|
+
{ value: "disabled1", label: "Disabled First", disabled: true },
|
|
1365
|
+
{ value: "disabled2", label: "Disabled Second", disabled: true },
|
|
1366
|
+
]
|
|
1367
|
+
render(
|
|
1368
|
+
<Autocomplete autoHighlight aria-label="Test autocomplete" options={options} />
|
|
1369
|
+
)
|
|
1370
|
+
|
|
1371
|
+
const input = screen.getByRole("combobox")
|
|
1372
|
+
await user.type(input, "d")
|
|
1373
|
+
|
|
1374
|
+
await waitFor(() => {
|
|
1375
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1376
|
+
})
|
|
1377
|
+
|
|
1378
|
+
// No item should be highlighted since all are disabled
|
|
1379
|
+
expect(document.querySelector('[data-highlighted="true"]')).not.toBeInTheDocument()
|
|
1380
|
+
})
|
|
1381
|
+
|
|
1382
|
+
it("arrow keys move from auto-highlighted option", async () => {
|
|
1383
|
+
const user = userEvent.setup()
|
|
1384
|
+
render(
|
|
1385
|
+
<Autocomplete autoHighlight aria-label="Test autocomplete" options={defaultOptions} />
|
|
1386
|
+
)
|
|
1387
|
+
|
|
1388
|
+
const input = screen.getByRole("combobox")
|
|
1389
|
+
await user.type(input, "a")
|
|
1390
|
+
|
|
1391
|
+
await waitFor(() => {
|
|
1392
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1393
|
+
expect(document.querySelector('[data-highlighted="true"]')).toHaveTextContent("Apple")
|
|
1394
|
+
})
|
|
1395
|
+
|
|
1396
|
+
await user.keyboard("{ArrowDown}")
|
|
1397
|
+
|
|
1398
|
+
await waitFor(() => {
|
|
1399
|
+
const highlighted = document.querySelector('[data-highlighted="true"]')
|
|
1400
|
+
expect(highlighted).toHaveTextContent("Banana")
|
|
1401
|
+
})
|
|
1402
|
+
})
|
|
1403
|
+
})
|
|
1404
|
+
|
|
1405
|
+
// ============================================
|
|
1406
|
+
// COMPLETE ON FOCUS TESTS
|
|
1407
|
+
// ============================================
|
|
1408
|
+
describe("completeOnFocus", () => {
|
|
1409
|
+
it("does not show dropdown on focus by default", async () => {
|
|
1410
|
+
const user = userEvent.setup()
|
|
1411
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
1412
|
+
|
|
1413
|
+
const input = screen.getByRole("combobox")
|
|
1414
|
+
await user.click(input)
|
|
1415
|
+
|
|
1416
|
+
// Dropdown should not open on focus when completeOnFocus is false
|
|
1417
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
1418
|
+
})
|
|
1419
|
+
|
|
1420
|
+
it("shows all suggestions on focus when completeOnFocus is true", async () => {
|
|
1421
|
+
const user = userEvent.setup()
|
|
1422
|
+
render(
|
|
1423
|
+
<Autocomplete completeOnFocus aria-label="Test autocomplete" options={defaultOptions} />
|
|
1424
|
+
)
|
|
1425
|
+
|
|
1426
|
+
const input = screen.getByRole("combobox")
|
|
1427
|
+
await user.click(input)
|
|
1428
|
+
|
|
1429
|
+
await waitFor(() => {
|
|
1430
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1431
|
+
})
|
|
1432
|
+
|
|
1433
|
+
// All options should be visible
|
|
1434
|
+
expect(screen.getAllByRole("option")).toHaveLength(3)
|
|
1435
|
+
})
|
|
1436
|
+
|
|
1437
|
+
it("ignores minChars when completeOnFocus is true and input is focused", async () => {
|
|
1438
|
+
const user = userEvent.setup()
|
|
1439
|
+
render(
|
|
1440
|
+
<Autocomplete
|
|
1441
|
+
completeOnFocus
|
|
1442
|
+
aria-label="Test autocomplete"
|
|
1443
|
+
minChars={3}
|
|
1444
|
+
options={defaultOptions}
|
|
1445
|
+
/>
|
|
1446
|
+
)
|
|
1447
|
+
|
|
1448
|
+
const input = screen.getByRole("combobox")
|
|
1449
|
+
await user.click(input)
|
|
1450
|
+
|
|
1451
|
+
// Dropdown should open even though minChars is 3 and input is empty
|
|
1452
|
+
await waitFor(() => {
|
|
1453
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1454
|
+
})
|
|
1455
|
+
})
|
|
1456
|
+
|
|
1457
|
+
it("triggers onInputChange with empty string on focus", async () => {
|
|
1458
|
+
const user = userEvent.setup()
|
|
1459
|
+
const handleInputChange = vi.fn()
|
|
1460
|
+
render(
|
|
1461
|
+
<Autocomplete
|
|
1462
|
+
completeOnFocus
|
|
1463
|
+
aria-label="Test autocomplete"
|
|
1464
|
+
options={defaultOptions}
|
|
1465
|
+
onInputChange={handleInputChange}
|
|
1466
|
+
/>
|
|
1467
|
+
)
|
|
1468
|
+
|
|
1469
|
+
const input = screen.getByRole("combobox")
|
|
1470
|
+
await user.click(input)
|
|
1471
|
+
|
|
1472
|
+
await waitFor(() => {
|
|
1473
|
+
expect(handleInputChange).toHaveBeenCalledWith("")
|
|
1474
|
+
})
|
|
1475
|
+
})
|
|
1476
|
+
|
|
1477
|
+
it("works with autoHighlight", async () => {
|
|
1478
|
+
const user = userEvent.setup()
|
|
1479
|
+
render(
|
|
1480
|
+
<Autocomplete
|
|
1481
|
+
autoHighlight
|
|
1482
|
+
completeOnFocus
|
|
1483
|
+
aria-label="Test autocomplete"
|
|
1484
|
+
options={defaultOptions}
|
|
1485
|
+
/>
|
|
1486
|
+
)
|
|
1487
|
+
|
|
1488
|
+
const input = screen.getByRole("combobox")
|
|
1489
|
+
await user.click(input)
|
|
1490
|
+
|
|
1491
|
+
await waitFor(() => {
|
|
1492
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1493
|
+
const highlighted = document.querySelector('[data-highlighted="true"]')
|
|
1494
|
+
expect(highlighted).toBeInTheDocument()
|
|
1495
|
+
expect(highlighted).toHaveTextContent("Apple")
|
|
1496
|
+
})
|
|
1497
|
+
})
|
|
1498
|
+
|
|
1499
|
+
it("closes dropdown when clicking outside after completeOnFocus opens it", async () => {
|
|
1500
|
+
const user = userEvent.setup()
|
|
1501
|
+
render(
|
|
1502
|
+
<div>
|
|
1503
|
+
<button>Outside</button>
|
|
1504
|
+
<Autocomplete completeOnFocus aria-label="Test autocomplete" options={defaultOptions} />
|
|
1505
|
+
</div>
|
|
1506
|
+
)
|
|
1507
|
+
|
|
1508
|
+
const input = screen.getByRole("combobox")
|
|
1509
|
+
await user.click(input)
|
|
1510
|
+
|
|
1511
|
+
await waitFor(() => {
|
|
1512
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1513
|
+
})
|
|
1514
|
+
|
|
1515
|
+
await user.click(screen.getByText("Outside"))
|
|
1516
|
+
|
|
1517
|
+
await waitFor(() => {
|
|
1518
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
1519
|
+
})
|
|
1520
|
+
})
|
|
1521
|
+
|
|
1522
|
+
it("allows selection after completeOnFocus opens dropdown", async () => {
|
|
1523
|
+
const user = userEvent.setup()
|
|
1524
|
+
const handleChange = vi.fn()
|
|
1525
|
+
render(
|
|
1526
|
+
<Autocomplete
|
|
1527
|
+
completeOnFocus
|
|
1528
|
+
aria-label="Test autocomplete"
|
|
1529
|
+
options={defaultOptions}
|
|
1530
|
+
onChange={handleChange}
|
|
1531
|
+
/>
|
|
1532
|
+
)
|
|
1533
|
+
|
|
1534
|
+
const input = screen.getByRole("combobox")
|
|
1535
|
+
await user.click(input)
|
|
1536
|
+
|
|
1537
|
+
await waitFor(() => {
|
|
1538
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1539
|
+
})
|
|
1540
|
+
|
|
1541
|
+
await user.click(screen.getByText("Banana"))
|
|
1542
|
+
|
|
1543
|
+
expect(handleChange).toHaveBeenCalledWith("banana")
|
|
1544
|
+
expect(input).toHaveValue("Banana")
|
|
1545
|
+
})
|
|
1546
|
+
})
|
|
1547
|
+
|
|
1548
|
+
describe("forceSelection", () => {
|
|
1549
|
+
it("allows any input when forceSelection is false (default)", async () => {
|
|
1550
|
+
const user = userEvent.setup()
|
|
1551
|
+
render(
|
|
1552
|
+
<div>
|
|
1553
|
+
<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />
|
|
1554
|
+
<button type="button">Outside</button>
|
|
1555
|
+
</div>
|
|
1556
|
+
)
|
|
1557
|
+
|
|
1558
|
+
const input = screen.getByRole("combobox")
|
|
1559
|
+
await user.type(input, "xyz")
|
|
1560
|
+
await user.click(screen.getByText("Outside"))
|
|
1561
|
+
|
|
1562
|
+
// Input should keep custom value
|
|
1563
|
+
expect(input).toHaveValue("xyz")
|
|
1564
|
+
})
|
|
1565
|
+
|
|
1566
|
+
it("reverts to empty on blur when no match and no prior selection", async () => {
|
|
1567
|
+
const user = userEvent.setup()
|
|
1568
|
+
const handleChange = vi.fn()
|
|
1569
|
+
render(
|
|
1570
|
+
<div>
|
|
1571
|
+
<Autocomplete
|
|
1572
|
+
forceSelection
|
|
1573
|
+
aria-label="Test autocomplete"
|
|
1574
|
+
options={defaultOptions}
|
|
1575
|
+
onChange={handleChange}
|
|
1576
|
+
/>
|
|
1577
|
+
<button type="button">Outside</button>
|
|
1578
|
+
</div>
|
|
1579
|
+
)
|
|
1580
|
+
|
|
1581
|
+
const input = screen.getByRole("combobox")
|
|
1582
|
+
await user.type(input, "xyz")
|
|
1583
|
+
await user.click(screen.getByText("Outside"))
|
|
1584
|
+
|
|
1585
|
+
// Input should be cleared
|
|
1586
|
+
expect(input).toHaveValue("")
|
|
1587
|
+
// onChange should be called with undefined to clear value
|
|
1588
|
+
expect(handleChange).toHaveBeenCalledWith(undefined)
|
|
1589
|
+
})
|
|
1590
|
+
|
|
1591
|
+
it("reverts to last valid selection on blur when no match", async () => {
|
|
1592
|
+
const user = userEvent.setup()
|
|
1593
|
+
const handleChange = vi.fn()
|
|
1594
|
+
render(
|
|
1595
|
+
<div>
|
|
1596
|
+
<Autocomplete
|
|
1597
|
+
forceSelection
|
|
1598
|
+
aria-label="Test autocomplete"
|
|
1599
|
+
defaultValue="apple"
|
|
1600
|
+
options={defaultOptions}
|
|
1601
|
+
onChange={handleChange}
|
|
1602
|
+
/>
|
|
1603
|
+
<button type="button">Outside</button>
|
|
1604
|
+
</div>
|
|
1605
|
+
)
|
|
1606
|
+
|
|
1607
|
+
const input = screen.getByRole("combobox")
|
|
1608
|
+
expect(input).toHaveValue("Apple")
|
|
1609
|
+
|
|
1610
|
+
// Clear and type something that doesn't match
|
|
1611
|
+
await user.clear(input)
|
|
1612
|
+
await user.type(input, "xyz")
|
|
1613
|
+
await user.click(screen.getByText("Outside"))
|
|
1614
|
+
|
|
1615
|
+
// Should revert to last valid selection
|
|
1616
|
+
expect(input).toHaveValue("Apple")
|
|
1617
|
+
// onChange should NOT be called since we reverted to same value
|
|
1618
|
+
})
|
|
1619
|
+
|
|
1620
|
+
it("keeps value when input text exactly matches an option label", async () => {
|
|
1621
|
+
const user = userEvent.setup()
|
|
1622
|
+
const handleChange = vi.fn()
|
|
1623
|
+
render(
|
|
1624
|
+
<div>
|
|
1625
|
+
<Autocomplete
|
|
1626
|
+
forceSelection
|
|
1627
|
+
aria-label="Test autocomplete"
|
|
1628
|
+
options={defaultOptions}
|
|
1629
|
+
onChange={handleChange}
|
|
1630
|
+
/>
|
|
1631
|
+
<button type="button">Outside</button>
|
|
1632
|
+
</div>
|
|
1633
|
+
)
|
|
1634
|
+
|
|
1635
|
+
const input = screen.getByRole("combobox")
|
|
1636
|
+
// Type exactly matching label (case-insensitive)
|
|
1637
|
+
await user.type(input, "Apple")
|
|
1638
|
+
await user.click(screen.getByText("Outside"))
|
|
1639
|
+
|
|
1640
|
+
// Value should be kept and selection made
|
|
1641
|
+
expect(input).toHaveValue("Apple")
|
|
1642
|
+
expect(handleChange).toHaveBeenCalledWith("apple")
|
|
1643
|
+
})
|
|
1644
|
+
|
|
1645
|
+
it("handles case-insensitive matching", async () => {
|
|
1646
|
+
const user = userEvent.setup()
|
|
1647
|
+
const handleChange = vi.fn()
|
|
1648
|
+
render(
|
|
1649
|
+
<div>
|
|
1650
|
+
<Autocomplete
|
|
1651
|
+
forceSelection
|
|
1652
|
+
aria-label="Test autocomplete"
|
|
1653
|
+
options={defaultOptions}
|
|
1654
|
+
onChange={handleChange}
|
|
1655
|
+
/>
|
|
1656
|
+
<button type="button">Outside</button>
|
|
1657
|
+
</div>
|
|
1658
|
+
)
|
|
1659
|
+
|
|
1660
|
+
const input = screen.getByRole("combobox")
|
|
1661
|
+
await user.type(input, "BANANA")
|
|
1662
|
+
await user.click(screen.getByText("Outside"))
|
|
1663
|
+
|
|
1664
|
+
// Should match "Banana" case-insensitively
|
|
1665
|
+
expect(input).toHaveValue("Banana")
|
|
1666
|
+
expect(handleChange).toHaveBeenCalledWith("banana")
|
|
1667
|
+
})
|
|
1668
|
+
|
|
1669
|
+
it("sets aria-invalid when input doesn't match any option", async () => {
|
|
1670
|
+
const user = userEvent.setup()
|
|
1671
|
+
render(
|
|
1672
|
+
<Autocomplete forceSelection aria-label="Test autocomplete" options={defaultOptions} />
|
|
1673
|
+
)
|
|
1674
|
+
|
|
1675
|
+
const input = screen.getByRole("combobox")
|
|
1676
|
+
await user.type(input, "xyz")
|
|
1677
|
+
|
|
1678
|
+
// Should have aria-invalid while typing non-matching text
|
|
1679
|
+
expect(input).toHaveAttribute("aria-invalid", "true")
|
|
1680
|
+
})
|
|
1681
|
+
|
|
1682
|
+
it("removes aria-invalid when input matches an option", async () => {
|
|
1683
|
+
const user = userEvent.setup()
|
|
1684
|
+
render(
|
|
1685
|
+
<Autocomplete forceSelection aria-label="Test autocomplete" options={defaultOptions} />
|
|
1686
|
+
)
|
|
1687
|
+
|
|
1688
|
+
const input = screen.getByRole("combobox")
|
|
1689
|
+
await user.type(input, "Apple")
|
|
1690
|
+
|
|
1691
|
+
// Should not have aria-invalid when text matches
|
|
1692
|
+
expect(input).not.toHaveAttribute("aria-invalid", "true")
|
|
1693
|
+
})
|
|
1694
|
+
|
|
1695
|
+
it("removes aria-invalid when input is empty", async () => {
|
|
1696
|
+
const user = userEvent.setup()
|
|
1697
|
+
render(
|
|
1698
|
+
<Autocomplete forceSelection aria-label="Test autocomplete" options={defaultOptions} />
|
|
1699
|
+
)
|
|
1700
|
+
|
|
1701
|
+
const input = screen.getByRole("combobox")
|
|
1702
|
+
await user.type(input, "xyz")
|
|
1703
|
+
expect(input).toHaveAttribute("aria-invalid", "true")
|
|
1704
|
+
|
|
1705
|
+
await user.clear(input)
|
|
1706
|
+
expect(input).not.toHaveAttribute("aria-invalid", "true")
|
|
1707
|
+
})
|
|
1708
|
+
|
|
1709
|
+
it("validates on Tab key (blur)", async () => {
|
|
1710
|
+
const user = userEvent.setup()
|
|
1711
|
+
const handleChange = vi.fn()
|
|
1712
|
+
render(
|
|
1713
|
+
<div>
|
|
1714
|
+
<Autocomplete
|
|
1715
|
+
forceSelection
|
|
1716
|
+
aria-label="Test autocomplete"
|
|
1717
|
+
defaultValue="apple"
|
|
1718
|
+
options={defaultOptions}
|
|
1719
|
+
onChange={handleChange}
|
|
1720
|
+
/>
|
|
1721
|
+
<button type="button">Next Field</button>
|
|
1722
|
+
</div>
|
|
1723
|
+
)
|
|
1724
|
+
|
|
1725
|
+
const input = screen.getByRole("combobox")
|
|
1726
|
+
await user.clear(input)
|
|
1727
|
+
await user.type(input, "xyz")
|
|
1728
|
+
await user.tab()
|
|
1729
|
+
|
|
1730
|
+
// Should revert to last valid selection
|
|
1731
|
+
expect(input).toHaveValue("Apple")
|
|
1732
|
+
})
|
|
1733
|
+
|
|
1734
|
+
it("accepts partial match when user selects from dropdown", async () => {
|
|
1735
|
+
const user = userEvent.setup()
|
|
1736
|
+
const handleChange = vi.fn()
|
|
1737
|
+
render(
|
|
1738
|
+
<Autocomplete
|
|
1739
|
+
forceSelection
|
|
1740
|
+
aria-label="Test autocomplete"
|
|
1741
|
+
options={defaultOptions}
|
|
1742
|
+
onChange={handleChange}
|
|
1743
|
+
/>
|
|
1744
|
+
)
|
|
1745
|
+
|
|
1746
|
+
const input = screen.getByRole("combobox")
|
|
1747
|
+
await user.type(input, "app")
|
|
1748
|
+
|
|
1749
|
+
await waitFor(() => {
|
|
1750
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1751
|
+
})
|
|
1752
|
+
|
|
1753
|
+
await user.click(screen.getByText("Apple"))
|
|
1754
|
+
|
|
1755
|
+
expect(input).toHaveValue("Apple")
|
|
1756
|
+
expect(handleChange).toHaveBeenCalledWith("apple")
|
|
1757
|
+
})
|
|
1758
|
+
|
|
1759
|
+
it("works with controlled value", async () => {
|
|
1760
|
+
const user = userEvent.setup()
|
|
1761
|
+
const handleChange = vi.fn()
|
|
1762
|
+
|
|
1763
|
+
function ControlledAutocomplete() {
|
|
1764
|
+
const [value, setValue] = React.useState<string | undefined>("apple")
|
|
1765
|
+
return (
|
|
1766
|
+
<div>
|
|
1767
|
+
<Autocomplete
|
|
1768
|
+
forceSelection
|
|
1769
|
+
aria-label="Test autocomplete"
|
|
1770
|
+
options={defaultOptions}
|
|
1771
|
+
value={value}
|
|
1772
|
+
onChange={(newValue) => {
|
|
1773
|
+
setValue(newValue as string | undefined)
|
|
1774
|
+
handleChange(newValue)
|
|
1775
|
+
}}
|
|
1776
|
+
/>
|
|
1777
|
+
<button type="button">Outside</button>
|
|
1778
|
+
</div>
|
|
1779
|
+
)
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
render(<ControlledAutocomplete />)
|
|
1783
|
+
|
|
1784
|
+
const input = screen.getByRole("combobox")
|
|
1785
|
+
expect(input).toHaveValue("Apple")
|
|
1786
|
+
|
|
1787
|
+
await user.clear(input)
|
|
1788
|
+
await user.type(input, "xyz")
|
|
1789
|
+
await user.click(screen.getByText("Outside"))
|
|
1790
|
+
|
|
1791
|
+
// Should revert to controlled value
|
|
1792
|
+
expect(input).toHaveValue("Apple")
|
|
1793
|
+
})
|
|
1794
|
+
})
|
|
1795
|
+
|
|
1796
|
+
describe("multiple", () => {
|
|
1797
|
+
// Rendering
|
|
1798
|
+
it("does not render tags when multiple is false (default)", async () => {
|
|
1799
|
+
render(<Autocomplete aria-label="Test autocomplete" defaultValue="apple" options={defaultOptions} />)
|
|
1800
|
+
|
|
1801
|
+
// Should not have tags
|
|
1802
|
+
expect(screen.queryByRole("list")).not.toBeInTheDocument()
|
|
1803
|
+
expect(screen.queryByTestId("autocomplete-tags")).not.toBeInTheDocument()
|
|
1804
|
+
})
|
|
1805
|
+
|
|
1806
|
+
it("renders selected values as tags when multiple is true", async () => {
|
|
1807
|
+
render(
|
|
1808
|
+
<Autocomplete
|
|
1809
|
+
multiple
|
|
1810
|
+
aria-label="Test autocomplete"
|
|
1811
|
+
defaultValue={["apple", "banana"]}
|
|
1812
|
+
options={defaultOptions}
|
|
1813
|
+
/>
|
|
1814
|
+
)
|
|
1815
|
+
|
|
1816
|
+
// Should have tags with data-slot
|
|
1817
|
+
const tags = document.querySelectorAll('[data-slot="autocomplete-tag"]')
|
|
1818
|
+
expect(tags).toHaveLength(2)
|
|
1819
|
+
expect(tags[0]).toHaveTextContent("Apple")
|
|
1820
|
+
expect(tags[1]).toHaveTextContent("Banana")
|
|
1821
|
+
})
|
|
1822
|
+
|
|
1823
|
+
it("renders tag remove buttons", async () => {
|
|
1824
|
+
render(
|
|
1825
|
+
<Autocomplete
|
|
1826
|
+
multiple
|
|
1827
|
+
aria-label="Test autocomplete"
|
|
1828
|
+
defaultValue={["apple"]}
|
|
1829
|
+
options={defaultOptions}
|
|
1830
|
+
/>
|
|
1831
|
+
)
|
|
1832
|
+
|
|
1833
|
+
const removeButton = document.querySelector('[data-slot="autocomplete-tag-remove"]')
|
|
1834
|
+
expect(removeButton).toBeInTheDocument()
|
|
1835
|
+
expect(removeButton).toHaveAttribute("aria-label", "Remove Apple")
|
|
1836
|
+
})
|
|
1837
|
+
|
|
1838
|
+
// Selection
|
|
1839
|
+
it("allows selecting multiple values", async () => {
|
|
1840
|
+
const user = userEvent.setup()
|
|
1841
|
+
const handleChange = vi.fn()
|
|
1842
|
+
render(
|
|
1843
|
+
<Autocomplete
|
|
1844
|
+
completeOnFocus
|
|
1845
|
+
multiple
|
|
1846
|
+
aria-label="Test autocomplete"
|
|
1847
|
+
options={defaultOptions}
|
|
1848
|
+
onChange={handleChange}
|
|
1849
|
+
/>
|
|
1850
|
+
)
|
|
1851
|
+
|
|
1852
|
+
const input = screen.getByRole("combobox")
|
|
1853
|
+
await user.click(input)
|
|
1854
|
+
|
|
1855
|
+
await waitFor(() => {
|
|
1856
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1857
|
+
})
|
|
1858
|
+
|
|
1859
|
+
// Select first item
|
|
1860
|
+
await user.click(screen.getByText("Apple"))
|
|
1861
|
+
expect(handleChange).toHaveBeenLastCalledWith(["apple"])
|
|
1862
|
+
|
|
1863
|
+
// Select second item
|
|
1864
|
+
await user.click(screen.getByText("Banana"))
|
|
1865
|
+
expect(handleChange).toHaveBeenLastCalledWith(["apple", "banana"])
|
|
1866
|
+
})
|
|
1867
|
+
|
|
1868
|
+
it("does not close dropdown after selection in multiple mode", async () => {
|
|
1869
|
+
const user = userEvent.setup()
|
|
1870
|
+
render(
|
|
1871
|
+
<Autocomplete
|
|
1872
|
+
completeOnFocus
|
|
1873
|
+
multiple
|
|
1874
|
+
aria-label="Test autocomplete"
|
|
1875
|
+
options={defaultOptions}
|
|
1876
|
+
/>
|
|
1877
|
+
)
|
|
1878
|
+
|
|
1879
|
+
const input = screen.getByRole("combobox")
|
|
1880
|
+
await user.click(input)
|
|
1881
|
+
|
|
1882
|
+
await waitFor(() => {
|
|
1883
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1884
|
+
})
|
|
1885
|
+
|
|
1886
|
+
await user.click(screen.getByText("Apple"))
|
|
1887
|
+
|
|
1888
|
+
// Dropdown should still be open
|
|
1889
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1890
|
+
})
|
|
1891
|
+
|
|
1892
|
+
it("toggles selection on click", async () => {
|
|
1893
|
+
const user = userEvent.setup()
|
|
1894
|
+
const handleChange = vi.fn()
|
|
1895
|
+
render(
|
|
1896
|
+
<Autocomplete
|
|
1897
|
+
completeOnFocus
|
|
1898
|
+
multiple
|
|
1899
|
+
aria-label="Test autocomplete"
|
|
1900
|
+
defaultValue={["apple"]}
|
|
1901
|
+
options={defaultOptions}
|
|
1902
|
+
onChange={handleChange}
|
|
1903
|
+
/>
|
|
1904
|
+
)
|
|
1905
|
+
|
|
1906
|
+
const input = screen.getByRole("combobox")
|
|
1907
|
+
await user.click(input)
|
|
1908
|
+
|
|
1909
|
+
await waitFor(() => {
|
|
1910
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1911
|
+
})
|
|
1912
|
+
|
|
1913
|
+
// Click on already selected item to deselect (use within to target dropdown option, not tag)
|
|
1914
|
+
const listbox = screen.getByRole("listbox")
|
|
1915
|
+
await user.click(within(listbox).getByText("Apple"))
|
|
1916
|
+
expect(handleChange).toHaveBeenLastCalledWith([])
|
|
1917
|
+
})
|
|
1918
|
+
|
|
1919
|
+
it("calls onChange with array of values", async () => {
|
|
1920
|
+
const user = userEvent.setup()
|
|
1921
|
+
const handleChange = vi.fn()
|
|
1922
|
+
render(
|
|
1923
|
+
<Autocomplete
|
|
1924
|
+
completeOnFocus
|
|
1925
|
+
multiple
|
|
1926
|
+
aria-label="Test autocomplete"
|
|
1927
|
+
options={defaultOptions}
|
|
1928
|
+
onChange={handleChange}
|
|
1929
|
+
/>
|
|
1930
|
+
)
|
|
1931
|
+
|
|
1932
|
+
const input = screen.getByRole("combobox")
|
|
1933
|
+
await user.click(input)
|
|
1934
|
+
|
|
1935
|
+
await waitFor(() => {
|
|
1936
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
1937
|
+
})
|
|
1938
|
+
|
|
1939
|
+
await user.click(screen.getByText("Apple"))
|
|
1940
|
+
|
|
1941
|
+
// onChange should be called with an array
|
|
1942
|
+
expect(handleChange).toHaveBeenCalledWith(["apple"])
|
|
1943
|
+
expect(Array.isArray(handleChange.mock.calls[0]?.[0])).toBe(true)
|
|
1944
|
+
})
|
|
1945
|
+
|
|
1946
|
+
// Tag Removal
|
|
1947
|
+
it("removes tag when remove button is clicked", async () => {
|
|
1948
|
+
const user = userEvent.setup()
|
|
1949
|
+
const handleChange = vi.fn()
|
|
1950
|
+
render(
|
|
1951
|
+
<Autocomplete
|
|
1952
|
+
multiple
|
|
1953
|
+
aria-label="Test autocomplete"
|
|
1954
|
+
defaultValue={["apple", "banana"]}
|
|
1955
|
+
options={defaultOptions}
|
|
1956
|
+
onChange={handleChange}
|
|
1957
|
+
/>
|
|
1958
|
+
)
|
|
1959
|
+
|
|
1960
|
+
// Click remove button on first tag
|
|
1961
|
+
const removeButton = document.querySelector('[data-slot="autocomplete-tag-remove"]')!
|
|
1962
|
+
await user.click(removeButton as HTMLElement)
|
|
1963
|
+
|
|
1964
|
+
expect(handleChange).toHaveBeenCalledWith(["banana"])
|
|
1965
|
+
})
|
|
1966
|
+
|
|
1967
|
+
it("removes last tag with Backspace when input is empty", async () => {
|
|
1968
|
+
const user = userEvent.setup()
|
|
1969
|
+
const handleChange = vi.fn()
|
|
1970
|
+
render(
|
|
1971
|
+
<Autocomplete
|
|
1972
|
+
multiple
|
|
1973
|
+
aria-label="Test autocomplete"
|
|
1974
|
+
defaultValue={["apple", "banana"]}
|
|
1975
|
+
options={defaultOptions}
|
|
1976
|
+
onChange={handleChange}
|
|
1977
|
+
/>
|
|
1978
|
+
)
|
|
1979
|
+
|
|
1980
|
+
const input = screen.getByRole("combobox")
|
|
1981
|
+
await user.click(input)
|
|
1982
|
+
await user.keyboard("{Backspace}")
|
|
1983
|
+
|
|
1984
|
+
expect(handleChange).toHaveBeenCalledWith(["apple"])
|
|
1985
|
+
})
|
|
1986
|
+
|
|
1987
|
+
it("does not remove tag with Backspace when input has text", async () => {
|
|
1988
|
+
const user = userEvent.setup()
|
|
1989
|
+
const handleChange = vi.fn()
|
|
1990
|
+
render(
|
|
1991
|
+
<Autocomplete
|
|
1992
|
+
multiple
|
|
1993
|
+
aria-label="Test autocomplete"
|
|
1994
|
+
defaultValue={["apple"]}
|
|
1995
|
+
options={defaultOptions}
|
|
1996
|
+
onChange={handleChange}
|
|
1997
|
+
/>
|
|
1998
|
+
)
|
|
1999
|
+
|
|
2000
|
+
const input = screen.getByRole("combobox")
|
|
2001
|
+
await user.type(input, "test")
|
|
2002
|
+
await user.keyboard("{Backspace}")
|
|
2003
|
+
|
|
2004
|
+
// Should only remove one character from input, not the tag
|
|
2005
|
+
expect(handleChange).not.toHaveBeenCalled()
|
|
2006
|
+
expect(input).toHaveValue("tes")
|
|
2007
|
+
})
|
|
2008
|
+
|
|
2009
|
+
// Integration
|
|
2010
|
+
it("works with showClear to clear all selections", async () => {
|
|
2011
|
+
const user = userEvent.setup()
|
|
2012
|
+
const handleChange = vi.fn()
|
|
2013
|
+
render(
|
|
2014
|
+
<Autocomplete
|
|
2015
|
+
multiple
|
|
2016
|
+
showClear
|
|
2017
|
+
aria-label="Test autocomplete"
|
|
2018
|
+
defaultValue={["apple", "banana"]}
|
|
2019
|
+
options={defaultOptions}
|
|
2020
|
+
onChange={handleChange}
|
|
2021
|
+
/>
|
|
2022
|
+
)
|
|
2023
|
+
|
|
2024
|
+
const clearButton = document.querySelector('[data-slot="autocomplete-clear"]')!
|
|
2025
|
+
await user.click(clearButton as HTMLElement)
|
|
2026
|
+
|
|
2027
|
+
expect(handleChange).toHaveBeenCalledWith([])
|
|
2028
|
+
})
|
|
2029
|
+
|
|
2030
|
+
it("shows check marks for selected items in dropdown", async () => {
|
|
2031
|
+
const user = userEvent.setup()
|
|
2032
|
+
render(
|
|
2033
|
+
<Autocomplete
|
|
2034
|
+
completeOnFocus
|
|
2035
|
+
multiple
|
|
2036
|
+
aria-label="Test autocomplete"
|
|
2037
|
+
defaultValue={["apple"]}
|
|
2038
|
+
options={defaultOptions}
|
|
2039
|
+
/>
|
|
2040
|
+
)
|
|
2041
|
+
|
|
2042
|
+
const input = screen.getByRole("combobox")
|
|
2043
|
+
await user.click(input)
|
|
2044
|
+
|
|
2045
|
+
await waitFor(() => {
|
|
2046
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2047
|
+
})
|
|
2048
|
+
|
|
2049
|
+
// Check that selected item has data-selected (use within to target dropdown option, not tag)
|
|
2050
|
+
const listbox = screen.getByRole("listbox")
|
|
2051
|
+
const appleOption = within(listbox).getByText("Apple").closest('[role="option"]')
|
|
2052
|
+
expect(appleOption).toHaveAttribute("data-selected", "true")
|
|
2053
|
+
})
|
|
2054
|
+
|
|
2055
|
+
it("clears input after selection in multiple mode", async () => {
|
|
2056
|
+
const user = userEvent.setup()
|
|
2057
|
+
render(<Autocomplete multiple aria-label="Test autocomplete" options={defaultOptions} />)
|
|
2058
|
+
|
|
2059
|
+
const input = screen.getByRole("combobox")
|
|
2060
|
+
await user.type(input, "app")
|
|
2061
|
+
|
|
2062
|
+
await waitFor(() => {
|
|
2063
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2064
|
+
})
|
|
2065
|
+
|
|
2066
|
+
await user.click(screen.getByText("Apple"))
|
|
2067
|
+
|
|
2068
|
+
// Input should be cleared after selection
|
|
2069
|
+
expect(input).toHaveValue("")
|
|
2070
|
+
})
|
|
2071
|
+
|
|
2072
|
+
it("works with controlled value as array", async () => {
|
|
2073
|
+
const user = userEvent.setup()
|
|
2074
|
+
const handleChange = vi.fn()
|
|
2075
|
+
|
|
2076
|
+
function ControlledMultiple() {
|
|
2077
|
+
const [value, setValue] = React.useState<string[]>(["apple"])
|
|
2078
|
+
return (
|
|
2079
|
+
<Autocomplete
|
|
2080
|
+
completeOnFocus
|
|
2081
|
+
multiple
|
|
2082
|
+
aria-label="Test autocomplete"
|
|
2083
|
+
options={defaultOptions}
|
|
2084
|
+
value={value}
|
|
2085
|
+
onChange={(newValue) => {
|
|
2086
|
+
setValue(newValue as string[])
|
|
2087
|
+
handleChange(newValue)
|
|
2088
|
+
}}
|
|
2089
|
+
/>
|
|
2090
|
+
)
|
|
2091
|
+
}
|
|
2092
|
+
|
|
2093
|
+
render(<ControlledMultiple />)
|
|
2094
|
+
|
|
2095
|
+
// Should have one tag initially
|
|
2096
|
+
let tags = document.querySelectorAll('[data-slot="autocomplete-tag"]')
|
|
2097
|
+
expect(tags).toHaveLength(1)
|
|
2098
|
+
|
|
2099
|
+
const input = screen.getByRole("combobox")
|
|
2100
|
+
await user.click(input)
|
|
2101
|
+
|
|
2102
|
+
await waitFor(() => {
|
|
2103
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2104
|
+
})
|
|
2105
|
+
|
|
2106
|
+
await user.click(screen.getByText("Banana"))
|
|
2107
|
+
|
|
2108
|
+
// Should have two tags now
|
|
2109
|
+
tags = document.querySelectorAll('[data-slot="autocomplete-tag"]')
|
|
2110
|
+
expect(tags).toHaveLength(2)
|
|
2111
|
+
})
|
|
2112
|
+
|
|
2113
|
+
it("handles keyboard selection in multiple mode", async () => {
|
|
2114
|
+
const user = userEvent.setup()
|
|
2115
|
+
const handleChange = vi.fn()
|
|
2116
|
+
render(
|
|
2117
|
+
<Autocomplete
|
|
2118
|
+
autoHighlight
|
|
2119
|
+
completeOnFocus
|
|
2120
|
+
multiple
|
|
2121
|
+
aria-label="Test autocomplete"
|
|
2122
|
+
options={defaultOptions}
|
|
2123
|
+
onChange={handleChange}
|
|
2124
|
+
/>
|
|
2125
|
+
)
|
|
2126
|
+
|
|
2127
|
+
const input = screen.getByRole("combobox")
|
|
2128
|
+
await user.click(input)
|
|
2129
|
+
|
|
2130
|
+
await waitFor(() => {
|
|
2131
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2132
|
+
})
|
|
2133
|
+
|
|
2134
|
+
// First item should be highlighted due to autoHighlight
|
|
2135
|
+
await user.keyboard("{Enter}")
|
|
2136
|
+
|
|
2137
|
+
expect(handleChange).toHaveBeenCalledWith(["apple"])
|
|
2138
|
+
// Dropdown should still be open
|
|
2139
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2140
|
+
})
|
|
2141
|
+
})
|
|
2142
|
+
|
|
2143
|
+
// ============================================
|
|
2144
|
+
// DROPDOWN BUTTON TESTS
|
|
2145
|
+
// ============================================
|
|
2146
|
+
describe("dropdown", () => {
|
|
2147
|
+
it("does not render dropdown button by default", () => {
|
|
2148
|
+
render(<Autocomplete aria-label="Test autocomplete" options={defaultOptions} />)
|
|
2149
|
+
expect(document.querySelector('[data-slot="autocomplete-dropdown"]')).not.toBeInTheDocument()
|
|
2150
|
+
})
|
|
2151
|
+
|
|
2152
|
+
it("renders dropdown button when dropdown is true", () => {
|
|
2153
|
+
render(
|
|
2154
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2155
|
+
)
|
|
2156
|
+
expect(document.querySelector('[data-slot="autocomplete-dropdown"]')).toBeInTheDocument()
|
|
2157
|
+
})
|
|
2158
|
+
|
|
2159
|
+
it("dropdown button has accessible name", () => {
|
|
2160
|
+
render(
|
|
2161
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2162
|
+
)
|
|
2163
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')
|
|
2164
|
+
expect(dropdownButton).toHaveAttribute("aria-label", "Toggle dropdown")
|
|
2165
|
+
})
|
|
2166
|
+
|
|
2167
|
+
it("opens dropdown when button is clicked", async () => {
|
|
2168
|
+
const user = userEvent.setup()
|
|
2169
|
+
render(
|
|
2170
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2171
|
+
)
|
|
2172
|
+
|
|
2173
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
2174
|
+
|
|
2175
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')!
|
|
2176
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2177
|
+
|
|
2178
|
+
await waitFor(() => {
|
|
2179
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2180
|
+
})
|
|
2181
|
+
})
|
|
2182
|
+
|
|
2183
|
+
it("closes dropdown when button is clicked while open", async () => {
|
|
2184
|
+
const user = userEvent.setup()
|
|
2185
|
+
render(
|
|
2186
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2187
|
+
)
|
|
2188
|
+
|
|
2189
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')!
|
|
2190
|
+
|
|
2191
|
+
// Open dropdown
|
|
2192
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2193
|
+
await waitFor(() => {
|
|
2194
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2195
|
+
})
|
|
2196
|
+
|
|
2197
|
+
// Close dropdown
|
|
2198
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2199
|
+
await waitFor(() => {
|
|
2200
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
2201
|
+
})
|
|
2202
|
+
})
|
|
2203
|
+
|
|
2204
|
+
it("clears input in 'blank' mode (default)", async () => {
|
|
2205
|
+
const user = userEvent.setup()
|
|
2206
|
+
render(
|
|
2207
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2208
|
+
)
|
|
2209
|
+
|
|
2210
|
+
const input = screen.getByRole("combobox")
|
|
2211
|
+
await user.type(input, "App")
|
|
2212
|
+
|
|
2213
|
+
expect(input).toHaveValue("App")
|
|
2214
|
+
|
|
2215
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')!
|
|
2216
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2217
|
+
|
|
2218
|
+
// Input should be cleared in blank mode
|
|
2219
|
+
expect(input).toHaveValue("")
|
|
2220
|
+
|
|
2221
|
+
await waitFor(() => {
|
|
2222
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2223
|
+
})
|
|
2224
|
+
|
|
2225
|
+
// All options should be visible
|
|
2226
|
+
expect(screen.getAllByRole("option")).toHaveLength(3)
|
|
2227
|
+
})
|
|
2228
|
+
|
|
2229
|
+
it("keeps query in 'current' mode", async () => {
|
|
2230
|
+
const user = userEvent.setup()
|
|
2231
|
+
render(
|
|
2232
|
+
<Autocomplete
|
|
2233
|
+
dropdown
|
|
2234
|
+
aria-label="Test autocomplete"
|
|
2235
|
+
dropdownMode="current"
|
|
2236
|
+
options={defaultOptions}
|
|
2237
|
+
/>
|
|
2238
|
+
)
|
|
2239
|
+
|
|
2240
|
+
const input = screen.getByRole("combobox")
|
|
2241
|
+
await user.type(input, "App")
|
|
2242
|
+
|
|
2243
|
+
expect(input).toHaveValue("App")
|
|
2244
|
+
|
|
2245
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')!
|
|
2246
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2247
|
+
|
|
2248
|
+
// Input should be kept in current mode
|
|
2249
|
+
expect(input).toHaveValue("App")
|
|
2250
|
+
|
|
2251
|
+
await waitFor(() => {
|
|
2252
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2253
|
+
})
|
|
2254
|
+
})
|
|
2255
|
+
|
|
2256
|
+
it("Alt+ArrowDown opens dropdown", async () => {
|
|
2257
|
+
const user = userEvent.setup()
|
|
2258
|
+
render(
|
|
2259
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2260
|
+
)
|
|
2261
|
+
|
|
2262
|
+
const input = screen.getByRole("combobox")
|
|
2263
|
+
await user.click(input)
|
|
2264
|
+
|
|
2265
|
+
expect(screen.queryByRole("listbox")).not.toBeInTheDocument()
|
|
2266
|
+
|
|
2267
|
+
await user.keyboard("{Alt>}{ArrowDown}{/Alt}")
|
|
2268
|
+
|
|
2269
|
+
await waitFor(() => {
|
|
2270
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2271
|
+
})
|
|
2272
|
+
})
|
|
2273
|
+
|
|
2274
|
+
it("does not show dropdown button when disabled", () => {
|
|
2275
|
+
render(
|
|
2276
|
+
<Autocomplete disabled dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2277
|
+
)
|
|
2278
|
+
// Dropdown button should be disabled
|
|
2279
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')
|
|
2280
|
+
expect(dropdownButton).toBeDisabled()
|
|
2281
|
+
})
|
|
2282
|
+
|
|
2283
|
+
it("triggers onOpenChange when dropdown button is clicked", async () => {
|
|
2284
|
+
const user = userEvent.setup()
|
|
2285
|
+
const handleOpenChange = vi.fn()
|
|
2286
|
+
render(
|
|
2287
|
+
<Autocomplete
|
|
2288
|
+
dropdown
|
|
2289
|
+
aria-label="Test autocomplete"
|
|
2290
|
+
options={defaultOptions}
|
|
2291
|
+
onOpenChange={handleOpenChange}
|
|
2292
|
+
/>
|
|
2293
|
+
)
|
|
2294
|
+
|
|
2295
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')!
|
|
2296
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2297
|
+
|
|
2298
|
+
expect(handleOpenChange).toHaveBeenCalledWith(true)
|
|
2299
|
+
})
|
|
2300
|
+
|
|
2301
|
+
it("renders ChevronDown icon in dropdown button", () => {
|
|
2302
|
+
render(
|
|
2303
|
+
<Autocomplete dropdown aria-label="Test autocomplete" options={defaultOptions} />
|
|
2304
|
+
)
|
|
2305
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')
|
|
2306
|
+
const icon = dropdownButton?.querySelector(".lucide-chevron-down")
|
|
2307
|
+
expect(icon).toBeInTheDocument()
|
|
2308
|
+
})
|
|
2309
|
+
|
|
2310
|
+
it("positions dropdown button after clear button when both present", async () => {
|
|
2311
|
+
const user = userEvent.setup()
|
|
2312
|
+
render(
|
|
2313
|
+
<Autocomplete
|
|
2314
|
+
dropdown
|
|
2315
|
+
showClear
|
|
2316
|
+
aria-label="Test autocomplete"
|
|
2317
|
+
options={defaultOptions}
|
|
2318
|
+
/>
|
|
2319
|
+
)
|
|
2320
|
+
|
|
2321
|
+
const input = screen.getByRole("combobox")
|
|
2322
|
+
await user.type(input, "App")
|
|
2323
|
+
|
|
2324
|
+
// Both buttons should be present
|
|
2325
|
+
expect(document.querySelector('[data-slot="autocomplete-clear"]')).toBeInTheDocument()
|
|
2326
|
+
expect(document.querySelector('[data-slot="autocomplete-dropdown"]')).toBeInTheDocument()
|
|
2327
|
+
})
|
|
2328
|
+
|
|
2329
|
+
it("works with completeOnFocus", async () => {
|
|
2330
|
+
const user = userEvent.setup()
|
|
2331
|
+
render(
|
|
2332
|
+
<Autocomplete
|
|
2333
|
+
completeOnFocus
|
|
2334
|
+
dropdown
|
|
2335
|
+
aria-label="Test autocomplete"
|
|
2336
|
+
options={defaultOptions}
|
|
2337
|
+
/>
|
|
2338
|
+
)
|
|
2339
|
+
|
|
2340
|
+
const dropdownButton = document.querySelector('[data-slot="autocomplete-dropdown"]')!
|
|
2341
|
+
await user.click(dropdownButton as HTMLElement)
|
|
2342
|
+
|
|
2343
|
+
await waitFor(() => {
|
|
2344
|
+
expect(screen.getByRole("listbox")).toBeInTheDocument()
|
|
2345
|
+
})
|
|
2346
|
+
|
|
2347
|
+
// All options should be visible
|
|
2348
|
+
expect(screen.getAllByRole("option")).toHaveLength(3)
|
|
2349
|
+
})
|
|
2350
|
+
})
|
|
2351
|
+
})
|