@moontra/moonui-pro 2.20.1 → 2.20.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/dist/index.d.ts +691 -261
- package/dist/index.mjs +7418 -4934
- package/package.json +4 -3
- package/scripts/postbuild.js +27 -0
- package/src/components/advanced-chart/index.tsx +5 -1
- package/src/components/advanced-forms/index.tsx +175 -16
- package/src/components/calendar/event-dialog.tsx +18 -13
- package/src/components/calendar/index.tsx +197 -50
- package/src/components/dashboard/dashboard-grid.tsx +21 -3
- package/src/components/dashboard/types.ts +3 -0
- package/src/components/dashboard/widgets/activity-feed.tsx +6 -1
- package/src/components/dashboard/widgets/comparison-widget.tsx +177 -0
- package/src/components/dashboard/widgets/index.ts +5 -0
- package/src/components/dashboard/widgets/metric-card.tsx +21 -1
- package/src/components/dashboard/widgets/progress-widget.tsx +113 -0
- package/src/components/error-boundary/index.tsx +160 -37
- package/src/components/form-wizard/form-wizard-context.tsx +54 -26
- package/src/components/form-wizard/form-wizard-progress.tsx +33 -2
- package/src/components/form-wizard/types.ts +2 -1
- package/src/components/github-stars/hooks.ts +1 -0
- package/src/components/github-stars/variants.tsx +3 -1
- package/src/components/health-check/index.tsx +14 -14
- package/src/components/hover-card-3d/index.tsx +2 -3
- package/src/components/index.ts +5 -3
- package/src/components/kanban/kanban.tsx +23 -18
- package/src/components/license-error/index.tsx +2 -0
- package/src/components/magnetic-button/index.tsx +56 -7
- package/src/components/memory-efficient-data/index.tsx +117 -115
- package/src/components/navbar/index.tsx +781 -0
- package/src/components/performance-debugger/index.tsx +62 -38
- package/src/components/performance-monitor/index.tsx +47 -33
- package/src/components/phone-number-input/index.tsx +32 -27
- package/src/components/phone-number-input/phone-number-input-simple.tsx +167 -0
- package/src/components/rich-text-editor/index.tsx +26 -28
- package/src/components/rich-text-editor/slash-commands-extension.ts +15 -5
- package/src/components/sidebar/index.tsx +32 -13
- package/src/components/timeline/index.tsx +84 -49
- package/src/components/ui/accordion.tsx +550 -42
- package/src/components/ui/avatar.tsx +2 -0
- package/src/components/ui/badge.tsx +2 -0
- package/src/components/ui/breadcrumb.tsx +2 -0
- package/src/components/ui/button.tsx +39 -33
- package/src/components/ui/card.tsx +2 -0
- package/src/components/ui/collapsible.tsx +546 -50
- package/src/components/ui/command.tsx +790 -67
- package/src/components/ui/dialog.tsx +510 -92
- package/src/components/ui/dropdown-menu.tsx +540 -52
- package/src/components/ui/index.ts +37 -5
- package/src/components/ui/input.tsx +2 -0
- package/src/components/ui/magnetic-button.tsx +1 -1
- package/src/components/ui/media-gallery.tsx +1 -2
- package/src/components/ui/navigation-menu.tsx +130 -0
- package/src/components/ui/pagination.tsx +2 -0
- package/src/components/ui/select.tsx +6 -2
- package/src/components/ui/spotlight-card.tsx +1 -1
- package/src/components/ui/table.tsx +2 -0
- package/src/components/ui/tabs-pro.tsx +542 -0
- package/src/components/ui/tabs.tsx +23 -167
- package/src/components/ui/toggle.tsx +12 -12
- package/src/index.ts +11 -3
- package/src/styles/index.css +596 -0
- package/src/use-performance-optimizer.ts +1 -1
- package/src/utils/chart-helpers.ts +1 -1
- package/src/__tests__/use-intersection-observer.test.tsx +0 -216
- package/src/__tests__/use-local-storage.test.tsx +0 -174
- package/src/__tests__/use-pro-access.test.tsx +0 -183
- package/src/components/advanced-chart/advanced-chart.test.tsx +0 -281
- package/src/components/data-table/data-table.test.tsx +0 -187
- package/src/components/enhanced/badge.tsx +0 -191
- package/src/components/enhanced/button.tsx +0 -362
- package/src/components/enhanced/card.tsx +0 -266
- package/src/components/enhanced/dialog.tsx +0 -246
- package/src/components/enhanced/index.ts +0 -4
- package/src/components/file-upload/file-upload.test.tsx +0 -243
- package/src/components/rich-text-editor/index-old-backup.tsx +0 -437
- package/src/types/moonui.d.ts +0 -22
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
"use client"
|
|
2
|
-
|
|
3
|
-
import * as React from "react"
|
|
4
|
-
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
|
5
|
-
import { motion, AnimatePresence } from "framer-motion"
|
|
6
|
-
import { X } from "lucide-react"
|
|
7
|
-
import { cn } from "../../lib/utils"
|
|
8
|
-
|
|
9
|
-
const Dialog = DialogPrimitive.Root
|
|
10
|
-
const DialogTrigger = DialogPrimitive.Trigger
|
|
11
|
-
const DialogPortal = DialogPrimitive.Portal
|
|
12
|
-
const DialogClose = DialogPrimitive.Close
|
|
13
|
-
|
|
14
|
-
interface DialogProOverlayProps
|
|
15
|
-
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> {
|
|
16
|
-
blur?: "none" | "sm" | "md" | "lg" | "xl"
|
|
17
|
-
variant?: "default" | "dark" | "light" | "gradient"
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const DialogProOverlay = React.forwardRef<
|
|
21
|
-
React.ElementRef<typeof DialogPrimitive.Overlay>,
|
|
22
|
-
DialogProOverlayProps
|
|
23
|
-
>(({ className, blur = "md", variant = "default", ...props }, ref) => {
|
|
24
|
-
const blurClasses = {
|
|
25
|
-
none: "",
|
|
26
|
-
sm: "backdrop-blur-sm",
|
|
27
|
-
md: "backdrop-blur-md",
|
|
28
|
-
lg: "backdrop-blur-lg",
|
|
29
|
-
xl: "backdrop-blur-xl",
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const variantClasses = {
|
|
33
|
-
default: "bg-black/80",
|
|
34
|
-
dark: "bg-black/90",
|
|
35
|
-
light: "bg-white/80",
|
|
36
|
-
gradient: "bg-gradient-to-br from-black/80 via-gray-900/80 to-black/80",
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return (
|
|
40
|
-
<DialogPrimitive.Overlay
|
|
41
|
-
ref={ref}
|
|
42
|
-
className={cn(
|
|
43
|
-
"fixed inset-0 z-50",
|
|
44
|
-
blurClasses[blur],
|
|
45
|
-
variantClasses[variant],
|
|
46
|
-
className
|
|
47
|
-
)}
|
|
48
|
-
{...props}
|
|
49
|
-
/>
|
|
50
|
-
)
|
|
51
|
-
})
|
|
52
|
-
DialogProOverlay.displayName = DialogPrimitive.Overlay.displayName
|
|
53
|
-
|
|
54
|
-
interface DialogProContentProps
|
|
55
|
-
extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> {
|
|
56
|
-
animation?: "fade" | "scale" | "slide" | "rotate" | "bounce"
|
|
57
|
-
animationDuration?: number
|
|
58
|
-
overlay?: boolean
|
|
59
|
-
overlayProps?: DialogProOverlayProps
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const DialogProContent = React.forwardRef<
|
|
63
|
-
React.ElementRef<typeof DialogPrimitive.Content>,
|
|
64
|
-
DialogProContentProps
|
|
65
|
-
>(({
|
|
66
|
-
className,
|
|
67
|
-
children,
|
|
68
|
-
animation = "scale",
|
|
69
|
-
animationDuration = 0.3,
|
|
70
|
-
overlay = true,
|
|
71
|
-
overlayProps = {},
|
|
72
|
-
...props
|
|
73
|
-
}, ref) => {
|
|
74
|
-
const animationVariants = {
|
|
75
|
-
fade: {
|
|
76
|
-
initial: { opacity: 0 },
|
|
77
|
-
animate: { opacity: 1 },
|
|
78
|
-
exit: { opacity: 0 }
|
|
79
|
-
},
|
|
80
|
-
scale: {
|
|
81
|
-
initial: { opacity: 0, scale: 0.9, y: 20 },
|
|
82
|
-
animate: { opacity: 1, scale: 1, y: 0 },
|
|
83
|
-
exit: { opacity: 0, scale: 0.9, y: 20 }
|
|
84
|
-
},
|
|
85
|
-
slide: {
|
|
86
|
-
initial: { opacity: 0, y: 100 },
|
|
87
|
-
animate: { opacity: 1, y: 0 },
|
|
88
|
-
exit: { opacity: 0, y: 100 }
|
|
89
|
-
},
|
|
90
|
-
rotate: {
|
|
91
|
-
initial: { opacity: 0, scale: 0.9, rotate: -10 },
|
|
92
|
-
animate: { opacity: 1, scale: 1, rotate: 0 },
|
|
93
|
-
exit: { opacity: 0, scale: 0.9, rotate: 10 }
|
|
94
|
-
},
|
|
95
|
-
bounce: {
|
|
96
|
-
initial: { opacity: 0, scale: 0.3, y: -100 },
|
|
97
|
-
animate: {
|
|
98
|
-
opacity: 1,
|
|
99
|
-
scale: 1,
|
|
100
|
-
y: 0,
|
|
101
|
-
transition: {
|
|
102
|
-
type: "spring",
|
|
103
|
-
duration: animationDuration,
|
|
104
|
-
bounce: 0.5
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
exit: { opacity: 0, scale: 0.5, y: -50 }
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return (
|
|
112
|
-
<AnimatePresence>
|
|
113
|
-
<DialogPortal>
|
|
114
|
-
{overlay && (
|
|
115
|
-
<motion.div
|
|
116
|
-
initial={{ opacity: 0 }}
|
|
117
|
-
animate={{ opacity: 1 }}
|
|
118
|
-
exit={{ opacity: 0 }}
|
|
119
|
-
transition={{ duration: animationDuration * 0.8 }}
|
|
120
|
-
>
|
|
121
|
-
<DialogProOverlay {...overlayProps} />
|
|
122
|
-
</motion.div>
|
|
123
|
-
)}
|
|
124
|
-
<motion.div
|
|
125
|
-
className="fixed inset-0 z-50 flex items-center justify-center p-4"
|
|
126
|
-
initial={{ opacity: 0 }}
|
|
127
|
-
animate={{ opacity: 1 }}
|
|
128
|
-
exit={{ opacity: 0 }}
|
|
129
|
-
>
|
|
130
|
-
<DialogPrimitive.Content
|
|
131
|
-
ref={ref}
|
|
132
|
-
asChild
|
|
133
|
-
className={cn(
|
|
134
|
-
"relative w-full max-w-lg",
|
|
135
|
-
className
|
|
136
|
-
)}
|
|
137
|
-
{...props}
|
|
138
|
-
>
|
|
139
|
-
<motion.div
|
|
140
|
-
variants={animationVariants[animation]}
|
|
141
|
-
initial="initial"
|
|
142
|
-
animate="animate"
|
|
143
|
-
exit="exit"
|
|
144
|
-
transition={{
|
|
145
|
-
duration: animationDuration,
|
|
146
|
-
ease: "easeInOut"
|
|
147
|
-
}}
|
|
148
|
-
className={cn(
|
|
149
|
-
"relative bg-background rounded-lg shadow-2xl",
|
|
150
|
-
"border border-border",
|
|
151
|
-
"w-full max-w-lg",
|
|
152
|
-
"max-h-[90vh] overflow-auto",
|
|
153
|
-
className
|
|
154
|
-
)}
|
|
155
|
-
>
|
|
156
|
-
{/* Glass effect overlay */}
|
|
157
|
-
<div className="absolute inset-0 rounded-lg bg-gradient-to-br from-white/5 to-white/0 pointer-events-none" />
|
|
158
|
-
|
|
159
|
-
{/* Content wrapper with padding */}
|
|
160
|
-
<div className="relative p-6">
|
|
161
|
-
{children}
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
{/* Close button with enhanced styling */}
|
|
165
|
-
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-all hover:opacity-100 hover:rotate-90 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
|
166
|
-
<X className="h-4 w-4" />
|
|
167
|
-
<span className="sr-only">Close</span>
|
|
168
|
-
</DialogPrimitive.Close>
|
|
169
|
-
</motion.div>
|
|
170
|
-
</DialogPrimitive.Content>
|
|
171
|
-
</motion.div>
|
|
172
|
-
</DialogPortal>
|
|
173
|
-
</AnimatePresence>
|
|
174
|
-
)
|
|
175
|
-
})
|
|
176
|
-
DialogProContent.displayName = DialogPrimitive.Content.displayName
|
|
177
|
-
|
|
178
|
-
const DialogProHeader = ({
|
|
179
|
-
className,
|
|
180
|
-
...props
|
|
181
|
-
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
182
|
-
<motion.div
|
|
183
|
-
className={cn(
|
|
184
|
-
"flex flex-col space-y-1.5 text-center sm:text-left",
|
|
185
|
-
className
|
|
186
|
-
)}
|
|
187
|
-
initial={{ opacity: 0, y: -10 }}
|
|
188
|
-
animate={{ opacity: 1, y: 0 }}
|
|
189
|
-
transition={{ delay: 0.1, duration: 0.3 }}
|
|
190
|
-
/>
|
|
191
|
-
)
|
|
192
|
-
DialogProHeader.displayName = "DialogProHeader"
|
|
193
|
-
|
|
194
|
-
const DialogProFooter = ({
|
|
195
|
-
className,
|
|
196
|
-
...props
|
|
197
|
-
}: React.HTMLAttributes<HTMLDivElement>) => (
|
|
198
|
-
<motion.div
|
|
199
|
-
className={cn(
|
|
200
|
-
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
|
201
|
-
className
|
|
202
|
-
)}
|
|
203
|
-
initial={{ opacity: 0, y: 10 }}
|
|
204
|
-
animate={{ opacity: 1, y: 0 }}
|
|
205
|
-
transition={{ delay: 0.2, duration: 0.3 }}
|
|
206
|
-
/>
|
|
207
|
-
)
|
|
208
|
-
DialogProFooter.displayName = "DialogProFooter"
|
|
209
|
-
|
|
210
|
-
const DialogProTitle = React.forwardRef<
|
|
211
|
-
React.ElementRef<typeof DialogPrimitive.Title>,
|
|
212
|
-
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Title>
|
|
213
|
-
>(({ className, ...props }, ref) => (
|
|
214
|
-
<DialogPrimitive.Title
|
|
215
|
-
ref={ref}
|
|
216
|
-
className={cn(
|
|
217
|
-
"text-lg font-semibold leading-none tracking-tight",
|
|
218
|
-
className
|
|
219
|
-
)}
|
|
220
|
-
{...props}
|
|
221
|
-
/>
|
|
222
|
-
))
|
|
223
|
-
DialogProTitle.displayName = DialogPrimitive.Title.displayName
|
|
224
|
-
|
|
225
|
-
const DialogProDescription = React.forwardRef<
|
|
226
|
-
React.ElementRef<typeof DialogPrimitive.Description>,
|
|
227
|
-
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Description>
|
|
228
|
-
>(({ className, ...props }, ref) => (
|
|
229
|
-
<DialogPrimitive.Description
|
|
230
|
-
ref={ref}
|
|
231
|
-
className={cn("text-sm text-muted-foreground", className)}
|
|
232
|
-
{...props}
|
|
233
|
-
/>
|
|
234
|
-
))
|
|
235
|
-
DialogProDescription.displayName = DialogPrimitive.Description.displayName
|
|
236
|
-
|
|
237
|
-
export {
|
|
238
|
-
Dialog as DialogPro,
|
|
239
|
-
DialogTrigger as DialogProTrigger,
|
|
240
|
-
DialogProContent,
|
|
241
|
-
DialogProHeader,
|
|
242
|
-
DialogProFooter,
|
|
243
|
-
DialogProTitle,
|
|
244
|
-
DialogProDescription,
|
|
245
|
-
DialogClose as DialogProClose,
|
|
246
|
-
}
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
|
|
2
|
-
import '@testing-library/jest-dom'
|
|
3
|
-
import React from 'react'
|
|
4
|
-
import MoonUIFileUploadPro from './index'
|
|
5
|
-
|
|
6
|
-
// Mock FileReader
|
|
7
|
-
const mockFileReader = {
|
|
8
|
-
readAsDataURL: jest.fn(),
|
|
9
|
-
result: 'data:image/png;base64,mock-base64-data',
|
|
10
|
-
onload: null as any,
|
|
11
|
-
onerror: null as any,
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
global.FileReader = jest.fn(() => mockFileReader) as any
|
|
15
|
-
|
|
16
|
-
// Mock file objects
|
|
17
|
-
const createMockFile = (name: string, size: number, type: string) => {
|
|
18
|
-
const file = new File(['mock content'], name, { type })
|
|
19
|
-
Object.defineProperty(file, 'size', { value: size })
|
|
20
|
-
return file
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
describe('MoonUIFileUploadPro', () => {
|
|
24
|
-
beforeEach(() => {
|
|
25
|
-
jest.clearAllMocks()
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
it('renders without crashing', () => {
|
|
29
|
-
render(<MoonUIFileUploadPro />)
|
|
30
|
-
|
|
31
|
-
expect(screen.getByText('File Upload')).toBeInTheDocument()
|
|
32
|
-
expect(screen.getByText('Drag and drop files here, or click to select')).toBeInTheDocument()
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('renders with custom title and description', () => {
|
|
36
|
-
render(
|
|
37
|
-
<MoonUIFileUploadPro
|
|
38
|
-
accept="image/*"
|
|
39
|
-
maxSize={5}
|
|
40
|
-
multiple
|
|
41
|
-
/>
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
expect(screen.getByText('Upload up to 5 files (max 5MB each)')).toBeInTheDocument()
|
|
45
|
-
expect(screen.getByText('Accepted types: image/*')).toBeInTheDocument()
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
it('handles file selection', async () => {
|
|
49
|
-
const mockOnUpload = jest.fn()
|
|
50
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} />)
|
|
51
|
-
|
|
52
|
-
const file = createMockFile('test.jpg', 1024, 'image/jpeg')
|
|
53
|
-
const input = screen.getByRole('button', { hidden: true }) // Hidden file input
|
|
54
|
-
|
|
55
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
56
|
-
|
|
57
|
-
await waitFor(() => {
|
|
58
|
-
expect(mockOnUpload).toHaveBeenCalledWith([file])
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('validates file size', () => {
|
|
63
|
-
const mockOnUpload = jest.fn()
|
|
64
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} maxSize={1} />)
|
|
65
|
-
|
|
66
|
-
const file = createMockFile('large-file.jpg', 2 * 1024 * 1024, 'image/jpeg') // 2MB
|
|
67
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
68
|
-
|
|
69
|
-
// Mock alert
|
|
70
|
-
global.alert = jest.fn()
|
|
71
|
-
|
|
72
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
73
|
-
|
|
74
|
-
expect(global.alert).toHaveBeenCalledWith('large-file.jpg: File size must be less than 1MB')
|
|
75
|
-
expect(mockOnUpload).not.toHaveBeenCalled()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('validates file type', () => {
|
|
79
|
-
const mockOnUpload = jest.fn()
|
|
80
|
-
render(
|
|
81
|
-
<MoonUIFileUploadPro
|
|
82
|
-
onUpload={mockOnUpload}
|
|
83
|
-
allowedMimeTypes={['image/jpeg', 'image/png']}
|
|
84
|
-
/>
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
const file = createMockFile('document.pdf', 1024, 'application/pdf')
|
|
88
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
89
|
-
|
|
90
|
-
global.alert = jest.fn()
|
|
91
|
-
|
|
92
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
93
|
-
|
|
94
|
-
expect(global.alert).toHaveBeenCalledWith('document.pdf: File type application/pdf is not allowed')
|
|
95
|
-
expect(mockOnUpload).not.toHaveBeenCalled()
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('handles multiple files', async () => {
|
|
99
|
-
const mockOnUpload = jest.fn()
|
|
100
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} multiple />)
|
|
101
|
-
|
|
102
|
-
const files = [
|
|
103
|
-
createMockFile('file1.jpg', 1024, 'image/jpeg'),
|
|
104
|
-
createMockFile('file2.png', 2048, 'image/png'),
|
|
105
|
-
]
|
|
106
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
107
|
-
|
|
108
|
-
fireEvent.change(input, { target: { files } })
|
|
109
|
-
|
|
110
|
-
await waitFor(() => {
|
|
111
|
-
expect(mockOnUpload).toHaveBeenCalledWith(files)
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('respects maxFiles limit', () => {
|
|
116
|
-
const mockOnUpload = jest.fn()
|
|
117
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} maxFiles={2} />)
|
|
118
|
-
|
|
119
|
-
const files = [
|
|
120
|
-
createMockFile('file1.jpg', 1024, 'image/jpeg'),
|
|
121
|
-
createMockFile('file2.png', 1024, 'image/png'),
|
|
122
|
-
createMockFile('file3.gif', 1024, 'image/gif'),
|
|
123
|
-
]
|
|
124
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
125
|
-
|
|
126
|
-
global.alert = jest.fn()
|
|
127
|
-
|
|
128
|
-
fireEvent.change(input, { target: { files } })
|
|
129
|
-
|
|
130
|
-
expect(global.alert).toHaveBeenCalledWith('Maximum 2 files allowed')
|
|
131
|
-
expect(mockOnUpload).not.toHaveBeenCalled()
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('handles drag and drop', () => {
|
|
135
|
-
const mockOnUpload = jest.fn()
|
|
136
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} />)
|
|
137
|
-
|
|
138
|
-
const dropZone = screen.getByText('Drag and drop files here, or click to select').closest('div')
|
|
139
|
-
const file = createMockFile('test.jpg', 1024, 'image/jpeg')
|
|
140
|
-
|
|
141
|
-
fireEvent.dragOver(dropZone!, {
|
|
142
|
-
dataTransfer: { files: [file] },
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
expect(dropZone).toHaveClass('border-primary')
|
|
146
|
-
|
|
147
|
-
fireEvent.drop(dropZone!, {
|
|
148
|
-
dataTransfer: { files: [file] },
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
expect(dropZone).not.toHaveClass('border-primary')
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('handles file removal', async () => {
|
|
155
|
-
const mockOnRemove = jest.fn()
|
|
156
|
-
render(<MoonUIFileUploadPro onRemove={mockOnRemove} />)
|
|
157
|
-
|
|
158
|
-
const file = createMockFile('test.jpg', 1024, 'image/jpeg')
|
|
159
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
160
|
-
|
|
161
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
162
|
-
|
|
163
|
-
await waitFor(() => {
|
|
164
|
-
expect(screen.getByText('test.jpg')).toBeInTheDocument()
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
const removeButton = screen.getByRole('button', { name: /remove/i })
|
|
168
|
-
fireEvent.click(removeButton)
|
|
169
|
-
|
|
170
|
-
expect(mockOnRemove).toHaveBeenCalledWith(file)
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
it('shows progress during upload', async () => {
|
|
174
|
-
const mockOnUpload = jest.fn().mockResolvedValue(undefined)
|
|
175
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} />)
|
|
176
|
-
|
|
177
|
-
const file = createMockFile('test.jpg', 1024, 'image/jpeg')
|
|
178
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
179
|
-
|
|
180
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
181
|
-
|
|
182
|
-
await waitFor(() => {
|
|
183
|
-
expect(screen.getByText('Uploading files...')).toBeInTheDocument()
|
|
184
|
-
})
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('handles upload success', async () => {
|
|
188
|
-
const mockOnUpload = jest.fn().mockResolvedValue(undefined)
|
|
189
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} />)
|
|
190
|
-
|
|
191
|
-
const file = createMockFile('test.jpg', 1024, 'image/jpeg')
|
|
192
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
193
|
-
|
|
194
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
195
|
-
|
|
196
|
-
await waitFor(() => {
|
|
197
|
-
expect(screen.getByText('success')).toBeInTheDocument()
|
|
198
|
-
})
|
|
199
|
-
})
|
|
200
|
-
|
|
201
|
-
it('handles upload error', async () => {
|
|
202
|
-
const mockOnUpload = jest.fn().mockRejectedValue(new Error('Upload failed'))
|
|
203
|
-
render(<MoonUIFileUploadPro onUpload={mockOnUpload} />)
|
|
204
|
-
|
|
205
|
-
const file = createMockFile('test.jpg', 1024, 'image/jpeg')
|
|
206
|
-
const input = screen.getByRole('button', { hidden: true })
|
|
207
|
-
|
|
208
|
-
fireEvent.change(input, { target: { files: [file] } })
|
|
209
|
-
|
|
210
|
-
await waitFor(() => {
|
|
211
|
-
expect(screen.getByText('error')).toBeInTheDocument()
|
|
212
|
-
expect(screen.getByText('Upload failed')).toBeInTheDocument()
|
|
213
|
-
})
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
it('is disabled when disabled prop is true', () => {
|
|
217
|
-
render(<MoonUIFileUploadPro disabled />)
|
|
218
|
-
|
|
219
|
-
const dropZone = screen.getByText('Drag and drop files here, or click to select').closest('div')
|
|
220
|
-
expect(dropZone).toHaveClass('cursor-not-allowed')
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('applies custom className', () => {
|
|
224
|
-
render(<MoonUIFileUploadPro className="custom-upload" />)
|
|
225
|
-
|
|
226
|
-
const container = screen.getByText('File Upload').closest('div')
|
|
227
|
-
expect(container).toHaveClass('custom-upload')
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('formats file sizes correctly', () => {
|
|
231
|
-
render(<MoonUIFileUploadPro />)
|
|
232
|
-
|
|
233
|
-
// This would be tested through the file display after upload
|
|
234
|
-
// The formatFileSize function should be tested separately
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('displays correct file icons', () => {
|
|
238
|
-
render(<MoonUIFileUploadPro />)
|
|
239
|
-
|
|
240
|
-
// This would be tested through the file display after upload
|
|
241
|
-
// The getFileIcon function should be tested separately
|
|
242
|
-
})
|
|
243
|
-
})
|