@pattern-stack/frontend-patterns 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +310 -0
- package/dist/atoms/composed/Accordion/Accordion.d.ts +20 -0
- package/dist/atoms/composed/Accordion/Accordion.d.ts.map +1 -0
- package/dist/atoms/composed/Accordion/index.d.ts +2 -0
- package/dist/atoms/composed/Accordion/index.d.ts.map +1 -0
- package/dist/atoms/composed/Alert/Alert.d.ts +25 -0
- package/dist/atoms/composed/Alert/Alert.d.ts.map +1 -0
- package/dist/atoms/composed/Alert/index.d.ts +2 -0
- package/dist/atoms/composed/Alert/index.d.ts.map +1 -0
- package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts +17 -0
- package/dist/atoms/composed/Breadcrumb/Breadcrumb.d.ts.map +1 -0
- package/dist/atoms/composed/Breadcrumb/index.d.ts +2 -0
- package/dist/atoms/composed/Breadcrumb/index.d.ts.map +1 -0
- package/dist/atoms/composed/Chart/Chart.d.ts +37 -0
- package/dist/atoms/composed/Chart/Chart.d.ts.map +1 -0
- package/dist/atoms/composed/Chart/index.d.ts +3 -0
- package/dist/atoms/composed/Chart/index.d.ts.map +1 -0
- package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts +19 -0
- package/dist/atoms/composed/ColorSwatch/ColorSwatch.d.ts.map +1 -0
- package/dist/atoms/composed/ColorSwatch/index.d.ts +2 -0
- package/dist/atoms/composed/ColorSwatch/index.d.ts.map +1 -0
- package/dist/atoms/composed/DarkModeToggle.d.ts +4 -0
- package/dist/atoms/composed/DarkModeToggle.d.ts.map +1 -0
- package/dist/atoms/composed/DataBadge/DataBadge.d.ts +13 -0
- package/dist/atoms/composed/DataBadge/DataBadge.d.ts.map +1 -0
- package/dist/atoms/composed/DataBadge/index.d.ts +2 -0
- package/dist/atoms/composed/DataBadge/index.d.ts.map +1 -0
- package/dist/atoms/composed/DataTable/DataTable.d.ts +28 -0
- package/dist/atoms/composed/DataTable/DataTable.d.ts.map +1 -0
- package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts +10 -0
- package/dist/atoms/composed/DataTable/TableCellWithTooltip.d.ts.map +1 -0
- package/dist/atoms/composed/DataTable/index.d.ts +3 -0
- package/dist/atoms/composed/DataTable/index.d.ts.map +1 -0
- package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts +45 -0
- package/dist/atoms/composed/DateTimePicker/DateTimePicker.d.ts.map +1 -0
- package/dist/atoms/composed/DateTimePicker/index.d.ts +3 -0
- package/dist/atoms/composed/DateTimePicker/index.d.ts.map +1 -0
- package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts +30 -0
- package/dist/atoms/composed/DetailedCard/DetailedCard.d.ts.map +1 -0
- package/dist/atoms/composed/DetailedCard/index.d.ts +3 -0
- package/dist/atoms/composed/DetailedCard/index.d.ts.map +1 -0
- package/dist/atoms/composed/EmptyState/EmptyState.d.ts +18 -0
- package/dist/atoms/composed/EmptyState/EmptyState.d.ts.map +1 -0
- package/dist/atoms/composed/EmptyState/index.d.ts +2 -0
- package/dist/atoms/composed/EmptyState/index.d.ts.map +1 -0
- package/dist/atoms/composed/FileUpload/FileUpload.d.ts +46 -0
- package/dist/atoms/composed/FileUpload/FileUpload.d.ts.map +1 -0
- package/dist/atoms/composed/FileUpload/index.d.ts +3 -0
- package/dist/atoms/composed/FileUpload/index.d.ts.map +1 -0
- package/dist/atoms/composed/FormField/FormField.d.ts +23 -0
- package/dist/atoms/composed/FormField/FormField.d.ts.map +1 -0
- package/dist/atoms/composed/FormField/index.d.ts +2 -0
- package/dist/atoms/composed/FormField/index.d.ts.map +1 -0
- package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts +8 -0
- package/dist/atoms/composed/GlobalSearch/GlobalSearch.d.ts.map +1 -0
- package/dist/atoms/composed/GlobalSearch/index.d.ts +2 -0
- package/dist/atoms/composed/GlobalSearch/index.d.ts.map +1 -0
- package/dist/atoms/composed/IconBadge/IconBadge.d.ts +16 -0
- package/dist/atoms/composed/IconBadge/IconBadge.d.ts.map +1 -0
- package/dist/atoms/composed/IconBadge/index.d.ts +3 -0
- package/dist/atoms/composed/IconBadge/index.d.ts.map +1 -0
- package/dist/atoms/composed/Modal/Modal.d.ts +18 -0
- package/dist/atoms/composed/Modal/Modal.d.ts.map +1 -0
- package/dist/atoms/composed/Modal/index.d.ts +3 -0
- package/dist/atoms/composed/Modal/index.d.ts.map +1 -0
- package/dist/atoms/composed/PaletteSwitcher.d.ts +7 -0
- package/dist/atoms/composed/PaletteSwitcher.d.ts.map +1 -0
- package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts +25 -0
- package/dist/atoms/composed/ProgressBar/ProgressBar.d.ts.map +1 -0
- package/dist/atoms/composed/ProgressBar/index.d.ts +2 -0
- package/dist/atoms/composed/ProgressBar/index.d.ts.map +1 -0
- package/dist/atoms/composed/StatCard/StatCard.d.ts +21 -0
- package/dist/atoms/composed/StatCard/StatCard.d.ts.map +1 -0
- package/dist/atoms/composed/StatCard/index.d.ts +2 -0
- package/dist/atoms/composed/StatCard/index.d.ts.map +1 -0
- package/dist/atoms/composed/StyleGuide.d.ts +3 -0
- package/dist/atoms/composed/StyleGuide.d.ts.map +1 -0
- package/dist/atoms/composed/Toast/Toast.d.ts +40 -0
- package/dist/atoms/composed/Toast/Toast.d.ts.map +1 -0
- package/dist/atoms/composed/Toast/index.d.ts +2 -0
- package/dist/atoms/composed/Toast/index.d.ts.map +1 -0
- package/dist/atoms/composed/Tooltip/Tooltip.d.ts +16 -0
- package/dist/atoms/composed/Tooltip/Tooltip.d.ts.map +1 -0
- package/dist/atoms/composed/Tooltip/index.d.ts +2 -0
- package/dist/atoms/composed/Tooltip/index.d.ts.map +1 -0
- package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts +8 -0
- package/dist/atoms/composed/UserAvatar/UserAvatar.d.ts.map +1 -0
- package/dist/atoms/composed/UserAvatar/index.d.ts +2 -0
- package/dist/atoms/composed/UserAvatar/index.d.ts.map +1 -0
- package/dist/atoms/composed/UserMenu/UserMenu.d.ts +8 -0
- package/dist/atoms/composed/UserMenu/UserMenu.d.ts.map +1 -0
- package/dist/atoms/composed/UserMenu/index.d.ts +2 -0
- package/dist/atoms/composed/UserMenu/index.d.ts.map +1 -0
- package/dist/atoms/composed/index.d.ts +25 -0
- package/dist/atoms/composed/index.d.ts.map +1 -0
- package/dist/atoms/hooks/useApi.d.ts +25 -0
- package/dist/atoms/hooks/useApi.d.ts.map +1 -0
- package/dist/atoms/hooks/useHealth.d.ts +19 -0
- package/dist/atoms/hooks/useHealth.d.ts.map +1 -0
- package/dist/atoms/index.d.ts +9 -0
- package/dist/atoms/index.d.ts.map +1 -0
- package/dist/atoms/services/api/client.d.ts +20 -0
- package/dist/atoms/services/api/client.d.ts.map +1 -0
- package/dist/atoms/services/auth-service.d.ts +24 -0
- package/dist/atoms/services/auth-service.d.ts.map +1 -0
- package/dist/atoms/services/health.d.ts +7 -0
- package/dist/atoms/services/health.d.ts.map +1 -0
- package/dist/atoms/services/index.d.ts +4 -0
- package/dist/atoms/services/index.d.ts.map +1 -0
- package/dist/atoms/shared/config/constants.d.ts +15 -0
- package/dist/atoms/shared/config/constants.d.ts.map +1 -0
- package/dist/atoms/shared/config/dashboard-sizes.d.ts +83 -0
- package/dist/atoms/shared/config/dashboard-sizes.d.ts.map +1 -0
- package/dist/atoms/shared/config/environment.d.ts +10 -0
- package/dist/atoms/shared/config/environment.d.ts.map +1 -0
- package/dist/atoms/shared/index.d.ts +4 -0
- package/dist/atoms/shared/index.d.ts.map +1 -0
- package/dist/atoms/types/auth.d.ts +56 -0
- package/dist/atoms/types/auth.d.ts.map +1 -0
- package/dist/atoms/types/generated.d.ts +1469 -0
- package/dist/atoms/types/generated.d.ts.map +1 -0
- package/dist/atoms/types/index.d.ts +4 -0
- package/dist/atoms/types/index.d.ts.map +1 -0
- package/dist/atoms/types/loading.d.ts +26 -0
- package/dist/atoms/types/loading.d.ts.map +1 -0
- package/dist/atoms/ui/Badge.d.ts +10 -0
- package/dist/atoms/ui/Badge.d.ts.map +1 -0
- package/dist/atoms/ui/ErrorBoundary.d.ts +18 -0
- package/dist/atoms/ui/ErrorBoundary.d.ts.map +1 -0
- package/dist/atoms/ui/Select.d.ts +28 -0
- package/dist/atoms/ui/Select.d.ts.map +1 -0
- package/dist/atoms/ui/Switch.d.ts +9 -0
- package/dist/atoms/ui/Switch.d.ts.map +1 -0
- package/dist/atoms/ui/Tabs.d.ts +30 -0
- package/dist/atoms/ui/Tabs.d.ts.map +1 -0
- package/dist/atoms/ui/avatar.d.ts +7 -0
- package/dist/atoms/ui/avatar.d.ts.map +1 -0
- package/dist/atoms/ui/button.d.ts +14 -0
- package/dist/atoms/ui/button.d.ts.map +1 -0
- package/dist/atoms/ui/card.d.ts +12 -0
- package/dist/atoms/ui/card.d.ts.map +1 -0
- package/dist/atoms/ui/dropdown-menu.d.ts +28 -0
- package/dist/atoms/ui/dropdown-menu.d.ts.map +1 -0
- package/dist/atoms/ui/index.d.ts +15 -0
- package/dist/atoms/ui/index.d.ts.map +1 -0
- package/dist/atoms/ui/input.d.ts +5 -0
- package/dist/atoms/ui/input.d.ts.map +1 -0
- package/dist/atoms/ui/label.d.ts +6 -0
- package/dist/atoms/ui/label.d.ts.map +1 -0
- package/dist/atoms/ui/skeleton.d.ts +3 -0
- package/dist/atoms/ui/skeleton.d.ts.map +1 -0
- package/dist/atoms/ui/spinner.d.ts +14 -0
- package/dist/atoms/ui/spinner.d.ts.map +1 -0
- package/dist/atoms/ui/table.d.ts +11 -0
- package/dist/atoms/ui/table.d.ts.map +1 -0
- package/dist/atoms/utils/animations.d.ts +65 -0
- package/dist/atoms/utils/animations.d.ts.map +1 -0
- package/dist/atoms/utils/tooltip-helpers.d.ts +71 -0
- package/dist/atoms/utils/tooltip-helpers.d.ts.map +1 -0
- package/dist/atoms/utils/utils.d.ts +4 -0
- package/dist/atoms/utils/utils.d.ts.map +1 -0
- package/dist/features/auth/components/LoginForm.d.ts +2 -0
- package/dist/features/auth/components/LoginForm.d.ts.map +1 -0
- package/dist/features/auth/components/LogoutButton.d.ts +2 -0
- package/dist/features/auth/components/LogoutButton.d.ts.map +1 -0
- package/dist/features/auth/components/ProtectedRoute.d.ts +10 -0
- package/dist/features/auth/components/ProtectedRoute.d.ts.map +1 -0
- package/dist/features/auth/components/index.d.ts +4 -0
- package/dist/features/auth/components/index.d.ts.map +1 -0
- package/dist/features/auth/hooks/index.d.ts +3 -0
- package/dist/features/auth/hooks/index.d.ts.map +1 -0
- package/dist/features/auth/hooks/useAuth.d.ts +10 -0
- package/dist/features/auth/hooks/useAuth.d.ts.map +1 -0
- package/dist/features/auth/hooks/usePermissions.d.ts +13 -0
- package/dist/features/auth/hooks/usePermissions.d.ts.map +1 -0
- package/dist/features/auth/index.d.ts +3 -0
- package/dist/features/auth/index.d.ts.map +1 -0
- package/dist/features/index.d.ts +2 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/frontend-patterns.css +567 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +10152 -0
- package/dist/index.es.js.map +1 -0
- package/dist/index.js +10170 -0
- package/dist/index.js.map +1 -0
- package/dist/molecules/forms/FormGroup.d.ts +17 -0
- package/dist/molecules/forms/FormGroup.d.ts.map +1 -0
- package/dist/molecules/forms/SearchInput.d.ts +36 -0
- package/dist/molecules/forms/SearchInput.d.ts.map +1 -0
- package/dist/molecules/forms/index.d.ts +3 -0
- package/dist/molecules/forms/index.d.ts.map +1 -0
- package/dist/molecules/index.d.ts +4 -0
- package/dist/molecules/index.d.ts.map +1 -0
- package/dist/molecules/layout/AppHeader/AppHeader.d.ts +7 -0
- package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +1 -0
- package/dist/molecules/layout/AppHeader/index.d.ts +2 -0
- package/dist/molecules/layout/AppHeader/index.d.ts.map +1 -0
- package/dist/molecules/layout/AppLayout.d.ts +2 -0
- package/dist/molecules/layout/AppLayout.d.ts.map +1 -0
- package/dist/molecules/layout/PageTemplate.d.ts +19 -0
- package/dist/molecules/layout/PageTemplate.d.ts.map +1 -0
- package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts +24 -0
- package/dist/molecules/layout/SectionHeader/SectionHeader.d.ts.map +1 -0
- package/dist/molecules/layout/SectionHeader/index.d.ts +2 -0
- package/dist/molecules/layout/SectionHeader/index.d.ts.map +1 -0
- package/dist/molecules/layout/ShowcaseSection.d.ts +22 -0
- package/dist/molecules/layout/ShowcaseSection.d.ts.map +1 -0
- package/dist/molecules/layout/Sidebar.d.ts +6 -0
- package/dist/molecules/layout/Sidebar.d.ts.map +1 -0
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts +13 -0
- package/dist/molecules/layout/SidebarButton/SidebarButton.d.ts.map +1 -0
- package/dist/molecules/layout/SidebarButton/index.d.ts +2 -0
- package/dist/molecules/layout/SidebarButton/index.d.ts.map +1 -0
- package/dist/molecules/layout/SidebarContext.d.ts +12 -0
- package/dist/molecules/layout/SidebarContext.d.ts.map +1 -0
- package/dist/molecules/layout/index.d.ts +8 -0
- package/dist/molecules/layout/index.d.ts.map +1 -0
- package/dist/molecules/navigation/NavMenu.d.ts +20 -0
- package/dist/molecules/navigation/NavMenu.d.ts.map +1 -0
- package/dist/molecules/navigation/Pagination.d.ts +14 -0
- package/dist/molecules/navigation/Pagination.d.ts.map +1 -0
- package/dist/molecules/navigation/index.d.ts +3 -0
- package/dist/molecules/navigation/index.d.ts.map +1 -0
- package/dist/organisms/index.d.ts +2 -0
- package/dist/organisms/index.d.ts.map +1 -0
- package/dist/organisms/showcase/ComponentShowcasePage.d.ts +3 -0
- package/dist/organisms/showcase/ComponentShowcasePage.d.ts.map +1 -0
- package/dist/templates/AuthTemplate.d.ts +68 -0
- package/dist/templates/AuthTemplate.d.ts.map +1 -0
- package/dist/templates/ComponentShowcaseTemplate.d.ts +53 -0
- package/dist/templates/ComponentShowcaseTemplate.d.ts.map +1 -0
- package/dist/templates/DashboardTemplate.d.ts +62 -0
- package/dist/templates/DashboardTemplate.d.ts.map +1 -0
- package/dist/templates/DataTemplate.d.ts +78 -0
- package/dist/templates/DataTemplate.d.ts.map +1 -0
- package/dist/templates/admin/AdminCRUDTemplate.d.ts +105 -0
- package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +1 -0
- package/dist/templates/admin/AdminDashboardTemplate.d.ts +89 -0
- package/dist/templates/admin/AdminDashboardTemplate.d.ts.map +1 -0
- package/dist/templates/admin/AdminDetailTemplate.d.ts +132 -0
- package/dist/templates/admin/AdminDetailTemplate.d.ts.map +1 -0
- package/dist/templates/admin/index.d.ts +4 -0
- package/dist/templates/admin/index.d.ts.map +1 -0
- package/dist/templates/factory.d.ts +28 -0
- package/dist/templates/factory.d.ts.map +1 -0
- package/dist/templates/index.d.ts +7 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/package.json +118 -0
- package/src/App.css +42 -0
- package/src/App.tsx +54 -0
- package/src/__tests__/README.md +221 -0
- package/src/__tests__/atoms/hooks/simple-hooks.test.ts +44 -0
- package/src/__tests__/atoms/ui/button.test.tsx +68 -0
- package/src/__tests__/atoms/utils/simple.test.ts +18 -0
- package/src/__tests__/atoms/utils/utils.test.ts +77 -0
- package/src/__tests__/features/auth/simple-auth.test.tsx +40 -0
- package/src/__tests__/molecules/layout/simple-layout.test.tsx +81 -0
- package/src/__tests__/organisms/showcase/simple-showcase.test.tsx +167 -0
- package/src/__tests__/setup.ts +51 -0
- package/src/__tests__/utils.tsx +123 -0
- package/src/atoms/composed/Accordion/Accordion.tsx +271 -0
- package/src/atoms/composed/Accordion/index.ts +1 -0
- package/src/atoms/composed/Alert/Alert.tsx +132 -0
- package/src/atoms/composed/Alert/index.ts +1 -0
- package/src/atoms/composed/Breadcrumb/Breadcrumb.tsx +83 -0
- package/src/atoms/composed/Breadcrumb/index.ts +1 -0
- package/src/atoms/composed/Chart/Chart.tsx +425 -0
- package/src/atoms/composed/Chart/index.ts +2 -0
- package/src/atoms/composed/ColorSwatch/ColorSwatch.tsx +72 -0
- package/src/atoms/composed/ColorSwatch/index.ts +1 -0
- package/src/atoms/composed/DarkModeToggle.tsx +66 -0
- package/src/atoms/composed/DataBadge/DataBadge.tsx +81 -0
- package/src/atoms/composed/DataBadge/index.ts +1 -0
- package/src/atoms/composed/DataTable/DataTable.tsx +394 -0
- package/src/atoms/composed/DataTable/TableCellWithTooltip.tsx +41 -0
- package/src/atoms/composed/DataTable/index.ts +2 -0
- package/src/atoms/composed/DateTimePicker/DateTimePicker.tsx +611 -0
- package/src/atoms/composed/DateTimePicker/index.ts +2 -0
- package/src/atoms/composed/DetailedCard/DetailedCard.tsx +181 -0
- package/src/atoms/composed/DetailedCard/index.ts +2 -0
- package/src/atoms/composed/EmptyState/EmptyState.tsx +90 -0
- package/src/atoms/composed/EmptyState/index.ts +1 -0
- package/src/atoms/composed/FileUpload/FileUpload.tsx +477 -0
- package/src/atoms/composed/FileUpload/index.ts +2 -0
- package/src/atoms/composed/FormField/FormField.tsx +92 -0
- package/src/atoms/composed/FormField/index.ts +1 -0
- package/src/atoms/composed/GlobalSearch/GlobalSearch.tsx +37 -0
- package/src/atoms/composed/GlobalSearch/index.ts +1 -0
- package/src/atoms/composed/IconBadge/IconBadge.tsx +95 -0
- package/src/atoms/composed/IconBadge/index.ts +2 -0
- package/src/atoms/composed/Modal/Modal.tsx +223 -0
- package/src/atoms/composed/Modal/index.ts +2 -0
- package/src/atoms/composed/PaletteSwitcher.tsx +386 -0
- package/src/atoms/composed/ProgressBar/ProgressBar.tsx +116 -0
- package/src/atoms/composed/ProgressBar/index.ts +1 -0
- package/src/atoms/composed/StatCard/StatCard.tsx +219 -0
- package/src/atoms/composed/StatCard/index.ts +1 -0
- package/src/atoms/composed/StyleGuide.tsx +717 -0
- package/src/atoms/composed/Toast/Toast.tsx +219 -0
- package/src/atoms/composed/Toast/index.ts +1 -0
- package/src/atoms/composed/Tooltip/Tooltip.tsx +213 -0
- package/src/atoms/composed/Tooltip/index.ts +1 -0
- package/src/atoms/composed/UserAvatar/UserAvatar.tsx +139 -0
- package/src/atoms/composed/UserAvatar/index.ts +1 -0
- package/src/atoms/composed/UserMenu/UserMenu.tsx +16 -0
- package/src/atoms/composed/UserMenu/index.ts +1 -0
- package/src/atoms/composed/index.ts +29 -0
- package/src/atoms/hooks/useApi.ts +80 -0
- package/src/atoms/hooks/useHealth.ts +17 -0
- package/src/atoms/index.ts +13 -0
- package/src/atoms/services/api/client.ts +134 -0
- package/src/atoms/services/auth-service.ts +248 -0
- package/src/atoms/services/health.ts +15 -0
- package/src/atoms/services/index.ts +3 -0
- package/src/atoms/shared/config/constants.ts +17 -0
- package/src/atoms/shared/config/dashboard-sizes.ts +111 -0
- package/src/atoms/shared/config/environment.ts +10 -0
- package/src/atoms/shared/index.ts +4 -0
- package/src/atoms/shared/styles/color-palettes.css +566 -0
- package/src/atoms/types/auth.ts +62 -0
- package/src/atoms/types/generated.ts +1469 -0
- package/src/atoms/types/index.ts +4 -0
- package/src/atoms/types/loading.ts +28 -0
- package/src/atoms/ui/Badge.tsx +30 -0
- package/src/atoms/ui/ErrorBoundary.tsx +59 -0
- package/src/atoms/ui/Select.tsx +53 -0
- package/src/atoms/ui/Switch.tsx +42 -0
- package/src/atoms/ui/Tabs.tsx +118 -0
- package/src/atoms/ui/avatar.tsx +48 -0
- package/src/atoms/ui/button.tsx +70 -0
- package/src/atoms/ui/card.tsx +76 -0
- package/src/atoms/ui/dropdown-menu.tsx +199 -0
- package/src/atoms/ui/index.ts +39 -0
- package/src/atoms/ui/input.tsx +23 -0
- package/src/atoms/ui/label.tsx +23 -0
- package/src/atoms/ui/skeleton.tsx +13 -0
- package/src/atoms/ui/spinner.tsx +49 -0
- package/src/atoms/ui/table.tsx +116 -0
- package/src/atoms/utils/animations.ts +135 -0
- package/src/atoms/utils/tooltip-helpers.ts +140 -0
- package/src/atoms/utils/utils.ts +9 -0
- package/src/features/auth/components/LoginForm.tsx +168 -0
- package/src/features/auth/components/LogoutButton.tsx +19 -0
- package/src/features/auth/components/ProtectedRoute.tsx +60 -0
- package/src/features/auth/components/index.ts +4 -0
- package/src/features/auth/hooks/index.ts +2 -0
- package/src/features/auth/hooks/useAuth.tsx +205 -0
- package/src/features/auth/hooks/usePermissions.ts +35 -0
- package/src/features/auth/index.ts +2 -0
- package/src/features/index.ts +2 -0
- package/src/index.css +704 -0
- package/src/index.ts +13 -0
- package/src/main.tsx +48 -0
- package/src/molecules/.gitkeep +0 -0
- package/src/molecules/forms/FormGroup.tsx +75 -0
- package/src/molecules/forms/SearchInput.tsx +259 -0
- package/src/molecules/forms/index.ts +4 -0
- package/src/molecules/index.ts +4 -0
- package/src/molecules/layout/AppHeader/AppHeader.tsx +42 -0
- package/src/molecules/layout/AppHeader/index.ts +1 -0
- package/src/molecules/layout/AppLayout.tsx +29 -0
- package/src/molecules/layout/PageTemplate.tsx +87 -0
- package/src/molecules/layout/SectionHeader/SectionHeader.tsx +87 -0
- package/src/molecules/layout/SectionHeader/index.ts +1 -0
- package/src/molecules/layout/ShowcaseSection.tsx +57 -0
- package/src/molecules/layout/Sidebar.tsx +144 -0
- package/src/molecules/layout/SidebarButton/SidebarButton.tsx +99 -0
- package/src/molecules/layout/SidebarButton/index.ts +1 -0
- package/src/molecules/layout/SidebarContext.tsx +31 -0
- package/src/molecules/layout/index.ts +7 -0
- package/src/molecules/navigation/NavMenu.tsx +188 -0
- package/src/molecules/navigation/Pagination.tsx +172 -0
- package/src/molecules/navigation/index.ts +4 -0
- package/src/organisms/index.ts +5 -0
- package/src/organisms/showcase/ComponentShowcasePage.tsx +2496 -0
- package/src/organisms/showcase/index.ts +1 -0
- package/src/pages/AdminShowcase/AdminCRUDShowcase.tsx +242 -0
- package/src/pages/AdminShowcase/AdminDashboardShowcase.tsx +171 -0
- package/src/pages/AdminShowcase/AdminDetailShowcase.tsx +385 -0
- package/src/pages/AdminShowcase/index.tsx +3 -0
- package/src/pages/ComponentShowcase/BadgesShowcase.tsx +188 -0
- package/src/pages/ComponentShowcase/CardsShowcase.tsx +392 -0
- package/src/pages/ComponentShowcase/PalettesShowcase.tsx +207 -0
- package/src/pages/ComponentShowcase/StatesShowcase.tsx +485 -0
- package/src/pages/ComponentShowcase/TablesShowcase.tsx +134 -0
- package/src/pages/ComponentShowcase/TypographyShowcase.tsx +255 -0
- package/src/pages/ComponentShowcase/index.tsx +188 -0
- package/src/pages/index.ts +2 -0
- package/src/templates/AuthTemplate.tsx +216 -0
- package/src/templates/ComponentShowcaseTemplate.tsx +173 -0
- package/src/templates/DashboardTemplate.tsx +232 -0
- package/src/templates/DataTemplate.tsx +319 -0
- package/src/templates/admin/AdminCRUDTemplate.tsx +630 -0
- package/src/templates/admin/AdminDashboardTemplate.tsx +351 -0
- package/src/templates/admin/AdminDetailTemplate.tsx +563 -0
- package/src/templates/admin/index.ts +29 -0
- package/src/templates/factory.tsx +169 -0
- package/src/templates/index.ts +37 -0
- package/src/vite-env.d.ts +1 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Frontend Testing Strategy
|
|
2
|
+
|
|
3
|
+
> Atomic design testing that mirrors your backend testing architecture
|
|
4
|
+
|
|
5
|
+
## 🏗️ Testing Architecture
|
|
6
|
+
|
|
7
|
+
Our frontend tests follow the **same atomic structure** as your backend tests:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
src/__tests__/
|
|
11
|
+
├── atoms/ # Unit tests (like backend atoms/)
|
|
12
|
+
│ ├── ui/ # UI component tests
|
|
13
|
+
│ ├── business/ # Business component tests
|
|
14
|
+
│ ├── hooks/ # React hooks tests
|
|
15
|
+
│ ├── utils/ # Utility function tests
|
|
16
|
+
│ └── services/ # API client tests
|
|
17
|
+
├── molecules/ # Component integration tests
|
|
18
|
+
│ ├── forms/ # Form component tests
|
|
19
|
+
│ ├── layout/ # Layout component tests
|
|
20
|
+
│ └── navigation/ # Navigation tests
|
|
21
|
+
├── organisms/ # Page/complex component tests
|
|
22
|
+
│ └── showcase/ # Full page tests
|
|
23
|
+
├── features/ # Feature integration tests
|
|
24
|
+
│ └── auth/ # Auth system tests
|
|
25
|
+
├── setup.ts # Test setup (like conftest.py)
|
|
26
|
+
└── utils.tsx # Test utilities (like pytest fixtures)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## 🧪 Testing Stack
|
|
30
|
+
|
|
31
|
+
**Framework**: Vitest (Vite-native, fast)
|
|
32
|
+
**React Testing**: @testing-library/react
|
|
33
|
+
**User Interaction**: @testing-library/user-event
|
|
34
|
+
**Environment**: jsdom (browser simulation)
|
|
35
|
+
**Assertions**: jest-dom matchers
|
|
36
|
+
|
|
37
|
+
## 📋 Test Commands
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Run all tests
|
|
41
|
+
npm run test
|
|
42
|
+
|
|
43
|
+
# Run with UI (visual test runner)
|
|
44
|
+
npm run test:ui
|
|
45
|
+
|
|
46
|
+
# Run once (CI mode)
|
|
47
|
+
npm run test:run
|
|
48
|
+
|
|
49
|
+
# Watch mode (development)
|
|
50
|
+
npm run test:watch
|
|
51
|
+
|
|
52
|
+
# Coverage report
|
|
53
|
+
npm run test:coverage
|
|
54
|
+
|
|
55
|
+
# Test by atomic level (like backend)
|
|
56
|
+
npm run test:atoms # Unit tests
|
|
57
|
+
npm run test:molecules # Component integration
|
|
58
|
+
npm run test:organisms # Page tests
|
|
59
|
+
npm run test:features # Feature integration
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 🎯 Testing Patterns
|
|
63
|
+
|
|
64
|
+
### Atoms (Unit Tests)
|
|
65
|
+
```typescript
|
|
66
|
+
// UI Components
|
|
67
|
+
describe('Button (Atom)', () => {
|
|
68
|
+
it('renders with correct variant styles', () => {
|
|
69
|
+
render(<Button variant="primary">Click me</Button>)
|
|
70
|
+
expect(screen.getByRole('button')).toHaveClass('bg-primary')
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
// Hooks
|
|
75
|
+
describe('useApi (Atom)', () => {
|
|
76
|
+
it('handles API calls correctly', async () => {
|
|
77
|
+
const { result } = renderHook(() => useApiQuery(['test'], mockFn))
|
|
78
|
+
await waitFor(() => expect(result.current.isSuccess).toBe(true))
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// Utils
|
|
83
|
+
describe('cn utility (Atom)', () => {
|
|
84
|
+
it('merges classes correctly', () => {
|
|
85
|
+
expect(cn('px-4', 'py-2')).toBe('px-4 py-2')
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Molecules (Component Integration)
|
|
91
|
+
```typescript
|
|
92
|
+
describe('AppHeader (Molecule)', () => {
|
|
93
|
+
it('integrates auth, navigation, and UI atoms', () => {
|
|
94
|
+
render(<AppHeader />)
|
|
95
|
+
expect(screen.getByRole('banner')).toBeInTheDocument()
|
|
96
|
+
expect(screen.getByTestId('user-menu')).toBeInTheDocument()
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Organisms (Page Tests)
|
|
102
|
+
```typescript
|
|
103
|
+
describe('ComponentShowcasePage (Organism)', () => {
|
|
104
|
+
it('renders complete page with all sections', () => {
|
|
105
|
+
render(<ComponentShowcasePage />)
|
|
106
|
+
expect(screen.getByRole('main')).toBeInTheDocument()
|
|
107
|
+
// Test full page functionality
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Features (Integration Tests)
|
|
113
|
+
```typescript
|
|
114
|
+
describe('useAuth (Feature)', () => {
|
|
115
|
+
it('handles complete auth flow', async () => {
|
|
116
|
+
const { result } = renderHook(() => useAuth())
|
|
117
|
+
await act(() => result.current.login(credentials))
|
|
118
|
+
expect(result.current.isAuthenticated).toBe(true)
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 🛠️ Test Utilities
|
|
124
|
+
|
|
125
|
+
### Setup (`setup.ts`)
|
|
126
|
+
- Global test configuration
|
|
127
|
+
- Mock browser APIs
|
|
128
|
+
- Environment setup
|
|
129
|
+
- Like your `conftest.py`
|
|
130
|
+
|
|
131
|
+
### Utils (`utils.tsx`)
|
|
132
|
+
- Custom render with providers
|
|
133
|
+
- Mock factories (users, API clients)
|
|
134
|
+
- Test helpers and fixtures
|
|
135
|
+
- Like your pytest fixtures
|
|
136
|
+
|
|
137
|
+
### Example Usage
|
|
138
|
+
```typescript
|
|
139
|
+
import { render, createMockUser, createMockApiClient } from '../utils'
|
|
140
|
+
|
|
141
|
+
describe('MyComponent', () => {
|
|
142
|
+
it('works with test utilities', () => {
|
|
143
|
+
const mockUser = createMockUser({ name: 'Test User' })
|
|
144
|
+
const mockApi = createMockApiClient()
|
|
145
|
+
|
|
146
|
+
render(<MyComponent />, {
|
|
147
|
+
initialRoute: '/dashboard',
|
|
148
|
+
queryClient: new QueryClient()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
})
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 🎨 Design System Testing
|
|
155
|
+
|
|
156
|
+
Our tests enforce design system compliance:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
it('uses design system classes only', () => {
|
|
160
|
+
render(<Component />)
|
|
161
|
+
const element = screen.getByRole('button')
|
|
162
|
+
|
|
163
|
+
// ✅ Should use design system
|
|
164
|
+
expect(element).toHaveClass('bg-primary')
|
|
165
|
+
|
|
166
|
+
// ❌ Should NOT use hardcoded colors
|
|
167
|
+
expect(element.className).not.toMatch(/bg-blue-\d+/)
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## 📊 Coverage Goals
|
|
172
|
+
|
|
173
|
+
- **Atoms**: 90%+ (pure functions, UI components)
|
|
174
|
+
- **Molecules**: 80%+ (component integration)
|
|
175
|
+
- **Organisms**: 70%+ (complex pages)
|
|
176
|
+
- **Features**: 85%+ (business logic)
|
|
177
|
+
|
|
178
|
+
## 🔄 CI/CD Integration
|
|
179
|
+
|
|
180
|
+
Tests run in pre-commit hooks:
|
|
181
|
+
```bash
|
|
182
|
+
npm run pre-commit # style:check + test:run + build
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## 🚀 Running Tests
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
# Development workflow
|
|
189
|
+
npm run test:watch # Watch mode while coding
|
|
190
|
+
|
|
191
|
+
# Before committing
|
|
192
|
+
npm run test:run # Full test suite
|
|
193
|
+
npm run test:coverage # Check coverage
|
|
194
|
+
|
|
195
|
+
# Debugging specific areas
|
|
196
|
+
npm run test:atoms # Just unit tests
|
|
197
|
+
npm run test:features # Just integration tests
|
|
198
|
+
|
|
199
|
+
# Visual debugging
|
|
200
|
+
npm run test:ui # Vitest UI runner
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## 🎯 Best Practices
|
|
204
|
+
|
|
205
|
+
1. **Mirror Backend Structure**: Tests follow atomic design
|
|
206
|
+
2. **Design System Compliance**: Test CSS class usage
|
|
207
|
+
3. **Integration Focus**: Test component interactions
|
|
208
|
+
4. **User-Centric**: Test user workflows, not implementation
|
|
209
|
+
5. **Mock External Dependencies**: API calls, localStorage, etc.
|
|
210
|
+
6. **Accessibility**: Test keyboard navigation, ARIA attributes
|
|
211
|
+
|
|
212
|
+
## 🔧 Configuration
|
|
213
|
+
|
|
214
|
+
- **vitest.config.ts**: Main test configuration
|
|
215
|
+
- **setup.ts**: Global test setup and mocks
|
|
216
|
+
- **utils.tsx**: Reusable test utilities and providers
|
|
217
|
+
- **package.json**: Test scripts and atomic-level commands
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
**Result**: Frontend testing that mirrors your backend's atomic architecture with comprehensive coverage and design system enforcement! 🧪⚛️
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { renderHook } from '@testing-library/react'
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
describe('Simple Hook Tests (Example)', () => {
|
|
6
|
+
it('useState hook works correctly', () => {
|
|
7
|
+
const { result } = renderHook(() => useState(0))
|
|
8
|
+
|
|
9
|
+
const [count, setCount] = result.current
|
|
10
|
+
expect(count).toBe(0)
|
|
11
|
+
|
|
12
|
+
// Test that hook function is returned
|
|
13
|
+
expect(typeof setCount).toBe('function')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('useEffect-like behavior can be tested', () => {
|
|
17
|
+
const { result, rerender } = renderHook(
|
|
18
|
+
({ initialValue }) => useState(initialValue),
|
|
19
|
+
{ initialProps: { initialValue: 10 } }
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
expect(result.current[0]).toBe(10)
|
|
23
|
+
|
|
24
|
+
// Test re-render with new props
|
|
25
|
+
rerender({ initialValue: 20 })
|
|
26
|
+
// Note: useState doesn't re-initialize on prop changes, this is just testing the pattern
|
|
27
|
+
expect(result.current[0]).toBe(10) // Still 10 because useState doesn't re-init
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('custom hook pattern testing', () => {
|
|
31
|
+
const useCounter = (initial = 0) => {
|
|
32
|
+
const [count, setCount] = useState(initial)
|
|
33
|
+
const increment = () => setCount(c => c + 1)
|
|
34
|
+
const decrement = () => setCount(c => c - 1)
|
|
35
|
+
return { count, increment, decrement }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { result } = renderHook(() => useCounter(5))
|
|
39
|
+
|
|
40
|
+
expect(result.current.count).toBe(5)
|
|
41
|
+
expect(typeof result.current.increment).toBe('function')
|
|
42
|
+
expect(typeof result.current.decrement).toBe('function')
|
|
43
|
+
})
|
|
44
|
+
})
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { describe, it, expect } from 'vitest'
|
|
3
|
+
import { render, screen, userEvent } from '../../utils'
|
|
4
|
+
import { Button } from '../../../atoms/ui/button'
|
|
5
|
+
|
|
6
|
+
describe('Button (Atom)', () => {
|
|
7
|
+
it('renders with default variant', () => {
|
|
8
|
+
render(<Button>Click me</Button>)
|
|
9
|
+
const button = screen.getByRole('button', { name: /click me/i })
|
|
10
|
+
expect(button).toBeInTheDocument()
|
|
11
|
+
// The button uses gradient, so check for primary color reference
|
|
12
|
+
expect(button.className).toContain('from-primary')
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('renders with different variants', () => {
|
|
16
|
+
const { rerender } = render(<Button variant="secondary">Secondary</Button>)
|
|
17
|
+
expect(screen.getByRole('button')).toHaveClass('bg-gray-100')
|
|
18
|
+
|
|
19
|
+
rerender(<Button variant="destructive">Destructive</Button>)
|
|
20
|
+
expect(screen.getByRole('button').className).toContain('from-destructive')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('renders with different sizes', () => {
|
|
24
|
+
const { rerender } = render(<Button size="sm">Small</Button>)
|
|
25
|
+
expect(screen.getByRole('button')).toHaveClass('h-9')
|
|
26
|
+
|
|
27
|
+
rerender(<Button size="lg">Large</Button>)
|
|
28
|
+
expect(screen.getByRole('button')).toHaveClass('h-12')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('handles click events', async () => {
|
|
32
|
+
const user = userEvent.setup()
|
|
33
|
+
const handleClick = vi.fn()
|
|
34
|
+
|
|
35
|
+
render(<Button onClick={handleClick}>Click me</Button>)
|
|
36
|
+
|
|
37
|
+
await user.click(screen.getByRole('button'))
|
|
38
|
+
expect(handleClick).toHaveBeenCalledOnce()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('is disabled when disabled prop is true', () => {
|
|
42
|
+
render(<Button disabled>Disabled</Button>)
|
|
43
|
+
const button = screen.getByRole('button')
|
|
44
|
+
expect(button).toBeDisabled()
|
|
45
|
+
expect(button).toHaveClass('disabled:pointer-events-none')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('renders as child component when asChild is true', () => {
|
|
49
|
+
render(
|
|
50
|
+
<Button asChild>
|
|
51
|
+
<a href="/test">Link button</a>
|
|
52
|
+
</Button>
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
const link = screen.getByRole('link')
|
|
56
|
+
expect(link).toBeInTheDocument()
|
|
57
|
+
expect(link.className).toContain('from-primary')
|
|
58
|
+
expect(link).toHaveAttribute('href', '/test')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('forwards ref correctly', () => {
|
|
62
|
+
const ref = React.createRef<HTMLButtonElement>()
|
|
63
|
+
render(<Button ref={ref}>Button</Button>)
|
|
64
|
+
|
|
65
|
+
expect(ref.current).toBeInstanceOf(HTMLButtonElement)
|
|
66
|
+
expect(ref.current).toHaveTextContent('Button')
|
|
67
|
+
})
|
|
68
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
|
|
3
|
+
describe('Simple Test (Example)', () => {
|
|
4
|
+
it('basic arithmetic works', () => {
|
|
5
|
+
expect(2 + 2).toBe(4)
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
it('string manipulation works', () => {
|
|
9
|
+
const str = 'hello world'
|
|
10
|
+
expect(str.toUpperCase()).toBe('HELLO WORLD')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('array operations work', () => {
|
|
14
|
+
const arr = [1, 2, 3]
|
|
15
|
+
expect(arr.length).toBe(3)
|
|
16
|
+
expect(arr.includes(2)).toBe(true)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { cn } from '../../../atoms/utils/utils'
|
|
3
|
+
|
|
4
|
+
describe('Utils (Atoms)', () => {
|
|
5
|
+
describe('cn (className utility)', () => {
|
|
6
|
+
it('merges class names correctly', () => {
|
|
7
|
+
const result = cn('px-4 py-2', 'bg-blue-500')
|
|
8
|
+
expect(result).toBe('px-4 py-2 bg-blue-500')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('handles conditional classes', () => {
|
|
12
|
+
const isActive = true
|
|
13
|
+
const isDisabled = false
|
|
14
|
+
|
|
15
|
+
const result = cn(
|
|
16
|
+
'base-class',
|
|
17
|
+
isActive && 'active-class',
|
|
18
|
+
isDisabled && 'disabled-class'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
expect(result).toBe('base-class active-class')
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('resolves conflicting Tailwind classes', () => {
|
|
25
|
+
// tailwind-merge should resolve conflicts
|
|
26
|
+
const result = cn('px-2 px-4') // px-4 should win
|
|
27
|
+
expect(result).toBe('px-4')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('handles arrays of classes', () => {
|
|
31
|
+
const result = cn(['px-4', 'py-2'], ['bg-blue-500', 'text-white'])
|
|
32
|
+
expect(result).toBe('px-4 py-2 bg-blue-500 text-white')
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('handles object syntax', () => {
|
|
36
|
+
const result = cn({
|
|
37
|
+
'px-4': true,
|
|
38
|
+
'py-2': true,
|
|
39
|
+
'bg-red-500': false,
|
|
40
|
+
'bg-blue-500': true,
|
|
41
|
+
})
|
|
42
|
+
expect(result).toBe('px-4 py-2 bg-blue-500')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('handles undefined and null values', () => {
|
|
46
|
+
const result = cn('px-4', undefined, null, 'py-2')
|
|
47
|
+
expect(result).toBe('px-4 py-2')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('handles empty strings', () => {
|
|
51
|
+
const result = cn('', 'px-4', '', 'py-2')
|
|
52
|
+
expect(result).toBe('px-4 py-2')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('maintains design system classes', () => {
|
|
56
|
+
// Test that design system classes are preserved
|
|
57
|
+
const result = cn('bg-primary text-primary-foreground', 'hover:bg-primary-hover')
|
|
58
|
+
expect(result).toBe('bg-primary text-primary-foreground hover:bg-primary-hover')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('works with status classes', () => {
|
|
62
|
+
const status = 'success'
|
|
63
|
+
const result = cn(
|
|
64
|
+
'base-class',
|
|
65
|
+
status === 'success' && 'status-success',
|
|
66
|
+
status === 'error' && 'status-error'
|
|
67
|
+
)
|
|
68
|
+
expect(result).toBe('base-class status-success')
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('works with category classes', () => {
|
|
72
|
+
const category = 2
|
|
73
|
+
const result = cn('base-class', `category-${category}`)
|
|
74
|
+
expect(result).toBe('base-class category-2')
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { describe, it, expect } from 'vitest'
|
|
3
|
+
import { render, screen } from '../../utils'
|
|
4
|
+
|
|
5
|
+
// Simple auth component for testing
|
|
6
|
+
const AuthStatus = ({ isAuthenticated }: { isAuthenticated: boolean }) => (
|
|
7
|
+
<div>
|
|
8
|
+
{isAuthenticated ? (
|
|
9
|
+
<span data-testid="authenticated">User is logged in</span>
|
|
10
|
+
) : (
|
|
11
|
+
<span data-testid="not-authenticated">Please log in</span>
|
|
12
|
+
)}
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
describe('Auth Components (Feature)', () => {
|
|
17
|
+
it('displays authenticated state correctly', () => {
|
|
18
|
+
render(<AuthStatus isAuthenticated={true} />)
|
|
19
|
+
|
|
20
|
+
expect(screen.getByTestId('authenticated')).toBeInTheDocument()
|
|
21
|
+
expect(screen.getByText('User is logged in')).toBeInTheDocument()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('displays unauthenticated state correctly', () => {
|
|
25
|
+
render(<AuthStatus isAuthenticated={false} />)
|
|
26
|
+
|
|
27
|
+
expect(screen.getByTestId('not-authenticated')).toBeInTheDocument()
|
|
28
|
+
expect(screen.getByText('Please log in')).toBeInTheDocument()
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('toggles between states correctly', () => {
|
|
32
|
+
const { rerender } = render(<AuthStatus isAuthenticated={false} />)
|
|
33
|
+
|
|
34
|
+
expect(screen.getByText('Please log in')).toBeInTheDocument()
|
|
35
|
+
|
|
36
|
+
rerender(<AuthStatus isAuthenticated={true} />)
|
|
37
|
+
|
|
38
|
+
expect(screen.getByText('User is logged in')).toBeInTheDocument()
|
|
39
|
+
})
|
|
40
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { describe, it, expect } from 'vitest'
|
|
3
|
+
import { render, screen } from '../../utils'
|
|
4
|
+
|
|
5
|
+
// Simple layout component for testing
|
|
6
|
+
const SimpleHeader = ({ title, showNav = true }: { title: string; showNav?: boolean }) => (
|
|
7
|
+
<header role="banner" className="bg-background border-b border-border">
|
|
8
|
+
<div className="flex items-center justify-between p-4">
|
|
9
|
+
<h1 className="text-xl font-semibold text-foreground">{title}</h1>
|
|
10
|
+
{showNav && (
|
|
11
|
+
<nav aria-label="Main navigation">
|
|
12
|
+
<ul className="flex space-x-4">
|
|
13
|
+
<li>
|
|
14
|
+
<a href="/" className="text-primary hover:text-primary-hover">Home</a>
|
|
15
|
+
</li>
|
|
16
|
+
<li>
|
|
17
|
+
<a href="/about" className="text-primary hover:text-primary-hover">About</a>
|
|
18
|
+
</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</nav>
|
|
21
|
+
)}
|
|
22
|
+
</div>
|
|
23
|
+
</header>
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
describe('Layout Components (Molecule)', () => {
|
|
27
|
+
it('renders header with title', () => {
|
|
28
|
+
render(<SimpleHeader title="Test App" />)
|
|
29
|
+
|
|
30
|
+
const header = screen.getByRole('banner')
|
|
31
|
+
expect(header).toBeInTheDocument()
|
|
32
|
+
|
|
33
|
+
const title = screen.getByRole('heading', { level: 1 })
|
|
34
|
+
expect(title).toHaveTextContent('Test App')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('renders navigation when showNav is true', () => {
|
|
38
|
+
render(<SimpleHeader title="Test App" showNav={true} />)
|
|
39
|
+
|
|
40
|
+
const nav = screen.getByRole('navigation', { name: /main navigation/i })
|
|
41
|
+
expect(nav).toBeInTheDocument()
|
|
42
|
+
|
|
43
|
+
expect(screen.getByRole('link', { name: /home/i })).toBeInTheDocument()
|
|
44
|
+
expect(screen.getByRole('link', { name: /about/i })).toBeInTheDocument()
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('hides navigation when showNav is false', () => {
|
|
48
|
+
render(<SimpleHeader title="Test App" showNav={false} />)
|
|
49
|
+
|
|
50
|
+
const nav = screen.queryByRole('navigation')
|
|
51
|
+
expect(nav).not.toBeInTheDocument()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('uses design system classes', () => {
|
|
55
|
+
render(<SimpleHeader title="Test App" />)
|
|
56
|
+
|
|
57
|
+
const header = screen.getByRole('banner')
|
|
58
|
+
|
|
59
|
+
// Should use design system classes
|
|
60
|
+
expect(header).toHaveClass('bg-background')
|
|
61
|
+
expect(header).toHaveClass('border-border')
|
|
62
|
+
|
|
63
|
+
// Should not use hardcoded color classes
|
|
64
|
+
expect(header.className).not.toMatch(/bg-(blue|red|green)-\d+/)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('has proper accessibility attributes', () => {
|
|
68
|
+
render(<SimpleHeader title="Test App" />)
|
|
69
|
+
|
|
70
|
+
const header = screen.getByRole('banner')
|
|
71
|
+
expect(header).toBeInTheDocument()
|
|
72
|
+
|
|
73
|
+
const nav = screen.getByRole('navigation')
|
|
74
|
+
expect(nav).toHaveAttribute('aria-label', 'Main navigation')
|
|
75
|
+
|
|
76
|
+
const links = screen.getAllByRole('link')
|
|
77
|
+
links.forEach(link => {
|
|
78
|
+
expect(link).toHaveAttribute('href')
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|