@brcarddev/frontend-commons 1.0.2
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 +241 -0
- package/eslint.config.js +190 -0
- package/package.json +161 -0
- package/src/components/atoms/AudioPlayer/audio-player.stories.tsx +36 -0
- package/src/components/atoms/AudioPlayer/audio-player.test.tsx +20 -0
- package/src/components/atoms/AudioPlayer/audio-player.tsx +13 -0
- package/src/components/atoms/AudioPlayer/index.ts +1 -0
- package/src/components/atoms/Badge/badge.stories.tsx +80 -0
- package/src/components/atoms/Badge/badge.test.tsx +59 -0
- package/src/components/atoms/Badge/badge.tsx +47 -0
- package/src/components/atoms/Badge/index.ts +1 -0
- package/src/components/atoms/Box/box.stories.tsx +37 -0
- package/src/components/atoms/Box/box.test.tsx +47 -0
- package/src/components/atoms/Box/box.tsx +7 -0
- package/src/components/atoms/Box/index.ts +1 -0
- package/src/components/atoms/Button/button.stories.tsx +108 -0
- package/src/components/atoms/Button/button.test.tsx +54 -0
- package/src/components/atoms/Button/button.tsx +96 -0
- package/src/components/atoms/Button/index.ts +1 -0
- package/src/components/atoms/ButtonUpload/button-upload.stories.tsx +137 -0
- package/src/components/atoms/ButtonUpload/button-upload.tsx +304 -0
- package/src/components/atoms/ButtonUpload/index.ts +1 -0
- package/src/components/atoms/Calendar/calendar.stories.tsx +51 -0
- package/src/components/atoms/Calendar/calendar.test.tsx +41 -0
- package/src/components/atoms/Calendar/calendar.tsx +107 -0
- package/src/components/atoms/Calendar/index.ts +1 -0
- package/src/components/atoms/CheckIcon/check-icon.stories.tsx +38 -0
- package/src/components/atoms/CheckIcon/check-icon.test.tsx +270 -0
- package/src/components/atoms/CheckIcon/check-icon.tsx +141 -0
- package/src/components/atoms/CheckIcon/index.ts +1 -0
- package/src/components/atoms/Checkbox/checkbox.stories.tsx +133 -0
- package/src/components/atoms/Checkbox/checkbox.test.tsx +70 -0
- package/src/components/atoms/Checkbox/checkbox.tsx +31 -0
- package/src/components/atoms/Checkbox/index.ts +1 -0
- package/src/components/atoms/Flex/flex.stories.tsx +33 -0
- package/src/components/atoms/Flex/flex.test.tsx +47 -0
- package/src/components/atoms/Flex/flex.tsx +7 -0
- package/src/components/atoms/Flex/index.ts +1 -0
- package/src/components/atoms/Grid/grid.stories.tsx +33 -0
- package/src/components/atoms/Grid/grid.test.tsx +47 -0
- package/src/components/atoms/Grid/grid.tsx +7 -0
- package/src/components/atoms/Grid/index.ts +1 -0
- package/src/components/atoms/Icon/icon.stories.tsx +169 -0
- package/src/components/atoms/Icon/icon.test.tsx +272 -0
- package/src/components/atoms/Icon/icon.tsx +119 -0
- package/src/components/atoms/Icon/index.ts +1 -0
- package/src/components/atoms/Input/index.ts +1 -0
- package/src/components/atoms/Input/input.stories.tsx +704 -0
- package/src/components/atoms/Input/input.test.tsx +86 -0
- package/src/components/atoms/Input/input.tsx +161 -0
- package/src/components/atoms/InputMoney/index.ts +2 -0
- package/src/components/atoms/InputMoney/input-money.stories.tsx +240 -0
- package/src/components/atoms/InputMoney/input-money.test.tsx +98 -0
- package/src/components/atoms/InputMoney/input-money.tsx +254 -0
- package/src/components/atoms/InputPhone/index.ts +2 -0
- package/src/components/atoms/InputPhone/input-phone.stories.tsx +447 -0
- package/src/components/atoms/InputPhone/input-phone.test.tsx +148 -0
- package/src/components/atoms/InputPhone/input-phone.tsx +267 -0
- package/src/components/atoms/InputSearch/index.ts +2 -0
- package/src/components/atoms/InputSearch/input-search.stories.tsx +360 -0
- package/src/components/atoms/InputSearch/input-search.test.tsx +239 -0
- package/src/components/atoms/InputSearch/input-search.tsx +210 -0
- package/src/components/atoms/InputUpload/index.ts +1 -0
- package/src/components/atoms/InputUpload/input-upload.stories.tsx +229 -0
- package/src/components/atoms/InputUpload/input-upload.test.tsx +556 -0
- package/src/components/atoms/InputUpload/input-upload.tsx +434 -0
- package/src/components/atoms/InputWithButton/index.ts +2 -0
- package/src/components/atoms/InputWithButton/input-with-button.stories.tsx +503 -0
- package/src/components/atoms/InputWithButton/input-with-button.test.tsx +128 -0
- package/src/components/atoms/InputWithButton/input-with-button.tsx +170 -0
- package/src/components/atoms/Label/index.ts +1 -0
- package/src/components/atoms/Label/label.stories.tsx +90 -0
- package/src/components/atoms/Label/label.test.tsx +59 -0
- package/src/components/atoms/Label/label.tsx +43 -0
- package/src/components/atoms/Progress/index.ts +1 -0
- package/src/components/atoms/Progress/progress.stories.tsx +30 -0
- package/src/components/atoms/Progress/progress.test.tsx +63 -0
- package/src/components/atoms/Progress/progress.tsx +32 -0
- package/src/components/atoms/RenderCondition/index.ts +1 -0
- package/src/components/atoms/RenderCondition/render-condition.stories.tsx +28 -0
- package/src/components/atoms/RenderCondition/render-condition.test.tsx +27 -0
- package/src/components/atoms/RenderCondition/render-condition.tsx +9 -0
- package/src/components/atoms/RichTextEditor/index.ts +1 -0
- package/src/components/atoms/RichTextEditor/rich-text-editor.stories.tsx +214 -0
- package/src/components/atoms/RichTextEditor/rich-text-editor.test.tsx +442 -0
- package/src/components/atoms/RichTextEditor/rich-text-editor.tsx +202 -0
- package/src/components/atoms/Separator/index.ts +1 -0
- package/src/components/atoms/Separator/separator.stories.tsx +117 -0
- package/src/components/atoms/Separator/separator.test.tsx +50 -0
- package/src/components/atoms/Separator/separator.tsx +28 -0
- package/src/components/atoms/Skeleton/index.ts +1 -0
- package/src/components/atoms/Skeleton/skeleton.stories.tsx +84 -0
- package/src/components/atoms/Skeleton/skeleton.test.tsx +39 -0
- package/src/components/atoms/Skeleton/skeleton.tsx +14 -0
- package/src/components/atoms/Slider/index.ts +1 -0
- package/src/components/atoms/Slider/slider.stories.tsx +28 -0
- package/src/components/atoms/Slider/slider.test.tsx +90 -0
- package/src/components/atoms/Slider/slider.tsx +54 -0
- package/src/components/atoms/Sonner/index.ts +1 -0
- package/src/components/atoms/Sonner/sonner.css +39 -0
- package/src/components/atoms/Sonner/sonner.stories.tsx +261 -0
- package/src/components/atoms/Sonner/sonner.test.tsx +24 -0
- package/src/components/atoms/Sonner/sonner.tsx +13 -0
- package/src/components/atoms/Switch/index.ts +1 -0
- package/src/components/atoms/Switch/switch.stories.tsx +128 -0
- package/src/components/atoms/Switch/switch.test.tsx +70 -0
- package/src/components/atoms/Switch/switch.tsx +61 -0
- package/src/components/atoms/Textarea/index.ts +1 -0
- package/src/components/atoms/Textarea/textarea.stories.tsx +169 -0
- package/src/components/atoms/Textarea/textarea.test.tsx +56 -0
- package/src/components/atoms/Textarea/textarea.tsx +26 -0
- package/src/components/atoms/Toggle/index.ts +1 -0
- package/src/components/atoms/Toggle/toggle.stories.tsx +170 -0
- package/src/components/atoms/Toggle/toggle.test.tsx +62 -0
- package/src/components/atoms/Toggle/toggle.tsx +47 -0
- package/src/components/atoms/Typography/index.ts +1 -0
- package/src/components/atoms/Typography/typography.stories.tsx +95 -0
- package/src/components/atoms/Typography/typography.test.tsx +66 -0
- package/src/components/atoms/Typography/typography.tsx +63 -0
- package/src/components/atoms/UploadImageField/index.ts +1 -0
- package/src/components/atoms/UploadImageField/upload-image-field.stories.tsx +249 -0
- package/src/components/atoms/UploadImageField/upload-image-field.test.tsx +348 -0
- package/src/components/atoms/UploadImageField/upload-image-field.tsx +362 -0
- package/src/components/atoms/VideoPlayer/index.ts +1 -0
- package/src/components/atoms/VideoPlayer/video-player.stories.tsx +37 -0
- package/src/components/atoms/VideoPlayer/video-player.test.tsx +20 -0
- package/src/components/atoms/VideoPlayer/video-player.tsx +26 -0
- package/src/components/atoms/index.ts +31 -0
- package/src/components/icons/alert-circle.tsx +22 -0
- package/src/components/icons/align-center.tsx +22 -0
- package/src/components/icons/align-left.tsx +22 -0
- package/src/components/icons/annotation-dots.tsx +16 -0
- package/src/components/icons/annotation-question.tsx +15 -0
- package/src/components/icons/annotation.tsx +22 -0
- package/src/components/icons/announcement-01.tsx +15 -0
- package/src/components/icons/announcement-02.tsx +15 -0
- package/src/components/icons/apple-logo.tsx +9 -0
- package/src/components/icons/arrow-circle-broken-right.tsx +16 -0
- package/src/components/icons/arrow-down.tsx +9 -0
- package/src/components/icons/arrow-up.tsx +9 -0
- package/src/components/icons/at-sign.tsx +21 -0
- package/src/components/icons/award-01.tsx +15 -0
- package/src/components/icons/award-03.tsx +16 -0
- package/src/components/icons/bank-note-01.tsx +15 -0
- package/src/components/icons/bar-chart-square-02.tsx +9 -0
- package/src/components/icons/bell-01.tsx +9 -0
- package/src/components/icons/bell-04.tsx +16 -0
- package/src/components/icons/bold-01.tsx +22 -0
- package/src/components/icons/book-open-01.tsx +15 -0
- package/src/components/icons/brackets-ellipses.tsx +22 -0
- package/src/components/icons/briefcase-01.tsx +9 -0
- package/src/components/icons/brush-01.tsx +22 -0
- package/src/components/icons/building-02.tsx +9 -0
- package/src/components/icons/building-06.tsx +9 -0
- package/src/components/icons/calendar-minus-02.tsx +15 -0
- package/src/components/icons/calendar.tsx +9 -0
- package/src/components/icons/certificate-01.tsx +16 -0
- package/src/components/icons/chart-breakout-square.tsx +16 -0
- package/src/components/icons/check-circle-02.tsx +16 -0
- package/src/components/icons/check-circle.tsx +15 -0
- package/src/components/icons/check.tsx +21 -0
- package/src/components/icons/chevron-down-double.tsx +16 -0
- package/src/components/icons/chevron-down.tsx +9 -0
- package/src/components/icons/chevron-left-double.tsx +22 -0
- package/src/components/icons/chevron-left.tsx +16 -0
- package/src/components/icons/chevron-right-double.tsx +22 -0
- package/src/components/icons/chevron-right.tsx +16 -0
- package/src/components/icons/chevron-up-double.tsx +16 -0
- package/src/components/icons/clock-rewind.tsx +15 -0
- package/src/components/icons/clock-stopwatch.tsx +15 -0
- package/src/components/icons/coins-hand.tsx +15 -0
- package/src/components/icons/coins-stacked-01.tsx +15 -0
- package/src/components/icons/coins-stacked-02.tsx +15 -0
- package/src/components/icons/container.tsx +9 -0
- package/src/components/icons/copy-02.tsx +16 -0
- package/src/components/icons/copy-04.tsx +21 -0
- package/src/components/icons/corner-down-right.tsx +9 -0
- package/src/components/icons/countries/br.tsx +20 -0
- package/src/components/icons/countries/es.tsx +19 -0
- package/src/components/icons/countries/index.ts +3 -0
- package/src/components/icons/countries/us.tsx +21 -0
- package/src/components/icons/dataflow-03.tsx +15 -0
- package/src/components/icons/dotpoints-01.tsx +22 -0
- package/src/components/icons/dots-vertical.tsx +35 -0
- package/src/components/icons/download-03.tsx +16 -0
- package/src/components/icons/download-cloud-02.tsx +15 -0
- package/src/components/icons/drag.tsx +14 -0
- package/src/components/icons/dropper.tsx +21 -0
- package/src/components/icons/edit-01.tsx +15 -0
- package/src/components/icons/edit-02.tsx +9 -0
- package/src/components/icons/edit-03.tsx +23 -0
- package/src/components/icons/eye.tsx +22 -0
- package/src/components/icons/face-frown.tsx +15 -0
- package/src/components/icons/face-happy.tsx +15 -0
- package/src/components/icons/file-06.tsx +15 -0
- package/src/components/icons/file-attachment-04.tsx +15 -0
- package/src/components/icons/file-download-02.tsx +16 -0
- package/src/components/icons/file-plus-02.tsx +16 -0
- package/src/components/icons/file-search-01.tsx +22 -0
- package/src/components/icons/file-search-03.tsx +15 -0
- package/src/components/icons/filter-lines.tsx +21 -0
- package/src/components/icons/first-category.tsx +16 -0
- package/src/components/icons/first-stage.tsx +19 -0
- package/src/components/icons/folder.tsx +15 -0
- package/src/components/icons/google.tsx +12 -0
- package/src/components/icons/graduation-hat-02.tsx +15 -0
- package/src/components/icons/grid-01.tsx +36 -0
- package/src/components/icons/help-circle.tsx +16 -0
- package/src/components/icons/help-square.tsx +16 -0
- package/src/components/icons/home-line.tsx +21 -0
- package/src/components/icons/icons.stories.tsx +199 -0
- package/src/components/icons/index.ts +140 -0
- package/src/components/icons/info-circle.tsx +15 -0
- package/src/components/icons/italic-01.tsx +22 -0
- package/src/components/icons/last-category.tsx +11 -0
- package/src/components/icons/last-stage.tsx +19 -0
- package/src/components/icons/layout-alt-04.tsx +9 -0
- package/src/components/icons/lightbulb-02.tsx +16 -0
- package/src/components/icons/link-01.tsx +16 -0
- package/src/components/icons/link-broken-01.tsx +16 -0
- package/src/components/icons/linkedin-logo.tsx +17 -0
- package/src/components/icons/lock-01.tsx +21 -0
- package/src/components/icons/log-out-01.tsx +9 -0
- package/src/components/icons/mail-01.tsx +16 -0
- package/src/components/icons/marker-pin-02.tsx +10 -0
- package/src/components/icons/menu-01.tsx +9 -0
- package/src/components/icons/middle-category.tsx +10 -0
- package/src/components/icons/middle-stage.tsx +19 -0
- package/src/components/icons/ms-outlook.tsx +47 -0
- package/src/components/icons/paragraph-spacing.tsx +15 -0
- package/src/components/icons/phone-01.tsx +21 -0
- package/src/components/icons/pie-chart-02.tsx +22 -0
- package/src/components/icons/plus-circle.tsx +15 -0
- package/src/components/icons/portal-logo.tsx +76 -0
- package/src/components/icons/presentation-chart-02.tsx +14 -0
- package/src/components/icons/route.tsx +22 -0
- package/src/components/icons/save-01.tsx +15 -0
- package/src/components/icons/search-lg.tsx +15 -0
- package/src/components/icons/search-sm.tsx +9 -0
- package/src/components/icons/send-03.tsx +15 -0
- package/src/components/icons/settings-01.tsx +17 -0
- package/src/components/icons/settings-03.tsx +14 -0
- package/src/components/icons/share-05.tsx +15 -0
- package/src/components/icons/share-06.tsx +15 -0
- package/src/components/icons/slash-circle-01.tsx +9 -0
- package/src/components/icons/star-01.tsx +9 -0
- package/src/components/icons/step-icon-active.tsx +11 -0
- package/src/components/icons/step-icon-checked.tsx +10 -0
- package/src/components/icons/step-icon-default.tsx +11 -0
- package/src/components/icons/switch-horizontal-01.tsx +9 -0
- package/src/components/icons/table-01.tsx +15 -0
- package/src/components/icons/tag-01.tsx +15 -0
- package/src/components/icons/tag-03.tsx +15 -0
- package/src/components/icons/tool-02.tsx +14 -0
- package/src/components/icons/trash-01.tsx +15 -0
- package/src/components/icons/underline-01.tsx +22 -0
- package/src/components/icons/upload-cloud-02.tsx +15 -0
- package/src/components/icons/user-01.tsx +9 -0
- package/src/components/icons/user-03.tsx +9 -0
- package/src/components/icons/user-check-01.tsx +14 -0
- package/src/components/icons/user-circle.tsx +14 -0
- package/src/components/icons/user-edit.tsx +16 -0
- package/src/components/icons/user-minus-02.tsx +15 -0
- package/src/components/icons/user-plus-01.tsx +22 -0
- package/src/components/icons/user-plus-02.tsx +15 -0
- package/src/components/icons/user-square.tsx +16 -0
- package/src/components/icons/users-01.tsx +14 -0
- package/src/components/icons/users-plus-01.tsx +21 -0
- package/src/components/icons/users-plus.tsx +16 -0
- package/src/components/icons/vertical-drag.tsx +21 -0
- package/src/components/icons/x-circle.tsx +15 -0
- package/src/components/icons/x-close.tsx +9 -0
- package/src/components/icons/zap-fast.tsx +16 -0
- package/src/components/icons/zap.tsx +9 -0
- package/src/components/index.ts +4 -0
- package/src/components/molecules/Accordion/accordion.stories.tsx +81 -0
- package/src/components/molecules/Accordion/accordion.test.tsx +91 -0
- package/src/components/molecules/Accordion/accordion.tsx +65 -0
- package/src/components/molecules/Accordion/index.ts +1 -0
- package/src/components/molecules/Alert/alert.stories.tsx +75 -0
- package/src/components/molecules/Alert/alert.test.tsx +58 -0
- package/src/components/molecules/Alert/alert.tsx +67 -0
- package/src/components/molecules/Alert/index.ts +1 -0
- package/src/components/molecules/AlertDialog/alert-dialog.stories.tsx +55 -0
- package/src/components/molecules/AlertDialog/alert-dialog.test.tsx +34 -0
- package/src/components/molecules/AlertDialog/alert-dialog.tsx +172 -0
- package/src/components/molecules/AlertDialog/index.ts +1 -0
- package/src/components/molecules/Avatar/avatar.stories.tsx +98 -0
- package/src/components/molecules/Avatar/avatar.test.tsx +55 -0
- package/src/components/molecules/Avatar/avatar.tsx +55 -0
- package/src/components/molecules/Avatar/index.ts +1 -0
- package/src/components/molecules/Breadcrumb/breadcrumb.stories.tsx +125 -0
- package/src/components/molecules/Breadcrumb/breadcrumb.test.tsx +118 -0
- package/src/components/molecules/Breadcrumb/breadcrumb.tsx +120 -0
- package/src/components/molecules/Breadcrumb/index.ts +1 -0
- package/src/components/molecules/Card/card.stories.tsx +109 -0
- package/src/components/molecules/Card/card.test.tsx +103 -0
- package/src/components/molecules/Card/card.tsx +78 -0
- package/src/components/molecules/Card/index.ts +1 -0
- package/src/components/molecules/Collapsible/collapsible.stories.tsx +28 -0
- package/src/components/molecules/Collapsible/collapsible.test.tsx +36 -0
- package/src/components/molecules/Collapsible/collapsible.tsx +31 -0
- package/src/components/molecules/Collapsible/index.ts +1 -0
- package/src/components/molecules/ColorPicker/color-picker.stories.tsx +40 -0
- package/src/components/molecules/ColorPicker/color-picker.tsx +106 -0
- package/src/components/molecules/ColorPicker/index.ts +1 -0
- package/src/components/molecules/ColorScale/color-scale.stories.tsx +31 -0
- package/src/components/molecules/ColorScale/color-scale.tsx +74 -0
- package/src/components/molecules/ColorScale/index.ts +1 -0
- package/src/components/molecules/Command/command.stories.tsx +71 -0
- package/src/components/molecules/Command/command.test.tsx +50 -0
- package/src/components/molecules/Command/command.tsx +177 -0
- package/src/components/molecules/Command/index.ts +1 -0
- package/src/components/molecules/ContextMenu/context-menu.stories.tsx +69 -0
- package/src/components/molecules/ContextMenu/context-menu.test.tsx +25 -0
- package/src/components/molecules/ContextMenu/context-menu.tsx +259 -0
- package/src/components/molecules/ContextMenu/index.ts +1 -0
- package/src/components/molecules/DatePicker/date-picker.stories.tsx +204 -0
- package/src/components/molecules/DatePicker/date-picker.test.tsx +27 -0
- package/src/components/molecules/DatePicker/date-picker.tsx +486 -0
- package/src/components/molecules/DatePicker/index.ts +1 -0
- package/src/components/molecules/Dialog/dialog.stories.tsx +48 -0
- package/src/components/molecules/Dialog/dialog.test.tsx +51 -0
- package/src/components/molecules/Dialog/dialog.tsx +150 -0
- package/src/components/molecules/Dialog/index.ts +1 -0
- package/src/components/molecules/Drawer/drawer.stories.tsx +182 -0
- package/src/components/molecules/Drawer/drawer.test.tsx +100 -0
- package/src/components/molecules/Drawer/drawer.tsx +206 -0
- package/src/components/molecules/Drawer/index.ts +1 -0
- package/src/components/molecules/Dropdown/dropdown-async.stories.tsx +15 -0
- package/src/components/molecules/Dropdown/dropdown.stories.tsx +112 -0
- package/src/components/molecules/Dropdown/dropdown.test.tsx +128 -0
- package/src/components/molecules/Dropdown/dropdown.tsx +322 -0
- package/src/components/molecules/Dropdown/index.ts +1 -0
- package/src/components/molecules/DropdownMenu/dropdown-menu.stories.tsx +154 -0
- package/src/components/molecules/DropdownMenu/dropdown-menu.test.tsx +163 -0
- package/src/components/molecules/DropdownMenu/dropdown-menu.tsx +313 -0
- package/src/components/molecules/DropdownMenu/index.ts +1 -0
- package/src/components/molecules/HoverCard/hover-card.stories.tsx +48 -0
- package/src/components/molecules/HoverCard/hover-card.test.tsx +42 -0
- package/src/components/molecules/HoverCard/hover-card.tsx +44 -0
- package/src/components/molecules/HoverCard/index.ts +1 -0
- package/src/components/molecules/InputOTP/index.ts +1 -0
- package/src/components/molecules/InputOTP/input-otp.stories.tsx +52 -0
- package/src/components/molecules/InputOTP/input-otp.test.tsx +28 -0
- package/src/components/molecules/InputOTP/input-otp.tsx +76 -0
- package/src/components/molecules/Menubar/index.ts +1 -0
- package/src/components/molecules/Menubar/menubar.stories.tsx +113 -0
- package/src/components/molecules/Menubar/menubar.test.tsx +42 -0
- package/src/components/molecules/Menubar/menubar.tsx +314 -0
- package/src/components/molecules/NavigationMenu/index.ts +1 -0
- package/src/components/molecules/NavigationMenu/navigation-menu.stories.tsx +143 -0
- package/src/components/molecules/NavigationMenu/navigation-menu.test.tsx +69 -0
- package/src/components/molecules/NavigationMenu/navigation-menu.tsx +174 -0
- package/src/components/molecules/PDFViewer/index.ts +1 -0
- package/src/components/molecules/PDFViewer/pdf-viewer.stories.tsx +34 -0
- package/src/components/molecules/PDFViewer/pdf-viewer.test.tsx +26 -0
- package/src/components/molecules/PDFViewer/pdf-viewer.tsx +358 -0
- package/src/components/molecules/Pagination/index.ts +1 -0
- package/src/components/molecules/Pagination/pagination.stories.tsx +193 -0
- package/src/components/molecules/Pagination/pagination.test.tsx +448 -0
- package/src/components/molecules/Pagination/pagination.tsx +206 -0
- package/src/components/molecules/PaginationDotGroup/index.ts +1 -0
- package/src/components/molecules/PaginationDotGroup/pagination-dot-group.stories.tsx +211 -0
- package/src/components/molecules/PaginationDotGroup/pagination-dot-group.test.tsx +385 -0
- package/src/components/molecules/PaginationDotGroup/pagination-dot-group.tsx +119 -0
- package/src/components/molecules/Popover/index.ts +1 -0
- package/src/components/molecules/Popover/popover-menu.stories.tsx +27 -0
- package/src/components/molecules/Popover/popover.test.tsx +50 -0
- package/src/components/molecules/Popover/popover.tsx +38 -0
- package/src/components/molecules/ProgressSteps/index.ts +1 -0
- package/src/components/molecules/ProgressSteps/progress-steps.stories.tsx +36 -0
- package/src/components/molecules/ProgressSteps/progress-steps.test.tsx +470 -0
- package/src/components/molecules/ProgressSteps/progress-steps.tsx +140 -0
- package/src/components/molecules/RadioGroup/index.ts +1 -0
- package/src/components/molecules/RadioGroup/radio-group.stories.tsx +42 -0
- package/src/components/molecules/RadioGroup/radio-group.test.tsx +22 -0
- package/src/components/molecules/RadioGroup/radio-group.tsx +55 -0
- package/src/components/molecules/Resizable/index.ts +1 -0
- package/src/components/molecules/Resizable/resizable.stories.tsx +52 -0
- package/src/components/molecules/Resizable/resizable.test.tsx +22 -0
- package/src/components/molecules/Resizable/resizable.tsx +55 -0
- package/src/components/molecules/ScrollArea/index.ts +1 -0
- package/src/components/molecules/ScrollArea/scroll-area.stories.tsx +93 -0
- package/src/components/molecules/ScrollArea/scroll-area.test.tsx +28 -0
- package/src/components/molecules/ScrollArea/scroll-area.tsx +57 -0
- package/src/components/molecules/Select/index.ts +1 -0
- package/src/components/molecules/Select/select.stories.tsx +63 -0
- package/src/components/molecules/Select/select.test.tsx +80 -0
- package/src/components/molecules/Select/select.tsx +172 -0
- package/src/components/molecules/Sheet/index.ts +1 -0
- package/src/components/molecules/Sheet/sheet.stories.tsx +141 -0
- package/src/components/molecules/Sheet/sheet.test.tsx +70 -0
- package/src/components/molecules/Sheet/sheet.tsx +133 -0
- package/src/components/molecules/Tabs/index.ts +1 -0
- package/src/components/molecules/Tabs/tabs.stories.tsx +222 -0
- package/src/components/molecules/Tabs/tabs.test.tsx +113 -0
- package/src/components/molecules/Tabs/tabs.tsx +102 -0
- package/src/components/molecules/ToggleGroup/index.ts +1 -0
- package/src/components/molecules/ToggleGroup/toggle-group.stories.tsx +117 -0
- package/src/components/molecules/ToggleGroup/toggle-group.test.tsx +100 -0
- package/src/components/molecules/ToggleGroup/toggle-group.tsx +70 -0
- package/src/components/molecules/Tooltip/index.ts +1 -0
- package/src/components/molecules/Tooltip/tooltip.stories.tsx +133 -0
- package/src/components/molecules/Tooltip/tooltip.test.tsx +58 -0
- package/src/components/molecules/Tooltip/tooltip.tsx +58 -0
- package/src/components/molecules/index.ts +33 -0
- package/src/components/organisms/Carousel/carousel.stories.tsx +94 -0
- package/src/components/organisms/Carousel/carousel.test.tsx +24 -0
- package/src/components/organisms/Carousel/carousel.tsx +383 -0
- package/src/components/organisms/Carousel/index.ts +1 -0
- package/src/components/organisms/Chart/chart.stories.tsx +102 -0
- package/src/components/organisms/Chart/chart.test.tsx +105 -0
- package/src/components/organisms/Chart/chart.tsx +294 -0
- package/src/components/organisms/Chart/index.ts +1 -0
- package/src/components/organisms/FileUpload/FilePreview/file-preview.tsx +55 -0
- package/src/components/organisms/FileUpload/FilePreview/index.ts +1 -0
- package/src/components/organisms/FileUpload/file-upload.stories.tsx +20 -0
- package/src/components/organisms/FileUpload/file-upload.test.tsx +59 -0
- package/src/components/organisms/FileUpload/file-upload.tsx +175 -0
- package/src/components/organisms/FileUpload/file.d.ts +21 -0
- package/src/components/organisms/FileUpload/index.ts +1 -0
- package/src/components/organisms/Form/form.stories.tsx +155 -0
- package/src/components/organisms/Form/form.test.tsx +49 -0
- package/src/components/organisms/Form/form.tsx +133 -0
- package/src/components/organisms/Form/index.ts +1 -0
- package/src/components/organisms/Sidebar/index.ts +1 -0
- package/src/components/organisms/Sidebar/sidebar.stories.tsx +86 -0
- package/src/components/organisms/Sidebar/sidebar.test.tsx +101 -0
- package/src/components/organisms/Sidebar/sidebar.tsx +666 -0
- package/src/components/organisms/Table/index.ts +1 -0
- package/src/components/organisms/Table/table.stories.tsx +86 -0
- package/src/components/organisms/Table/table.test.tsx +42 -0
- package/src/components/organisms/Table/table.tsx +120 -0
- package/src/components/organisms/index.ts +6 -0
- package/src/constants/brazilian-states.ts +29 -0
- package/src/constants/index.ts +1 -0
- package/src/hooks/index.ts +10 -0
- package/src/hooks/useEditorActiveMarks.ts +63 -0
- package/src/hooks/useForm.ts +8 -0
- package/src/hooks/useFormContext.ts +7 -0
- package/src/hooks/useFormField.ts +41 -0
- package/src/hooks/useMobile.ts +61 -0
- package/src/hooks/useOnToggle.ts +28 -0
- package/src/hooks/usePDFNavigation.ts +41 -0
- package/src/hooks/usePDFZoom.ts +35 -0
- package/src/hooks/useSidebar.ts +23 -0
- package/src/hooks/useToast.tsx +63 -0
- package/src/index.css +73 -0
- package/src/main.ts +6 -0
- package/src/theme.css +388 -0
- package/src/utils/api/api.test.ts +64 -0
- package/src/utils/api/api.ts +34 -0
- package/src/utils/api/index.ts +1 -0
- package/src/utils/array/array.test.ts +160 -0
- package/src/utils/array/array.ts +43 -0
- package/src/utils/array/index.ts +1 -0
- package/src/utils/clipboard/clipboard.test.ts +218 -0
- package/src/utils/clipboard/clipboard.ts +40 -0
- package/src/utils/clipboard/index.ts +1 -0
- package/src/utils/cn/cn.test.ts +43 -0
- package/src/utils/cn/cn.ts +6 -0
- package/src/utils/cn/index.ts +1 -0
- package/src/utils/color-utils/color-utils.test.ts +46 -0
- package/src/utils/color-utils/color-utils.ts +97 -0
- package/src/utils/color-utils/index.ts +1 -0
- package/src/utils/countries/countries.ts +69 -0
- package/src/utils/countries/index.ts +1 -0
- package/src/utils/currency/currency.test.ts +114 -0
- package/src/utils/currency/currency.ts +134 -0
- package/src/utils/currency/index.ts +1 -0
- package/src/utils/date/date.test.ts +167 -0
- package/src/utils/date/date.ts +83 -0
- package/src/utils/date/index.ts +1 -0
- package/src/utils/file/file.ts +45 -0
- package/src/utils/file/index.ts +1 -0
- package/src/utils/get-initials/get-initials.test.ts +40 -0
- package/src/utils/get-initials/get-initials.ts +13 -0
- package/src/utils/get-initials/index.ts +1 -0
- package/src/utils/index.ts +14 -0
- package/src/utils/masks/index.ts +1 -0
- package/src/utils/masks/masks.ts +180 -0
- package/src/utils/render-highlighted-text/index.ts +1 -0
- package/src/utils/render-highlighted-text/render-highlighted-text.tsx +25 -0
- package/src/utils/string/index.ts +1 -0
- package/src/utils/string/string.test.ts +171 -0
- package/src/utils/string/string.ts +155 -0
- package/src/utils/video/index.ts +1 -0
- package/src/utils/video/video.ts +9 -0
- package/src/vite-env.d.ts +3 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import { cva, type VariantProps } from 'class-variance-authority';
|
|
4
|
+
|
|
5
|
+
import { cn } from '@/utils';
|
|
6
|
+
|
|
7
|
+
const typographyVariants = cva('', {
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
h1: 'text-5xl font-semibold leading-tight tracking-normal',
|
|
11
|
+
h2: 'text-4xl font-semibold leading-[1.3] tracking-normal',
|
|
12
|
+
h3: 'text-3xl font-semibold leading-snug tracking-normal',
|
|
13
|
+
h4: 'text-2xl font-semibold leading-snug tracking-normal',
|
|
14
|
+
h5: 'text-xl font-semibold leading-snug tracking-normal',
|
|
15
|
+
h6: 'text-base font-semibold leading-relaxed tracking-normal',
|
|
16
|
+
lead: 'text-xl font-normal leading-relaxed',
|
|
17
|
+
paragraph: 'text-base font-light leading-relaxed',
|
|
18
|
+
small: 'text-sm font-light leading-normal',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
defaultVariants: {
|
|
22
|
+
variant: 'paragraph',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
type TypographyProps = {
|
|
27
|
+
as?: React.ElementType;
|
|
28
|
+
className?: string;
|
|
29
|
+
children: React.ReactNode;
|
|
30
|
+
} & VariantProps<typeof typographyVariants> &
|
|
31
|
+
Omit<React.ComponentPropsWithoutRef<'span'>, 'as'>;
|
|
32
|
+
|
|
33
|
+
function getDefaultTag (
|
|
34
|
+
variant: TypographyProps['variant']
|
|
35
|
+
): keyof React.JSX.IntrinsicElements {
|
|
36
|
+
if (!variant) return 'span';
|
|
37
|
+
|
|
38
|
+
if (variant.startsWith('h')) return variant as keyof React.JSX.IntrinsicElements;
|
|
39
|
+
|
|
40
|
+
if (variant === 'lead' || variant === 'paragraph') return 'p';
|
|
41
|
+
|
|
42
|
+
return 'span';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const Typography: React.FC<TypographyProps> = ({
|
|
46
|
+
as,
|
|
47
|
+
variant = 'paragraph',
|
|
48
|
+
className,
|
|
49
|
+
children,
|
|
50
|
+
...rest
|
|
51
|
+
}) => {
|
|
52
|
+
const Tag = as ?? getDefaultTag(variant);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<Tag
|
|
56
|
+
data-slot="typography"
|
|
57
|
+
className={cn(typographyVariants({ variant }), className)}
|
|
58
|
+
{...rest}
|
|
59
|
+
>
|
|
60
|
+
{children}
|
|
61
|
+
</Tag>
|
|
62
|
+
);
|
|
63
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './upload-image-field';
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { UploadImageField } from './upload-image-field';
|
|
4
|
+
|
|
5
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof UploadImageField> = {
|
|
8
|
+
title: 'Components/Data Entry/UploadImageField',
|
|
9
|
+
component: UploadImageField,
|
|
10
|
+
tags: [ 'autodocs' ],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: 'UploadImageField is a specialized image upload component with drag & drop support, full-container image preview, validation and loading states. When an image is selected, it fills the entire container and shows a hover button for replacement.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
argTypes: {
|
|
19
|
+
buttonText: { control: 'text' },
|
|
20
|
+
maxSize: { control: 'number' },
|
|
21
|
+
loading: { control: 'boolean' },
|
|
22
|
+
disabled: { control: 'boolean' },
|
|
23
|
+
error: { control: 'text' },
|
|
24
|
+
accept: { control: 'text' },
|
|
25
|
+
helperText: { control: 'text' },
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export default meta;
|
|
30
|
+
|
|
31
|
+
type Story = StoryObj<typeof meta>;
|
|
32
|
+
|
|
33
|
+
export const CustomButtonText: Story = {
|
|
34
|
+
args: {
|
|
35
|
+
buttonText: 'Choose image',
|
|
36
|
+
maxSize: 10,
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const Default: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
buttonText: 'Select image',
|
|
43
|
+
maxSize: 5,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const Disabled: Story = {
|
|
48
|
+
args: {
|
|
49
|
+
buttonText: 'Select image',
|
|
50
|
+
maxSize: 5,
|
|
51
|
+
disabled: true,
|
|
52
|
+
helperText: 'This field is temporarily unavailable',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const DragAndDropExample: Story = {
|
|
57
|
+
render: () => (
|
|
58
|
+
<div className="space-y-4">
|
|
59
|
+
<div className="h-64">
|
|
60
|
+
<UploadImageField
|
|
61
|
+
buttonText="Select image"
|
|
62
|
+
maxSize={5}
|
|
63
|
+
helperText="Drag and drop an image here or click to select"
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div className="text-sm text-gray-500">
|
|
68
|
+
<p>• Drag images directly to the area</p>
|
|
69
|
+
<p>• Click the button to open the file picker</p>
|
|
70
|
+
<p>• Accepted formats: JPG, PNG, GIF, WebP</p>
|
|
71
|
+
<p>• Maximum size: 5MB</p>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
),
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const HoverPreviewExample: Story = {
|
|
78
|
+
render: () => (
|
|
79
|
+
<div className="space-y-4">
|
|
80
|
+
<div className="h-64">
|
|
81
|
+
<UploadImageField
|
|
82
|
+
buttonText="Choose image"
|
|
83
|
+
maxSize={5}
|
|
84
|
+
helperText="Select an image to see full-screen preview"
|
|
85
|
+
/>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div className="text-sm text-gray-500">
|
|
89
|
+
<p>• Select an image to see full-screen preview</p>
|
|
90
|
+
<p>• Hover over the image to see the "Choose image" button</p>
|
|
91
|
+
<p>• Image gets opacity and blur on hover</p>
|
|
92
|
+
<p>• Click the button to select a new image</p>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
),
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export const InteractiveExample: Story = {
|
|
99
|
+
render: () => <InteractiveExampleComponent />,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function InteractiveExampleComponent() {
|
|
103
|
+
const [ file, setFile ] = useState<File | null>(null);
|
|
104
|
+
const [ error, setError ] = useState<string>('');
|
|
105
|
+
const [ loading, setLoading ] = useState(false);
|
|
106
|
+
|
|
107
|
+
const handleFileSelect = (selectedFile: File | null) => {
|
|
108
|
+
setFile(selectedFile);
|
|
109
|
+
setError('');
|
|
110
|
+
|
|
111
|
+
if (selectedFile) {
|
|
112
|
+
setLoading(true);
|
|
113
|
+
setTimeout(() => {
|
|
114
|
+
setLoading(false);
|
|
115
|
+
}, 2000);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleError = (errorMessage: string) => {
|
|
120
|
+
setError(errorMessage);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<div className="space-y-4">
|
|
125
|
+
<UploadImageField
|
|
126
|
+
buttonText="Choose image"
|
|
127
|
+
maxSize={5}
|
|
128
|
+
onFileSelect={handleFileSelect}
|
|
129
|
+
onError={handleError}
|
|
130
|
+
loading={loading}
|
|
131
|
+
error={error}
|
|
132
|
+
helperText="Drag and drop or click to select an image"
|
|
133
|
+
/>
|
|
134
|
+
|
|
135
|
+
{file && (
|
|
136
|
+
<div className="p-4 bg-gray-50 rounded-lg">
|
|
137
|
+
<h4 className="font-medium text-gray-900 mb-2">Selected file:</h4>
|
|
138
|
+
<p className="text-sm text-gray-600">
|
|
139
|
+
Name: {file.name}<br />
|
|
140
|
+
Size: {(file.size / 1024 / 1024).toFixed(2)} MB<br />
|
|
141
|
+
Type: {file.type}
|
|
142
|
+
</p>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export const Loading: Story = {
|
|
150
|
+
args: {
|
|
151
|
+
buttonText: 'Select image',
|
|
152
|
+
maxSize: 5,
|
|
153
|
+
loading: true,
|
|
154
|
+
helperText: 'Please wait while the image is being processed',
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
export const MultipleVariants: Story = {
|
|
159
|
+
render: () => (
|
|
160
|
+
<div className="space-y-6">
|
|
161
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
162
|
+
<div className="h-48">
|
|
163
|
+
<UploadImageField
|
|
164
|
+
buttonText="Select image"
|
|
165
|
+
maxSize={5}
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div className="h-48">
|
|
170
|
+
<UploadImageField
|
|
171
|
+
buttonText="Choose image"
|
|
172
|
+
maxSize={10}
|
|
173
|
+
helperText="Larger size allowed"
|
|
174
|
+
/>
|
|
175
|
+
</div>
|
|
176
|
+
|
|
177
|
+
<div className="h-48">
|
|
178
|
+
<UploadImageField
|
|
179
|
+
buttonText="Select image"
|
|
180
|
+
maxSize={1}
|
|
181
|
+
error="File too large"
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div className="h-48">
|
|
186
|
+
<UploadImageField
|
|
187
|
+
buttonText="Select image"
|
|
188
|
+
maxSize={5}
|
|
189
|
+
loading={true}
|
|
190
|
+
/>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
function WithUploadFunctionComponent() {
|
|
198
|
+
const [ uploadedUrl, setUploadedUrl ] = useState<string>('');
|
|
199
|
+
|
|
200
|
+
const mockUploadFunction = async (file: File): Promise<string> => {
|
|
201
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
202
|
+
return `https://example.com/uploads/${file.name}`;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const handleUploadComplete = (url: string) => {
|
|
206
|
+
setUploadedUrl(url);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<div className="space-y-4">
|
|
211
|
+
<UploadImageField
|
|
212
|
+
buttonText="Upload image"
|
|
213
|
+
maxSize={5}
|
|
214
|
+
uploadFunction={mockUploadFunction}
|
|
215
|
+
onUploadComplete={handleUploadComplete}
|
|
216
|
+
helperText="Image will be uploaded automatically"
|
|
217
|
+
/>
|
|
218
|
+
|
|
219
|
+
{uploadedUrl && (
|
|
220
|
+
<div className="p-4 bg-green-50 border border-green-200 rounded-lg">
|
|
221
|
+
<h4 className="font-medium text-green-900 mb-2">Upload completed!</h4>
|
|
222
|
+
<p className="text-sm text-green-700">
|
|
223
|
+
URL: {uploadedUrl}
|
|
224
|
+
</p>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const WithError: Story = {
|
|
232
|
+
args: {
|
|
233
|
+
buttonText: 'Select image',
|
|
234
|
+
maxSize: 5,
|
|
235
|
+
error: 'File too large. Maximum size: 5MB',
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const WithHelperText: Story = {
|
|
240
|
+
args: {
|
|
241
|
+
buttonText: 'Select image',
|
|
242
|
+
maxSize: 5,
|
|
243
|
+
helperText: 'We recommend images with 16:9 aspect ratio',
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const WithUploadFunction: Story = {
|
|
248
|
+
render: () => <WithUploadFunctionComponent />,
|
|
249
|
+
};
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import { UploadImageField } from './upload-image-field';
|
|
6
|
+
|
|
7
|
+
// Mock URL.createObjectURL and URL.revokeObjectURL
|
|
8
|
+
const mockCreateObjectURL = vi.fn(() => 'mock-url');
|
|
9
|
+
const mockRevokeObjectURL = vi.fn();
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(global, 'URL', {
|
|
12
|
+
value: {
|
|
13
|
+
createObjectURL: mockCreateObjectURL,
|
|
14
|
+
revokeObjectURL: mockRevokeObjectURL,
|
|
15
|
+
},
|
|
16
|
+
writable: true,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('UploadImageField', () => {
|
|
20
|
+
const mockFile = new File([ 'test content' ], 'test.jpg', { type: 'image/jpeg' });
|
|
21
|
+
const mockLargeFile = new File([ 'x'.repeat(6 * 1024 * 1024) ], 'large.jpg', { type: 'image/jpeg' });
|
|
22
|
+
const mockInvalidFile = new File([ 'test content' ], 'test.txt', { type: 'text/plain' });
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
mockCreateObjectURL.mockReturnValue('mock-url');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders with default props', () => {
|
|
30
|
+
render(<UploadImageField />);
|
|
31
|
+
|
|
32
|
+
expect(screen.getByText('Select image')).toBeInTheDocument();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('renders with custom button text', () => {
|
|
36
|
+
render(<UploadImageField buttonText="Custom Button" />);
|
|
37
|
+
|
|
38
|
+
expect(screen.getByText('Custom Button')).toBeInTheDocument();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('handles file selection via click', async () => {
|
|
42
|
+
const user = userEvent.setup();
|
|
43
|
+
const onFileSelect = vi.fn();
|
|
44
|
+
|
|
45
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
46
|
+
|
|
47
|
+
const button = screen.getByText('Select image');
|
|
48
|
+
await user.click(button);
|
|
49
|
+
|
|
50
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
51
|
+
expect(input).toHaveAttribute('type', 'file');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('handles file selection via input change', async () => {
|
|
55
|
+
const onFileSelect = vi.fn();
|
|
56
|
+
|
|
57
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
58
|
+
|
|
59
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
60
|
+
|
|
61
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
62
|
+
|
|
63
|
+
await waitFor(() => {
|
|
64
|
+
expect(onFileSelect).toHaveBeenCalledWith(mockFile, undefined);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('validates file size when maxSize is provided', async () => {
|
|
69
|
+
const onError = vi.fn();
|
|
70
|
+
|
|
71
|
+
render(<UploadImageField maxSize={5} onError={onError} />);
|
|
72
|
+
|
|
73
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
74
|
+
|
|
75
|
+
fireEvent.change(input, { target: { files: [ mockLargeFile ] } });
|
|
76
|
+
|
|
77
|
+
await waitFor(() => {
|
|
78
|
+
expect(onError).toHaveBeenCalledWith('Arquivo muito grande. Tamanho máximo: 5MB');
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('validates file type when accept is provided', async () => {
|
|
83
|
+
const onError = vi.fn();
|
|
84
|
+
|
|
85
|
+
render(<UploadImageField accept="image/*" onError={onError} />);
|
|
86
|
+
|
|
87
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
88
|
+
|
|
89
|
+
fireEvent.change(input, { target: { files: [ mockInvalidFile ] } });
|
|
90
|
+
|
|
91
|
+
await waitFor(() => {
|
|
92
|
+
expect(onError).toHaveBeenCalledWith('Tipo de arquivo não suportado. Tipos aceitos: image/*');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('shows error message when error prop is provided', () => {
|
|
97
|
+
const errorMessage = 'Test error message';
|
|
98
|
+
render(<UploadImageField error={errorMessage} />);
|
|
99
|
+
|
|
100
|
+
expect(screen.getByText(errorMessage)).toBeInTheDocument();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('shows loading state when loading prop is true', () => {
|
|
104
|
+
render(<UploadImageField loading={true} />);
|
|
105
|
+
|
|
106
|
+
expect(screen.getByText('Select image')).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('is disabled when disabled prop is true', () => {
|
|
110
|
+
render(<UploadImageField disabled={true} />);
|
|
111
|
+
|
|
112
|
+
const button = screen.getByText('Select image');
|
|
113
|
+
expect(button).toBeDisabled();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('handles drag and drop events', async () => {
|
|
117
|
+
const onFileSelect = vi.fn();
|
|
118
|
+
|
|
119
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
120
|
+
|
|
121
|
+
const container = screen.getByText('Select image').closest('div')?.parentElement;
|
|
122
|
+
|
|
123
|
+
fireEvent.dragOver(container!);
|
|
124
|
+
fireEvent.drop(container!, { dataTransfer: { files: [ mockFile ] } });
|
|
125
|
+
|
|
126
|
+
await waitFor(() => {
|
|
127
|
+
expect(onFileSelect).toHaveBeenCalledWith(mockFile, undefined);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('shows image preview when image is selected', async () => {
|
|
132
|
+
const onFileSelect = vi.fn();
|
|
133
|
+
|
|
134
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
135
|
+
|
|
136
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
137
|
+
|
|
138
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
const image = screen.getByAltText('Image preview');
|
|
142
|
+
expect(image).toBeInTheDocument();
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('shows uploaded file when value is URL string', () => {
|
|
147
|
+
render(<UploadImageField value="https://example.com/image.jpg" />);
|
|
148
|
+
|
|
149
|
+
const image = screen.getByAltText('Image preview');
|
|
150
|
+
expect(image).toHaveAttribute('src', 'https://example.com/image.jpg');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('calls onChange when file is selected', async () => {
|
|
154
|
+
const onChange = vi.fn();
|
|
155
|
+
|
|
156
|
+
render(<UploadImageField onChange={onChange} />);
|
|
157
|
+
|
|
158
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
159
|
+
|
|
160
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
161
|
+
|
|
162
|
+
await waitFor(() => {
|
|
163
|
+
expect(onChange).toHaveBeenCalledWith(mockFile);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('calls onUploadComplete when upload is successful', async () => {
|
|
168
|
+
const onUploadComplete = vi.fn();
|
|
169
|
+
const mockUploadFunction = vi.fn().mockResolvedValue('https://example.com/uploaded.jpg');
|
|
170
|
+
|
|
171
|
+
render(
|
|
172
|
+
<UploadImageField
|
|
173
|
+
uploadFunction={mockUploadFunction}
|
|
174
|
+
onUploadComplete={onUploadComplete}
|
|
175
|
+
/>
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
179
|
+
|
|
180
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
181
|
+
|
|
182
|
+
await waitFor(() => {
|
|
183
|
+
expect(mockUploadFunction).toHaveBeenCalledWith(mockFile);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
expect(onUploadComplete).toHaveBeenCalledWith('https://example.com/uploaded.jpg');
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('calls onError when upload fails', async () => {
|
|
192
|
+
const onError = vi.fn();
|
|
193
|
+
const mockUploadFunction = vi.fn().mockRejectedValue(new Error('Upload failed'));
|
|
194
|
+
|
|
195
|
+
render(
|
|
196
|
+
<UploadImageField
|
|
197
|
+
uploadFunction={mockUploadFunction}
|
|
198
|
+
onError={onError}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
203
|
+
|
|
204
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
205
|
+
|
|
206
|
+
await waitFor(() => {
|
|
207
|
+
expect(onError).toHaveBeenCalledWith('Upload failed');
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('shows helper text when provided', () => {
|
|
212
|
+
const helperText = 'We recommend images with 16:9 aspect ratio';
|
|
213
|
+
render(<UploadImageField helperText={helperText} />);
|
|
214
|
+
|
|
215
|
+
expect(screen.getByText(helperText)).toBeInTheDocument();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('does not show helper text when error is present', () => {
|
|
219
|
+
const helperText = 'We recommend images with 16:9 aspect ratio';
|
|
220
|
+
render(<UploadImageField helperText={helperText} error="Test error" />);
|
|
221
|
+
|
|
222
|
+
expect(screen.queryByText(helperText)).not.toBeInTheDocument();
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('handles file deselection', async () => {
|
|
226
|
+
const onFileSelect = vi.fn();
|
|
227
|
+
|
|
228
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
229
|
+
|
|
230
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
231
|
+
|
|
232
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
233
|
+
|
|
234
|
+
await waitFor(() => {
|
|
235
|
+
expect(onFileSelect).toHaveBeenCalledWith(mockFile, undefined);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
fireEvent.change(input, { target: { files: [] } });
|
|
239
|
+
|
|
240
|
+
await waitFor(() => {
|
|
241
|
+
expect(onFileSelect).toHaveBeenCalledWith(null);
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('prevents file selection when disabled', async () => {
|
|
246
|
+
const onFileSelect = vi.fn();
|
|
247
|
+
|
|
248
|
+
render(<UploadImageField disabled={true} onFileSelect={onFileSelect} />);
|
|
249
|
+
|
|
250
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
251
|
+
|
|
252
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
253
|
+
|
|
254
|
+
expect(onFileSelect).not.toHaveBeenCalled();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('prevents file selection when loading', async () => {
|
|
258
|
+
const onFileSelect = vi.fn();
|
|
259
|
+
|
|
260
|
+
render(<UploadImageField loading={true} onFileSelect={onFileSelect} />);
|
|
261
|
+
|
|
262
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
263
|
+
|
|
264
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
265
|
+
|
|
266
|
+
expect(onFileSelect).not.toHaveBeenCalled();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('shows full container image preview when image is selected', async () => {
|
|
270
|
+
const onFileSelect = vi.fn();
|
|
271
|
+
|
|
272
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
273
|
+
|
|
274
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
275
|
+
|
|
276
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
277
|
+
|
|
278
|
+
await waitFor(() => {
|
|
279
|
+
const image = screen.getByAltText('Image preview');
|
|
280
|
+
expect(image).toHaveClass('w-full', 'h-full', 'object-cover');
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('shows hover button when image is previewed', async () => {
|
|
285
|
+
const onFileSelect = vi.fn();
|
|
286
|
+
|
|
287
|
+
render(<UploadImageField onFileSelect={onFileSelect} />);
|
|
288
|
+
|
|
289
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
290
|
+
|
|
291
|
+
fireEvent.change(input, { target: { files: [ mockFile ] } });
|
|
292
|
+
|
|
293
|
+
await waitFor(() => {
|
|
294
|
+
const container = screen.getByAltText('Image preview').closest('div');
|
|
295
|
+
fireEvent.mouseEnter(container!);
|
|
296
|
+
|
|
297
|
+
expect(screen.getByText('Select image')).toBeInTheDocument();
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
it('applies drag over styles', () => {
|
|
302
|
+
render(<UploadImageField />);
|
|
303
|
+
|
|
304
|
+
const container = screen.getByText('Select image').closest('div')?.parentElement;
|
|
305
|
+
|
|
306
|
+
fireEvent.dragOver(container!);
|
|
307
|
+
|
|
308
|
+
expect(container).toHaveClass('border-primary', 'bg-brand-50');
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
it('applies error styles when error is present', () => {
|
|
312
|
+
render(<UploadImageField error="Test error" />);
|
|
313
|
+
|
|
314
|
+
const container = screen.getByText('Select image').closest('div')?.parentElement;
|
|
315
|
+
|
|
316
|
+
expect(container).toHaveClass('border-error-500');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('applies disabled styles when disabled', () => {
|
|
320
|
+
render(<UploadImageField disabled={true} />);
|
|
321
|
+
|
|
322
|
+
const container = screen.getByText('Select image').closest('div')?.parentElement;
|
|
323
|
+
|
|
324
|
+
expect(container).toHaveClass('opacity-50', 'cursor-not-allowed');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('has default accept prop set to image/*', () => {
|
|
328
|
+
render(<UploadImageField />);
|
|
329
|
+
|
|
330
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
331
|
+
|
|
332
|
+
expect(input).toHaveAttribute('accept', 'image/*');
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('has default maxSize prop set to 5', async () => {
|
|
336
|
+
const onError = vi.fn();
|
|
337
|
+
|
|
338
|
+
render(<UploadImageField onError={onError} />);
|
|
339
|
+
|
|
340
|
+
const input = screen.getByDisplayValue('') as HTMLInputElement;
|
|
341
|
+
|
|
342
|
+
fireEvent.change(input, { target: { files: [ mockLargeFile ] } });
|
|
343
|
+
|
|
344
|
+
await waitFor(() => {
|
|
345
|
+
expect(onError).toHaveBeenCalledWith('Arquivo muito grande. Tamanho máximo: 5MB');
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
});
|