@moontra/moonui-pro 2.20.0 → 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 +7419 -4935
- 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 +13 -13
- 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,281 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
2
|
-
import '@testing-library/jest-dom'
|
|
3
|
-
import { AdvancedChart } from './index'
|
|
4
|
-
|
|
5
|
-
// Mock recharts to avoid canvas issues in tests
|
|
6
|
-
jest.mock('recharts', () => ({
|
|
7
|
-
ResponsiveContainer: ({ children }: any) => <div data-testid="responsive-container">{children}</div>,
|
|
8
|
-
LineChart: ({ children }: any) => <div data-testid="line-chart">{children}</div>,
|
|
9
|
-
BarChart: ({ children }: any) => <div data-testid="bar-chart">{children}</div>,
|
|
10
|
-
AreaChart: ({ children }: any) => <div data-testid="area-chart">{children}</div>,
|
|
11
|
-
PieChart: ({ children }: any) => <div data-testid="pie-chart">{children}</div>,
|
|
12
|
-
ScatterChart: ({ children }: any) => <div data-testid="scatter-chart">{children}</div>,
|
|
13
|
-
Line: () => <div data-testid="line" />,
|
|
14
|
-
Bar: () => <div data-testid="bar" />,
|
|
15
|
-
Area: () => <div data-testid="area" />,
|
|
16
|
-
Pie: () => <div data-testid="pie" />,
|
|
17
|
-
Scatter: () => <div data-testid="scatter" />,
|
|
18
|
-
Cell: () => <div data-testid="cell" />,
|
|
19
|
-
XAxis: () => <div data-testid="x-axis" />,
|
|
20
|
-
YAxis: () => <div data-testid="y-axis" />,
|
|
21
|
-
CartesianGrid: () => <div data-testid="grid" />,
|
|
22
|
-
Tooltip: () => <div data-testid="tooltip" />,
|
|
23
|
-
Legend: () => <div data-testid="legend" />,
|
|
24
|
-
ReferenceLine: () => <div data-testid="reference-line" />,
|
|
25
|
-
ReferenceArea: () => <div data-testid="reference-area" />,
|
|
26
|
-
Brush: () => <div data-testid="brush" />,
|
|
27
|
-
}))
|
|
28
|
-
|
|
29
|
-
// Mock data for testing
|
|
30
|
-
const mockData = [
|
|
31
|
-
{ name: 'Jan', value: 400, revenue: 2400 },
|
|
32
|
-
{ name: 'Feb', value: 300, revenue: 1398 },
|
|
33
|
-
{ name: 'Mar', value: 200, revenue: 9800 },
|
|
34
|
-
{ name: 'Apr', value: 278, revenue: 3908 },
|
|
35
|
-
{ name: 'May', value: 189, revenue: 4800 },
|
|
36
|
-
{ name: 'Jun', value: 239, revenue: 3800 },
|
|
37
|
-
]
|
|
38
|
-
|
|
39
|
-
const mockSeries = [
|
|
40
|
-
{
|
|
41
|
-
dataKey: 'value',
|
|
42
|
-
name: 'Users',
|
|
43
|
-
color: '#8884d8',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
dataKey: 'revenue',
|
|
47
|
-
name: 'Revenue',
|
|
48
|
-
color: '#82ca9d',
|
|
49
|
-
},
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
describe('AdvancedChart', () => {
|
|
53
|
-
it('renders without crashing', () => {
|
|
54
|
-
render(
|
|
55
|
-
<AdvancedChart
|
|
56
|
-
data={mockData}
|
|
57
|
-
series={mockSeries}
|
|
58
|
-
type="line"
|
|
59
|
-
/>
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
expect(screen.getByText('Advanced Chart')).toBeInTheDocument()
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
it('renders with custom title', () => {
|
|
66
|
-
render(
|
|
67
|
-
<AdvancedChart
|
|
68
|
-
data={mockData}
|
|
69
|
-
series={mockSeries}
|
|
70
|
-
type="line"
|
|
71
|
-
title="Custom Chart Title"
|
|
72
|
-
/>
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
expect(screen.getByText('Custom Chart Title')).toBeInTheDocument()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('renders with subtitle', () => {
|
|
79
|
-
render(
|
|
80
|
-
<AdvancedChart
|
|
81
|
-
data={mockData}
|
|
82
|
-
series={mockSeries}
|
|
83
|
-
type="line"
|
|
84
|
-
title="Chart Title"
|
|
85
|
-
subtitle="Chart Subtitle"
|
|
86
|
-
/>
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
expect(screen.getByText('Chart Subtitle')).toBeInTheDocument()
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
it('renders line chart type', () => {
|
|
93
|
-
render(
|
|
94
|
-
<AdvancedChart
|
|
95
|
-
data={mockData}
|
|
96
|
-
series={mockSeries}
|
|
97
|
-
type="line"
|
|
98
|
-
/>
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
expect(screen.getByTestId('line-chart')).toBeInTheDocument()
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('renders bar chart type', () => {
|
|
105
|
-
render(
|
|
106
|
-
<AdvancedChart
|
|
107
|
-
data={mockData}
|
|
108
|
-
series={mockSeries}
|
|
109
|
-
type="bar"
|
|
110
|
-
/>
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
expect(screen.getByTestId('bar-chart')).toBeInTheDocument()
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('renders area chart type', () => {
|
|
117
|
-
render(
|
|
118
|
-
<AdvancedChart
|
|
119
|
-
data={mockData}
|
|
120
|
-
series={mockSeries}
|
|
121
|
-
type="area"
|
|
122
|
-
/>
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
expect(screen.getByTestId('area-chart')).toBeInTheDocument()
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
it('renders pie chart type', () => {
|
|
129
|
-
render(
|
|
130
|
-
<AdvancedChart
|
|
131
|
-
data={mockData}
|
|
132
|
-
series={mockSeries}
|
|
133
|
-
type="pie"
|
|
134
|
-
/>
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
expect(screen.getByTestId('pie-chart')).toBeInTheDocument()
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('renders scatter chart type', () => {
|
|
141
|
-
render(
|
|
142
|
-
<AdvancedChart
|
|
143
|
-
data={mockData}
|
|
144
|
-
series={mockSeries}
|
|
145
|
-
type="scatter"
|
|
146
|
-
/>
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
expect(screen.getByTestId('scatter-chart')).toBeInTheDocument()
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('shows loading state', () => {
|
|
153
|
-
render(
|
|
154
|
-
<AdvancedChart
|
|
155
|
-
data={mockData}
|
|
156
|
-
series={mockSeries}
|
|
157
|
-
type="line"
|
|
158
|
-
loading={true}
|
|
159
|
-
/>
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
expect(screen.getByText('Loading chart...')).toBeInTheDocument()
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
it('shows error state', () => {
|
|
166
|
-
render(
|
|
167
|
-
<AdvancedChart
|
|
168
|
-
data={mockData}
|
|
169
|
-
series={mockSeries}
|
|
170
|
-
type="line"
|
|
171
|
-
error="Something went wrong"
|
|
172
|
-
/>
|
|
173
|
-
)
|
|
174
|
-
|
|
175
|
-
expect(screen.getByText('Something went wrong')).toBeInTheDocument()
|
|
176
|
-
expect(screen.getByText('Retry')).toBeInTheDocument()
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('calls onRefresh when refresh button is clicked', () => {
|
|
180
|
-
const mockOnRefresh = jest.fn()
|
|
181
|
-
|
|
182
|
-
render(
|
|
183
|
-
<AdvancedChart
|
|
184
|
-
data={mockData}
|
|
185
|
-
series={mockSeries}
|
|
186
|
-
type="line"
|
|
187
|
-
onRefresh={mockOnRefresh}
|
|
188
|
-
/>
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
const refreshButton = screen.getByRole('button', { name: /refresh/i })
|
|
192
|
-
fireEvent.click(refreshButton)
|
|
193
|
-
|
|
194
|
-
expect(mockOnRefresh).toHaveBeenCalled()
|
|
195
|
-
})
|
|
196
|
-
|
|
197
|
-
it('calls onExport when export button is clicked', () => {
|
|
198
|
-
const mockOnExport = jest.fn()
|
|
199
|
-
|
|
200
|
-
render(
|
|
201
|
-
<AdvancedChart
|
|
202
|
-
data={mockData}
|
|
203
|
-
series={mockSeries}
|
|
204
|
-
type="line"
|
|
205
|
-
onExport={mockOnExport}
|
|
206
|
-
/>
|
|
207
|
-
)
|
|
208
|
-
|
|
209
|
-
const exportButton = screen.getByRole('button', { name: /download/i })
|
|
210
|
-
fireEvent.click(exportButton)
|
|
211
|
-
|
|
212
|
-
expect(mockOnExport).toHaveBeenCalledWith('png')
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
it('displays trend indicator', () => {
|
|
216
|
-
render(
|
|
217
|
-
<AdvancedChart
|
|
218
|
-
data={mockData}
|
|
219
|
-
series={mockSeries}
|
|
220
|
-
type="line"
|
|
221
|
-
title="Revenue Chart"
|
|
222
|
-
/>
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
// Should display trend percentage
|
|
226
|
-
expect(screen.getByText(/%/)).toBeInTheDocument()
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('renders with custom height', () => {
|
|
230
|
-
render(
|
|
231
|
-
<AdvancedChart
|
|
232
|
-
data={mockData}
|
|
233
|
-
series={mockSeries}
|
|
234
|
-
type="line"
|
|
235
|
-
height={600}
|
|
236
|
-
/>
|
|
237
|
-
)
|
|
238
|
-
|
|
239
|
-
expect(screen.getByTestId('responsive-container')).toBeInTheDocument()
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
it('handles responsive mode', () => {
|
|
243
|
-
render(
|
|
244
|
-
<AdvancedChart
|
|
245
|
-
data={mockData}
|
|
246
|
-
series={mockSeries}
|
|
247
|
-
type="line"
|
|
248
|
-
responsive={true}
|
|
249
|
-
/>
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
expect(screen.getByTestId('responsive-container')).toBeInTheDocument()
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
it('handles non-responsive mode', () => {
|
|
256
|
-
render(
|
|
257
|
-
<AdvancedChart
|
|
258
|
-
data={mockData}
|
|
259
|
-
series={mockSeries}
|
|
260
|
-
type="line"
|
|
261
|
-
responsive={false}
|
|
262
|
-
/>
|
|
263
|
-
)
|
|
264
|
-
|
|
265
|
-
expect(screen.queryByTestId('responsive-container')).not.toBeInTheDocument()
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
it('applies custom className', () => {
|
|
269
|
-
render(
|
|
270
|
-
<AdvancedChart
|
|
271
|
-
data={mockData}
|
|
272
|
-
series={mockSeries}
|
|
273
|
-
type="line"
|
|
274
|
-
className="custom-chart"
|
|
275
|
-
/>
|
|
276
|
-
)
|
|
277
|
-
|
|
278
|
-
const chartContainer = screen.getByText('Advanced Chart').closest('div')
|
|
279
|
-
expect(chartContainer).toHaveClass('custom-chart')
|
|
280
|
-
})
|
|
281
|
-
})
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { render, screen, fireEvent } from '@testing-library/react'
|
|
2
|
-
import '@testing-library/jest-dom'
|
|
3
|
-
import { DataTable } from './index'
|
|
4
|
-
|
|
5
|
-
// Mock data for testing
|
|
6
|
-
const mockData = [
|
|
7
|
-
{ id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' },
|
|
8
|
-
{ id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' },
|
|
9
|
-
{ id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'active' }
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
const mockColumns = [
|
|
13
|
-
{
|
|
14
|
-
accessorKey: 'name',
|
|
15
|
-
header: 'Name',
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
accessorKey: 'email',
|
|
19
|
-
header: 'Email',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
accessorKey: 'status',
|
|
23
|
-
header: 'Status',
|
|
24
|
-
}
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
describe('DataTable', () => {
|
|
28
|
-
it('renders without crashing', () => {
|
|
29
|
-
render(
|
|
30
|
-
<DataTable
|
|
31
|
-
columns={mockColumns}
|
|
32
|
-
data={mockData}
|
|
33
|
-
/>
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
expect(screen.getByText('Name')).toBeInTheDocument()
|
|
37
|
-
expect(screen.getByText('Email')).toBeInTheDocument()
|
|
38
|
-
expect(screen.getByText('Status')).toBeInTheDocument()
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('displays data correctly', () => {
|
|
42
|
-
render(
|
|
43
|
-
<DataTable
|
|
44
|
-
columns={mockColumns}
|
|
45
|
-
data={mockData}
|
|
46
|
-
/>
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
expect(screen.getByText('John Doe')).toBeInTheDocument()
|
|
50
|
-
expect(screen.getByText('jane@example.com')).toBeInTheDocument()
|
|
51
|
-
expect(screen.getByText('active')).toBeInTheDocument()
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('shows search input when searchable is true', () => {
|
|
55
|
-
render(
|
|
56
|
-
<DataTable
|
|
57
|
-
columns={mockColumns}
|
|
58
|
-
data={mockData}
|
|
59
|
-
searchable
|
|
60
|
-
/>
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
expect(screen.getByPlaceholderText('Search all columns...')).toBeInTheDocument()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('shows filter button when filterable is true', () => {
|
|
67
|
-
render(
|
|
68
|
-
<DataTable
|
|
69
|
-
columns={mockColumns}
|
|
70
|
-
data={mockData}
|
|
71
|
-
filterable
|
|
72
|
-
/>
|
|
73
|
-
)
|
|
74
|
-
|
|
75
|
-
expect(screen.getByText('Filters')).toBeInTheDocument()
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('shows export button when exportable is true', () => {
|
|
79
|
-
render(
|
|
80
|
-
<DataTable
|
|
81
|
-
columns={mockColumns}
|
|
82
|
-
data={mockData}
|
|
83
|
-
exportable
|
|
84
|
-
/>
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
expect(screen.getByText('Export')).toBeInTheDocument()
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('handles search functionality', () => {
|
|
91
|
-
render(
|
|
92
|
-
<DataTable
|
|
93
|
-
columns={mockColumns}
|
|
94
|
-
data={mockData}
|
|
95
|
-
searchable
|
|
96
|
-
/>
|
|
97
|
-
)
|
|
98
|
-
|
|
99
|
-
const searchInput = screen.getByPlaceholderText('Search all columns...')
|
|
100
|
-
fireEvent.change(searchInput, { target: { value: 'john' } })
|
|
101
|
-
|
|
102
|
-
expect(screen.getByText('John Doe')).toBeInTheDocument()
|
|
103
|
-
// Jane Smith should be filtered out
|
|
104
|
-
expect(screen.queryByText('Jane Smith')).not.toBeInTheDocument()
|
|
105
|
-
})
|
|
106
|
-
|
|
107
|
-
it('handles pagination', () => {
|
|
108
|
-
// Create more data for pagination testing
|
|
109
|
-
const moreData = Array.from({ length: 20 }, (_, i) => ({
|
|
110
|
-
id: i + 1,
|
|
111
|
-
name: `User ${i + 1}`,
|
|
112
|
-
email: `user${i + 1}@example.com`,
|
|
113
|
-
status: i % 2 === 0 ? 'active' : 'inactive'
|
|
114
|
-
}))
|
|
115
|
-
|
|
116
|
-
render(
|
|
117
|
-
<DataTable
|
|
118
|
-
columns={mockColumns}
|
|
119
|
-
data={moreData}
|
|
120
|
-
pagination
|
|
121
|
-
pageSize={10}
|
|
122
|
-
/>
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
expect(screen.getByText('Page 1 of 2')).toBeInTheDocument()
|
|
126
|
-
expect(screen.getByText('Rows per page')).toBeInTheDocument()
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('calls onRowSelect when row is selected', () => {
|
|
130
|
-
const mockOnRowSelect = jest.fn()
|
|
131
|
-
|
|
132
|
-
render(
|
|
133
|
-
<DataTable
|
|
134
|
-
columns={mockColumns}
|
|
135
|
-
data={mockData}
|
|
136
|
-
selectable
|
|
137
|
-
onRowSelect={mockOnRowSelect}
|
|
138
|
-
/>
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
// This would require implementing selection in the component
|
|
142
|
-
// For now, just verify the component renders with selection enabled
|
|
143
|
-
expect(screen.getByRole('table')).toBeInTheDocument()
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
it('calls onExport when export button is clicked', () => {
|
|
147
|
-
const mockOnExport = jest.fn()
|
|
148
|
-
|
|
149
|
-
render(
|
|
150
|
-
<DataTable
|
|
151
|
-
columns={mockColumns}
|
|
152
|
-
data={mockData}
|
|
153
|
-
exportable
|
|
154
|
-
onExport={mockOnExport}
|
|
155
|
-
/>
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
const exportButton = screen.getByText('Export')
|
|
159
|
-
fireEvent.click(exportButton)
|
|
160
|
-
|
|
161
|
-
expect(mockOnExport).toHaveBeenCalledWith(mockData)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('handles empty data state', () => {
|
|
165
|
-
render(
|
|
166
|
-
<DataTable
|
|
167
|
-
columns={mockColumns}
|
|
168
|
-
data={[]}
|
|
169
|
-
/>
|
|
170
|
-
)
|
|
171
|
-
|
|
172
|
-
expect(screen.getByText('No results found.')).toBeInTheDocument()
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
it('applies custom className', () => {
|
|
176
|
-
render(
|
|
177
|
-
<DataTable
|
|
178
|
-
columns={mockColumns}
|
|
179
|
-
data={mockData}
|
|
180
|
-
className="custom-table"
|
|
181
|
-
/>
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
const tableContainer = screen.getByRole('table').closest('div')
|
|
185
|
-
expect(tableContainer).toHaveClass('custom-table')
|
|
186
|
-
})
|
|
187
|
-
})
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { motion, AnimatePresence } from 'framer-motion'
|
|
3
|
-
import { cva, type VariantProps } from 'class-variance-authority'
|
|
4
|
-
import { cn } from '../../lib/utils'
|
|
5
|
-
|
|
6
|
-
const badgeVariants = cva(
|
|
7
|
-
["inline-flex items-center gap-1.5",
|
|
8
|
-
"font-medium transition-all duration-200",
|
|
9
|
-
"border focus:outline-none",
|
|
10
|
-
"focus-visible:ring-2 focus-visible:ring-offset-1"],
|
|
11
|
-
{
|
|
12
|
-
variants: {
|
|
13
|
-
variant: {
|
|
14
|
-
default: [
|
|
15
|
-
"border-transparent bg-primary text-primary-foreground",
|
|
16
|
-
"hover:bg-primary/90",
|
|
17
|
-
"focus-visible:ring-primary/30",
|
|
18
|
-
],
|
|
19
|
-
secondary: [
|
|
20
|
-
"border-transparent bg-secondary text-secondary-foreground",
|
|
21
|
-
"hover:bg-secondary/80",
|
|
22
|
-
"focus-visible:ring-gray-400/30",
|
|
23
|
-
],
|
|
24
|
-
destructive: [
|
|
25
|
-
"border-transparent bg-destructive text-destructive-foreground",
|
|
26
|
-
"hover:bg-destructive/90",
|
|
27
|
-
"focus-visible:ring-destructive/30",
|
|
28
|
-
],
|
|
29
|
-
outline: [
|
|
30
|
-
"border-input bg-transparent",
|
|
31
|
-
"hover:border-input/80",
|
|
32
|
-
"focus-visible:ring-gray-400/30",
|
|
33
|
-
],
|
|
34
|
-
success: [
|
|
35
|
-
"border-transparent bg-green-500 text-white",
|
|
36
|
-
"hover:bg-green-600",
|
|
37
|
-
"focus-visible:ring-green-500/30",
|
|
38
|
-
],
|
|
39
|
-
warning: [
|
|
40
|
-
"border-transparent bg-yellow-500 text-white",
|
|
41
|
-
"hover:bg-yellow-600",
|
|
42
|
-
"focus-visible:ring-yellow-500/30",
|
|
43
|
-
],
|
|
44
|
-
pro: [
|
|
45
|
-
"border-transparent bg-gradient-to-r from-purple-600 to-pink-600 text-white",
|
|
46
|
-
"hover:from-purple-700 hover:to-pink-700",
|
|
47
|
-
"focus-visible:ring-purple-400/30",
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
size: {
|
|
51
|
-
sm: "h-5 px-2 text-xs",
|
|
52
|
-
default: "h-6 px-3 text-sm",
|
|
53
|
-
lg: "h-8 px-4 text-base",
|
|
54
|
-
},
|
|
55
|
-
},
|
|
56
|
-
defaultVariants: {
|
|
57
|
-
variant: "default",
|
|
58
|
-
size: "default",
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
)
|
|
62
|
-
|
|
63
|
-
export interface BadgeProProps
|
|
64
|
-
extends React.HTMLAttributes<HTMLDivElement>,
|
|
65
|
-
VariantProps<typeof badgeVariants> {
|
|
66
|
-
enablePulse?: boolean
|
|
67
|
-
enableGlow?: boolean
|
|
68
|
-
enableShimmer?: boolean
|
|
69
|
-
removable?: boolean
|
|
70
|
-
onRemove?: () => void
|
|
71
|
-
leftIcon?: React.ReactNode
|
|
72
|
-
rightIcon?: React.ReactNode
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export const BadgePro = React.forwardRef<
|
|
76
|
-
HTMLDivElement,
|
|
77
|
-
BadgeProProps
|
|
78
|
-
>(({
|
|
79
|
-
className,
|
|
80
|
-
variant,
|
|
81
|
-
size,
|
|
82
|
-
enablePulse = false,
|
|
83
|
-
enableGlow = false,
|
|
84
|
-
enableShimmer = false,
|
|
85
|
-
removable = false,
|
|
86
|
-
onRemove,
|
|
87
|
-
leftIcon,
|
|
88
|
-
rightIcon,
|
|
89
|
-
children,
|
|
90
|
-
...props
|
|
91
|
-
}, ref) => {
|
|
92
|
-
const [isRemoving, setIsRemoving] = React.useState(false)
|
|
93
|
-
|
|
94
|
-
const handleRemove = () => {
|
|
95
|
-
setIsRemoving(true)
|
|
96
|
-
setTimeout(() => {
|
|
97
|
-
onRemove?.()
|
|
98
|
-
}, 300)
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Auto-assign icons for special variants
|
|
102
|
-
let autoLeftIcon = leftIcon
|
|
103
|
-
let autoChildren = children
|
|
104
|
-
|
|
105
|
-
if (variant === 'pro' && !leftIcon) {
|
|
106
|
-
autoLeftIcon = (
|
|
107
|
-
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-3 h-3">
|
|
108
|
-
<path d="M12 0L13.09 8.26L22 9L13.09 9.74L12 18L10.91 9.74L2 9L10.91 8.26L12 0Z" fill="currentColor"/>
|
|
109
|
-
<path d="M19 5L19.5 7L21 7.5L19.5 8L19 10L18.5 8L17 7.5L18.5 7L19 5Z" fill="currentColor"/>
|
|
110
|
-
<path d="M19 15L19.5 17L21 17.5L19.5 18L19 20L18.5 18L17 17.5L18.5 17L19 15Z" fill="currentColor"/>
|
|
111
|
-
</svg>
|
|
112
|
-
)
|
|
113
|
-
autoChildren = children || 'Pro'
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return (
|
|
117
|
-
<AnimatePresence>
|
|
118
|
-
{!isRemoving && (
|
|
119
|
-
<motion.div
|
|
120
|
-
ref={ref}
|
|
121
|
-
className={cn(
|
|
122
|
-
badgeVariants({ variant, size }),
|
|
123
|
-
enablePulse && "animate-pulse",
|
|
124
|
-
enableGlow && "shadow-lg shadow-current/50",
|
|
125
|
-
enableShimmer && "relative overflow-hidden",
|
|
126
|
-
className
|
|
127
|
-
)}
|
|
128
|
-
initial={{ opacity: 0, scale: 0.8 }}
|
|
129
|
-
animate={{ opacity: 1, scale: 1 }}
|
|
130
|
-
exit={{ opacity: 0, scale: 0.8 }}
|
|
131
|
-
transition={{ duration: 0.2 }}
|
|
132
|
-
whileHover={{ scale: 1.05 }}
|
|
133
|
-
whileTap={{ scale: 0.95 }}
|
|
134
|
-
>
|
|
135
|
-
{enableShimmer && (
|
|
136
|
-
<motion.div
|
|
137
|
-
className="absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent"
|
|
138
|
-
animate={{ translateX: "200%" }}
|
|
139
|
-
transition={{ duration: 2, repeat: Infinity, ease: "linear" }}
|
|
140
|
-
/>
|
|
141
|
-
)}
|
|
142
|
-
|
|
143
|
-
{autoLeftIcon && (
|
|
144
|
-
<motion.span
|
|
145
|
-
className="inline-flex shrink-0"
|
|
146
|
-
animate={enablePulse ? { scale: [1, 1.2, 1] } : {}}
|
|
147
|
-
transition={{ duration: 2, repeat: Infinity }}
|
|
148
|
-
>
|
|
149
|
-
{autoLeftIcon}
|
|
150
|
-
</motion.span>
|
|
151
|
-
)}
|
|
152
|
-
|
|
153
|
-
<span className="truncate">{autoChildren}</span>
|
|
154
|
-
|
|
155
|
-
{rightIcon && (
|
|
156
|
-
<span className="inline-flex shrink-0">
|
|
157
|
-
{rightIcon}
|
|
158
|
-
</span>
|
|
159
|
-
)}
|
|
160
|
-
|
|
161
|
-
{removable && onRemove && (
|
|
162
|
-
<motion.button
|
|
163
|
-
type="button"
|
|
164
|
-
className="ml-1 -mr-1 h-3.5 w-3.5 rounded-full inline-flex items-center justify-center hover:bg-black/10 dark:hover:bg-white/10"
|
|
165
|
-
onClick={handleRemove}
|
|
166
|
-
whileHover={{ rotate: 90 }}
|
|
167
|
-
transition={{ type: "spring", stiffness: 300 }}
|
|
168
|
-
aria-label="Remove badge"
|
|
169
|
-
>
|
|
170
|
-
<svg
|
|
171
|
-
width="8"
|
|
172
|
-
height="8"
|
|
173
|
-
viewBox="0 0 8 8"
|
|
174
|
-
fill="none"
|
|
175
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
176
|
-
className="opacity-70"
|
|
177
|
-
aria-hidden="true"
|
|
178
|
-
>
|
|
179
|
-
<path d="M0.799988 7.19999L7.19999 0.799988M0.799988 0.799988L7.19999 7.19999" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/>
|
|
180
|
-
</svg>
|
|
181
|
-
</motion.button>
|
|
182
|
-
)}
|
|
183
|
-
</motion.div>
|
|
184
|
-
)}
|
|
185
|
-
</AnimatePresence>
|
|
186
|
-
)
|
|
187
|
-
})
|
|
188
|
-
|
|
189
|
-
BadgePro.displayName = 'BadgePro'
|
|
190
|
-
|
|
191
|
-
export { badgeVariants }
|