@moontra/moonui 4.1.0 → 5.0.1

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.
Files changed (35) hide show
  1. package/dist/index.d.mts +2 -2
  2. package/dist/index.d.ts +2 -2
  3. package/dist/index.global.js +9 -9
  4. package/dist/index.global.js.map +1 -1
  5. package/dist/index.js +21 -15
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +21 -15
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/lib/theme.global.js.map +1 -1
  10. package/package.json +1 -1
  11. package/src/__tests__/use-intersection-observer.test.tsx +18 -4
  12. package/src/__tests__/use-local-storage.test.tsx +34 -20
  13. package/src/components/ui/__tests__/avatar.test.tsx +5 -7
  14. package/src/components/ui/__tests__/badge.test.tsx +24 -14
  15. package/src/components/ui/__tests__/breadcrumb.test.tsx +4 -7
  16. package/src/components/ui/__tests__/button.test.tsx +6 -3
  17. package/src/components/ui/__tests__/card.test.tsx +4 -1
  18. package/src/components/ui/__tests__/checkbox.test.tsx +4 -1
  19. package/src/components/ui/__tests__/command.test.tsx +268 -124
  20. package/src/components/ui/__tests__/data-table-basic.test.tsx +271 -0
  21. package/src/components/ui/__tests__/date-picker.test.tsx +184 -197
  22. package/src/components/ui/__tests__/progress.test.tsx +0 -1
  23. package/src/components/ui/__tests__/table.test.tsx +8 -2
  24. package/src/components/ui/__tests__/tabs.test.tsx +5 -3
  25. package/src/components/ui/__tests__/toast.test.tsx +461 -159
  26. package/src/components/ui/__tests__/tooltip.test.tsx +387 -264
  27. package/src/components/ui/badge.tsx +4 -4
  28. package/src/components/ui/command.tsx +11 -7
  29. package/src/components/ui/date-picker.tsx +12 -6
  30. package/src/components/ui/toast.tsx +3 -0
  31. package/src/styles/tokens.css +21 -6
  32. package/src/use-intersection-observer.tsx +9 -0
  33. package/src/use-local-storage.tsx +35 -1
  34. package/src/components/ui/__tests__/data-table.test.tsx +0 -256
  35. package/src/components/ui/data-table.stories.tsx +0 -511
@@ -1,511 +0,0 @@
1
- "use client"
2
-
3
- import type { Meta, StoryObj } from '@storybook/react';
4
- import { DataTable } from './data-table';
5
- import { Badge } from './badge';
6
- import { Button } from './button';
7
- import { MoreHorizontal, ArrowUpDown, Edit, Trash2 } from 'lucide-react';
8
- import { ColumnDef } from '@tanstack/react-table';
9
-
10
- // Sample data types
11
- type User = {
12
- id: string;
13
- name: string;
14
- email: string;
15
- role: 'admin' | 'user' | 'moderator';
16
- status: 'active' | 'inactive' | 'pending';
17
- createdAt: string;
18
- lastLogin: string;
19
- };
20
-
21
- type Product = {
22
- id: string;
23
- name: string;
24
- category: string;
25
- price: number;
26
- stock: number;
27
- status: 'available' | 'out_of_stock' | 'discontinued';
28
- };
29
-
30
- // Sample data
31
- const userData: User[] = [
32
- {
33
- id: '1',
34
- name: 'John Doe',
35
- email: 'john@example.com',
36
- role: 'admin',
37
- status: 'active',
38
- createdAt: '2023-01-15',
39
- lastLogin: '2024-01-15',
40
- },
41
- {
42
- id: '2',
43
- name: 'Jane Smith',
44
- email: 'jane@example.com',
45
- role: 'user',
46
- status: 'active',
47
- createdAt: '2023-02-20',
48
- lastLogin: '2024-01-14',
49
- },
50
- {
51
- id: '3',
52
- name: 'Bob Johnson',
53
- email: 'bob@example.com',
54
- role: 'moderator',
55
- status: 'inactive',
56
- createdAt: '2023-03-10',
57
- lastLogin: '2024-01-10',
58
- },
59
- {
60
- id: '4',
61
- name: 'Alice Brown',
62
- email: 'alice@example.com',
63
- role: 'user',
64
- status: 'pending',
65
- createdAt: '2023-04-05',
66
- lastLogin: '2024-01-12',
67
- },
68
- {
69
- id: '5',
70
- name: 'Charlie Wilson',
71
- email: 'charlie@example.com',
72
- role: 'user',
73
- status: 'active',
74
- createdAt: '2023-05-01',
75
- lastLogin: '2024-01-11',
76
- },
77
- ];
78
-
79
- const productData: Product[] = [
80
- {
81
- id: '1',
82
- name: 'Wireless Headphones',
83
- category: 'Electronics',
84
- price: 299.99,
85
- stock: 45,
86
- status: 'available',
87
- },
88
- {
89
- id: '2',
90
- name: 'Gaming Mouse',
91
- category: 'Electronics',
92
- price: 89.99,
93
- stock: 0,
94
- status: 'out_of_stock',
95
- },
96
- {
97
- id: '3',
98
- name: 'Mechanical Keyboard',
99
- category: 'Electronics',
100
- price: 159.99,
101
- stock: 23,
102
- status: 'available',
103
- },
104
- {
105
- id: '4',
106
- name: 'USB-C Cable',
107
- category: 'Accessories',
108
- price: 19.99,
109
- stock: 100,
110
- status: 'available',
111
- },
112
- {
113
- id: '5',
114
- name: 'Laptop Stand',
115
- category: 'Accessories',
116
- price: 49.99,
117
- stock: 0,
118
- status: 'discontinued',
119
- },
120
- ];
121
-
122
- // User columns
123
- const userColumns: ColumnDef<User>[] = [
124
- {
125
- accessorKey: 'name',
126
- header: ({ column }) => (
127
- <Button
128
- variant="ghost"
129
- onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
130
- >
131
- Name
132
- <ArrowUpDown className="ml-2 h-4 w-4" />
133
- </Button>
134
- ),
135
- },
136
- {
137
- accessorKey: 'email',
138
- header: 'Email',
139
- },
140
- {
141
- accessorKey: 'role',
142
- header: 'Role',
143
- cell: ({ row }) => {
144
- const role = row.getValue('role') as string;
145
- return (
146
- <Badge variant={role === 'admin' ? 'admin' : 'secondary'}>
147
- {role}
148
- </Badge>
149
- );
150
- },
151
- },
152
- {
153
- accessorKey: 'status',
154
- header: 'Status',
155
- cell: ({ row }) => {
156
- const status = row.getValue('status') as string;
157
- return (
158
- <Badge
159
- variant={
160
- status === 'active'
161
- ? 'success'
162
- : status === 'inactive'
163
- ? 'secondary'
164
- : 'outline'
165
- }
166
- >
167
- {status}
168
- </Badge>
169
- );
170
- },
171
- },
172
- {
173
- accessorKey: 'createdAt',
174
- header: 'Created',
175
- cell: ({ row }) => {
176
- const date = row.getValue('createdAt') as string;
177
- return new Date(date).toLocaleDateString();
178
- },
179
- },
180
- {
181
- id: 'actions',
182
- cell: ({ row }) => {
183
- const user = row.original;
184
- return (
185
- <div className="flex items-center gap-2">
186
- <Button variant="ghost" size="sm">
187
- <Edit className="h-4 w-4" />
188
- </Button>
189
- <Button variant="ghost" size="sm">
190
- <Trash2 className="h-4 w-4" />
191
- </Button>
192
- <Button variant="ghost" size="sm">
193
- <MoreHorizontal className="h-4 w-4" />
194
- </Button>
195
- </div>
196
- );
197
- },
198
- },
199
- ];
200
-
201
- // Product columns
202
- const productColumns: ColumnDef<Product>[] = [
203
- {
204
- accessorKey: 'name',
205
- header: ({ column }) => (
206
- <Button
207
- variant="ghost"
208
- onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
209
- >
210
- Product
211
- <ArrowUpDown className="ml-2 h-4 w-4" />
212
- </Button>
213
- ),
214
- },
215
- {
216
- accessorKey: 'category',
217
- header: 'Category',
218
- cell: ({ row }) => {
219
- const category = row.getValue('category') as string;
220
- return <Badge variant="outline">{category}</Badge>;
221
- },
222
- },
223
- {
224
- accessorKey: 'price',
225
- header: ({ column }) => (
226
- <Button
227
- variant="ghost"
228
- onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
229
- >
230
- Price
231
- <ArrowUpDown className="ml-2 h-4 w-4" />
232
- </Button>
233
- ),
234
- cell: ({ row }) => {
235
- const price = parseFloat(row.getValue('price'));
236
- return new Intl.NumberFormat('en-US', {
237
- style: 'currency',
238
- currency: 'USD',
239
- }).format(price);
240
- },
241
- },
242
- {
243
- accessorKey: 'stock',
244
- header: ({ column }) => (
245
- <Button
246
- variant="ghost"
247
- onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
248
- >
249
- Stock
250
- <ArrowUpDown className="ml-2 h-4 w-4" />
251
- </Button>
252
- ),
253
- },
254
- {
255
- accessorKey: 'status',
256
- header: 'Status',
257
- cell: ({ row }) => {
258
- const status = row.getValue('status') as string;
259
- return (
260
- <Badge
261
- variant={
262
- status === 'available'
263
- ? 'default'
264
- : status === 'out_of_stock'
265
- ? 'secondary'
266
- : 'outline'
267
- }
268
- >
269
- {status.replace('_', ' ')}
270
- </Badge>
271
- );
272
- },
273
- },
274
- ];
275
-
276
- const meta: Meta<typeof DataTable> = {
277
- title: 'Components/DataTable',
278
- component: DataTable,
279
- parameters: {
280
- layout: 'fullscreen',
281
- docs: {
282
- description: {
283
- component: 'A powerful data table component with sorting, filtering, and pagination.',
284
- },
285
- },
286
- },
287
- tags: ['autodocs'],
288
- argTypes: {
289
- columns: {
290
- description: 'Column definitions for the table',
291
- },
292
- data: {
293
- description: 'Data to display in the table',
294
- },
295
- searchable: {
296
- control: 'boolean',
297
- description: 'Enable search functionality',
298
- },
299
- filterable: {
300
- control: 'boolean',
301
- description: 'Enable column filtering',
302
- },
303
- exportable: {
304
- control: 'boolean',
305
- description: 'Enable data export',
306
- },
307
- },
308
- };
309
-
310
- export default meta;
311
- type Story = StoryObj<typeof meta>;
312
-
313
- export const Default: Story = {
314
- args: {
315
- columns: userColumns,
316
- data: userData,
317
- },
318
- };
319
-
320
- export const WithSearch: Story = {
321
- args: {
322
- columns: userColumns,
323
- data: userData,
324
- searchable: true,
325
- },
326
- };
327
-
328
- export const WithFilters: Story = {
329
- args: {
330
- columns: userColumns,
331
- data: userData,
332
- searchable: true,
333
- filterable: true,
334
- },
335
- };
336
-
337
- export const WithExport: Story = {
338
- args: {
339
- columns: userColumns,
340
- data: userData,
341
- searchable: true,
342
- filterable: true,
343
- exportable: true,
344
- },
345
- };
346
-
347
- export const ProductTable: Story = {
348
- args: {
349
- columns: productColumns,
350
- data: productData,
351
- searchable: true,
352
- filterable: true,
353
- },
354
- };
355
-
356
- export const LargeDataset: Story = {
357
- args: {
358
- columns: userColumns,
359
- data: Array.from({ length: 50 }, (_, i) => ({
360
- id: `${i + 1}`,
361
- name: `User ${i + 1}`,
362
- email: `user${i + 1}@example.com`,
363
- role: ['admin', 'user', 'moderator'][i % 3] as 'admin' | 'user' | 'moderator',
364
- status: ['active', 'inactive', 'pending'][i % 3] as 'active' | 'inactive' | 'pending',
365
- createdAt: new Date(2023, Math.floor(i / 12), (i % 30) + 1).toISOString().split('T')[0],
366
- lastLogin: new Date(2024, 0, (i % 15) + 1).toISOString().split('T')[0],
367
- })),
368
- searchable: true,
369
- filterable: true,
370
- exportable: true,
371
- },
372
- };
373
-
374
- export const EmptyState: Story = {
375
- args: {
376
- columns: userColumns,
377
- data: [],
378
- searchable: true,
379
- filterable: true,
380
- },
381
- };
382
-
383
- export const SingleRow: Story = {
384
- args: {
385
- columns: userColumns,
386
- data: [userData[0]],
387
- searchable: true,
388
- },
389
- };
390
-
391
- export const CustomPageSize: Story = {
392
- args: {
393
- columns: productColumns,
394
- data: productData,
395
- searchable: true,
396
- filterable: true,
397
- // Custom page size would be handled by the DataTable component
398
- },
399
- };
400
-
401
- export const CompactTable: Story = {
402
- args: {
403
- columns: productColumns.map(col => ({
404
- ...col,
405
- // Add compact styling
406
- })),
407
- data: productData,
408
- searchable: true,
409
- // Compact prop would be handled by the DataTable component
410
- },
411
- };
412
-
413
- export const InteractiveExample: Story = {
414
- render: () => {
415
- const columns: ColumnDef<User>[] = [
416
- {
417
- accessorKey: 'name',
418
- header: ({ column }) => (
419
- <Button
420
- variant="ghost"
421
- onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
422
- >
423
- Name
424
- <ArrowUpDown className="ml-2 h-4 w-4" />
425
- </Button>
426
- ),
427
- },
428
- {
429
- accessorKey: 'email',
430
- header: 'Email',
431
- },
432
- {
433
- accessorKey: 'role',
434
- header: 'Role',
435
- cell: ({ row }) => {
436
- const role = row.getValue('role') as string;
437
- return (
438
- <Badge
439
- variant={role === 'admin' ? 'default' : 'secondary'}
440
- className="cursor-pointer"
441
- onClick={() => alert(`Role: ${role}`)}
442
- >
443
- {role}
444
- </Badge>
445
- );
446
- },
447
- },
448
- {
449
- accessorKey: 'status',
450
- header: 'Status',
451
- cell: ({ row }) => {
452
- const status = row.getValue('status') as string;
453
- return (
454
- <Badge
455
- variant={
456
- status === 'active'
457
- ? 'default'
458
- : status === 'inactive'
459
- ? 'secondary'
460
- : 'outline'
461
- }
462
- className="cursor-pointer"
463
- onClick={() => alert(`Status: ${status}`)}
464
- >
465
- {status}
466
- </Badge>
467
- );
468
- },
469
- },
470
- {
471
- id: 'actions',
472
- cell: ({ row }) => {
473
- const user = row.original;
474
- return (
475
- <div className="flex items-center gap-2">
476
- <Button
477
- variant="ghost"
478
- size="sm"
479
- onClick={() => alert(`Edit user: ${user.name}`)}
480
- >
481
- <Edit className="h-4 w-4" />
482
- </Button>
483
- <Button
484
- variant="ghost"
485
- size="sm"
486
- onClick={() => alert(`Delete user: ${user.name}`)}
487
- >
488
- <Trash2 className="h-4 w-4" />
489
- </Button>
490
- </div>
491
- );
492
- },
493
- },
494
- ];
495
-
496
- return (
497
- <div className="space-y-4">
498
- <div className="text-sm text-muted-foreground">
499
- Click on badges or action buttons to see interactions
500
- </div>
501
- <DataTable
502
- columns={columns}
503
- data={userData}
504
- searchable={true}
505
- filterable={true}
506
- exportable={true}
507
- />
508
- </div>
509
- );
510
- },
511
- };