@startsimpli/ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +537 -0
  2. package/package.json +80 -0
  3. package/src/components/index.ts +50 -0
  4. package/src/components/navigation/sidebar.tsx +178 -0
  5. package/src/components/ui/accordion.tsx +58 -0
  6. package/src/components/ui/alert.tsx +59 -0
  7. package/src/components/ui/badge.tsx +36 -0
  8. package/src/components/ui/button.tsx +57 -0
  9. package/src/components/ui/calendar.tsx +70 -0
  10. package/src/components/ui/card.tsx +68 -0
  11. package/src/components/ui/checkbox.tsx +30 -0
  12. package/src/components/ui/collapsible.tsx +12 -0
  13. package/src/components/ui/dialog.tsx +122 -0
  14. package/src/components/ui/dropdown-menu.tsx +200 -0
  15. package/src/components/ui/index.ts +24 -0
  16. package/src/components/ui/input.tsx +25 -0
  17. package/src/components/ui/label.tsx +26 -0
  18. package/src/components/ui/popover.tsx +31 -0
  19. package/src/components/ui/progress.tsx +28 -0
  20. package/src/components/ui/scroll-area.tsx +48 -0
  21. package/src/components/ui/select.tsx +160 -0
  22. package/src/components/ui/separator.tsx +31 -0
  23. package/src/components/ui/skeleton.tsx +15 -0
  24. package/src/components/ui/table.tsx +117 -0
  25. package/src/components/ui/tabs.tsx +55 -0
  26. package/src/components/ui/textarea.tsx +24 -0
  27. package/src/components/ui/tooltip.tsx +30 -0
  28. package/src/components/unified-table/UnifiedTable.tsx +553 -0
  29. package/src/components/unified-table/__tests__/components/BulkActionBar.test.tsx +477 -0
  30. package/src/components/unified-table/__tests__/components/ExportButton.test.tsx +467 -0
  31. package/src/components/unified-table/__tests__/components/InlineEditCell.test.tsx +159 -0
  32. package/src/components/unified-table/__tests__/components/SavedViewsDropdown.test.tsx +128 -0
  33. package/src/components/unified-table/__tests__/components/TablePagination.test.tsx +374 -0
  34. package/src/components/unified-table/__tests__/hooks/useColumnReorder.test.ts +191 -0
  35. package/src/components/unified-table/__tests__/hooks/useColumnResize.test.ts +122 -0
  36. package/src/components/unified-table/__tests__/hooks/useColumnVisibility.test.ts +594 -0
  37. package/src/components/unified-table/__tests__/hooks/useFilters.test.ts +460 -0
  38. package/src/components/unified-table/__tests__/hooks/usePagination.test.ts +439 -0
  39. package/src/components/unified-table/__tests__/hooks/useResponsive.test.ts +421 -0
  40. package/src/components/unified-table/__tests__/hooks/useSelection.test.ts +367 -0
  41. package/src/components/unified-table/__tests__/hooks/useTableKeyboard.test.ts +803 -0
  42. package/src/components/unified-table/__tests__/hooks/useTableState.test.ts +210 -0
  43. package/src/components/unified-table/__tests__/integration/table-with-selection.test.tsx +624 -0
  44. package/src/components/unified-table/__tests__/utils/export.test.ts +427 -0
  45. package/src/components/unified-table/components/BulkActionBar/index.tsx +119 -0
  46. package/src/components/unified-table/components/DataTableCore/index.tsx +473 -0
  47. package/src/components/unified-table/components/InlineEditCell/index.tsx +159 -0
  48. package/src/components/unified-table/components/MobileView/Card.tsx +218 -0
  49. package/src/components/unified-table/components/MobileView/CardActions.tsx +126 -0
  50. package/src/components/unified-table/components/MobileView/README.md +411 -0
  51. package/src/components/unified-table/components/MobileView/index.tsx +77 -0
  52. package/src/components/unified-table/components/MobileView/types.ts +77 -0
  53. package/src/components/unified-table/components/TableFilters/index.tsx +298 -0
  54. package/src/components/unified-table/components/TablePagination/index.tsx +157 -0
  55. package/src/components/unified-table/components/Toolbar/ExportButton.tsx +229 -0
  56. package/src/components/unified-table/components/Toolbar/SavedViewsDropdown.tsx +251 -0
  57. package/src/components/unified-table/components/Toolbar/StandardTableToolbar.tsx +146 -0
  58. package/src/components/unified-table/components/Toolbar/index.tsx +3 -0
  59. package/src/components/unified-table/hooks/index.ts +21 -0
  60. package/src/components/unified-table/hooks/useColumnReorder.ts +90 -0
  61. package/src/components/unified-table/hooks/useColumnResize.ts +123 -0
  62. package/src/components/unified-table/hooks/useColumnVisibility.ts +92 -0
  63. package/src/components/unified-table/hooks/useFilters.ts +53 -0
  64. package/src/components/unified-table/hooks/usePagination.ts +120 -0
  65. package/src/components/unified-table/hooks/useResponsive.ts +50 -0
  66. package/src/components/unified-table/hooks/useSelection.ts +152 -0
  67. package/src/components/unified-table/hooks/useTableKeyboard.ts +206 -0
  68. package/src/components/unified-table/hooks/useTablePreferences.ts +198 -0
  69. package/src/components/unified-table/hooks/useTableState.ts +103 -0
  70. package/src/components/unified-table/hooks/useTableURL.test.tsx +921 -0
  71. package/src/components/unified-table/hooks/useTableURL.ts +301 -0
  72. package/src/components/unified-table/index.ts +16 -0
  73. package/src/components/unified-table/types.ts +393 -0
  74. package/src/components/unified-table/utils/export.ts +236 -0
  75. package/src/components/unified-table/utils/index.ts +4 -0
  76. package/src/components/unified-table/utils/renderers.ts +105 -0
  77. package/src/components/unified-table/utils/themes.ts +87 -0
  78. package/src/components/unified-table/utils/validation.ts +122 -0
  79. package/src/index.ts +6 -0
  80. package/src/lib/utils.ts +1 -0
  81. package/src/theme/contract.ts +46 -0
  82. package/src/theme/index.ts +9 -0
  83. package/src/theme/tailwind.config.js +70 -0
  84. package/src/theme/tailwind.preset.ts +93 -0
  85. package/src/utils/cn.ts +6 -0
  86. package/src/utils/index.ts +91 -0
@@ -0,0 +1,367 @@
1
+ import { renderHook, act } from '@testing-library/react'
2
+ import { useSelection } from '../../hooks/useSelection'
3
+
4
+ interface TestData {
5
+ id: string
6
+ name: string
7
+ }
8
+
9
+ describe('useSelection', () => {
10
+ const mockData: TestData[] = [
11
+ { id: '1', name: 'Item 1' },
12
+ { id: '2', name: 'Item 2' },
13
+ { id: '3', name: 'Item 3' },
14
+ ]
15
+
16
+ const getRowId = (row: TestData) => row.id
17
+
18
+ const defaultProps = {
19
+ currentPageData: mockData,
20
+ totalCount: 10,
21
+ getRowId,
22
+ }
23
+
24
+ describe('Initialization', () => {
25
+ it('should initialize with no selection', () => {
26
+ const { result } = renderHook(() => useSelection(defaultProps))
27
+
28
+ expect(result.current.selectedIds.size).toBe(0)
29
+ expect(result.current.selectAllPages).toBe(false)
30
+ expect(result.current.getSelectedCount()).toBe(0)
31
+ })
32
+ })
33
+
34
+ describe('Single Row Selection', () => {
35
+ it('should toggle a single row selection', () => {
36
+ const { result } = renderHook(() => useSelection(defaultProps))
37
+
38
+ act(() => {
39
+ result.current.toggleRow('1')
40
+ })
41
+
42
+ expect(result.current.selectedIds.has('1')).toBe(true)
43
+ expect(result.current.selectedIds.size).toBe(1)
44
+ expect(result.current.getSelectedCount()).toBe(1)
45
+ })
46
+
47
+ it('should deselect a selected row', () => {
48
+ const { result } = renderHook(() => useSelection(defaultProps))
49
+
50
+ act(() => {
51
+ result.current.toggleRow('1')
52
+ })
53
+
54
+ expect(result.current.selectedIds.has('1')).toBe(true)
55
+
56
+ act(() => {
57
+ result.current.toggleRow('1')
58
+ })
59
+
60
+ expect(result.current.selectedIds.has('1')).toBe(false)
61
+ expect(result.current.selectedIds.size).toBe(0)
62
+ })
63
+
64
+ it('should select multiple rows individually', () => {
65
+ const { result } = renderHook(() => useSelection(defaultProps))
66
+
67
+ act(() => {
68
+ result.current.toggleRow('1')
69
+ result.current.toggleRow('2')
70
+ result.current.toggleRow('3')
71
+ })
72
+
73
+ expect(result.current.selectedIds.size).toBe(3)
74
+ expect(result.current.selectedIds.has('1')).toBe(true)
75
+ expect(result.current.selectedIds.has('2')).toBe(true)
76
+ expect(result.current.selectedIds.has('3')).toBe(true)
77
+ })
78
+
79
+ it('should call onSelectionChange callback', () => {
80
+ const onSelectionChange = jest.fn()
81
+ const { result } = renderHook(() =>
82
+ useSelection({ ...defaultProps, onSelectionChange })
83
+ )
84
+
85
+ act(() => {
86
+ result.current.toggleRow('1')
87
+ })
88
+
89
+ expect(onSelectionChange).toHaveBeenCalledWith(expect.any(Set))
90
+ expect(onSelectionChange.mock.calls[0][0].has('1')).toBe(true)
91
+ })
92
+ })
93
+
94
+ describe('Toggle All (Current Page)', () => {
95
+ it('should select all rows on current page', () => {
96
+ const { result } = renderHook(() => useSelection(defaultProps))
97
+
98
+ act(() => {
99
+ result.current.toggleAll()
100
+ })
101
+
102
+ expect(result.current.selectedIds.size).toBe(3)
103
+ expect(result.current.selectedIds.has('1')).toBe(true)
104
+ expect(result.current.selectedIds.has('2')).toBe(true)
105
+ expect(result.current.selectedIds.has('3')).toBe(true)
106
+ })
107
+
108
+ it('should deselect all rows on current page', () => {
109
+ const { result } = renderHook(() => useSelection(defaultProps))
110
+
111
+ act(() => {
112
+ result.current.toggleAll()
113
+ })
114
+
115
+ expect(result.current.selectedIds.size).toBe(3)
116
+
117
+ act(() => {
118
+ result.current.toggleAll()
119
+ })
120
+
121
+ expect(result.current.selectedIds.size).toBe(0)
122
+ })
123
+
124
+ it('should select all when some are selected', () => {
125
+ const { result } = renderHook(() => useSelection(defaultProps))
126
+
127
+ act(() => {
128
+ result.current.toggleRow('1')
129
+ })
130
+
131
+ expect(result.current.selectedIds.size).toBe(1)
132
+
133
+ act(() => {
134
+ result.current.toggleAll()
135
+ })
136
+
137
+ // Should select all since not all were selected
138
+ expect(result.current.selectedIds.size).toBe(3)
139
+ })
140
+
141
+ it('should deselect selectAllPages when toggling individual page', () => {
142
+ const { result } = renderHook(() => useSelection(defaultProps))
143
+
144
+ act(() => {
145
+ result.current.selectAllPagesToggle()
146
+ })
147
+
148
+ expect(result.current.selectAllPages).toBe(true)
149
+
150
+ act(() => {
151
+ result.current.toggleAll()
152
+ })
153
+
154
+ expect(result.current.selectAllPages).toBe(false)
155
+ })
156
+ })
157
+
158
+ describe('Select All Pages', () => {
159
+ it('should toggle selectAllPages flag', () => {
160
+ const { result } = renderHook(() => useSelection(defaultProps))
161
+
162
+ act(() => {
163
+ result.current.selectAllPagesToggle()
164
+ })
165
+
166
+ expect(result.current.selectAllPages).toBe(true)
167
+
168
+ act(() => {
169
+ result.current.selectAllPagesToggle()
170
+ })
171
+
172
+ expect(result.current.selectAllPages).toBe(false)
173
+ })
174
+
175
+ it('should clear individual selections when selecting all pages', () => {
176
+ const { result } = renderHook(() => useSelection(defaultProps))
177
+
178
+ act(() => {
179
+ result.current.toggleRow('1')
180
+ result.current.toggleRow('2')
181
+ })
182
+
183
+ expect(result.current.selectedIds.size).toBe(2)
184
+
185
+ act(() => {
186
+ result.current.selectAllPagesToggle()
187
+ })
188
+
189
+ expect(result.current.selectAllPages).toBe(true)
190
+ expect(result.current.selectedIds.size).toBe(0)
191
+ })
192
+
193
+ it('should call onSelectAllPages callback', () => {
194
+ const onSelectAllPages = jest.fn()
195
+ const { result } = renderHook(() =>
196
+ useSelection({ ...defaultProps, onSelectAllPages })
197
+ )
198
+
199
+ act(() => {
200
+ result.current.selectAllPagesToggle()
201
+ })
202
+
203
+ expect(onSelectAllPages).toHaveBeenCalledWith(true)
204
+
205
+ act(() => {
206
+ result.current.selectAllPagesToggle()
207
+ })
208
+
209
+ expect(onSelectAllPages).toHaveBeenCalledWith(false)
210
+ })
211
+
212
+ it('should disable selectAllPages when toggling individual row', () => {
213
+ const { result } = renderHook(() => useSelection(defaultProps))
214
+
215
+ act(() => {
216
+ result.current.selectAllPagesToggle()
217
+ })
218
+
219
+ expect(result.current.selectAllPages).toBe(true)
220
+
221
+ act(() => {
222
+ result.current.toggleRow('1')
223
+ })
224
+
225
+ expect(result.current.selectAllPages).toBe(false)
226
+ })
227
+ })
228
+
229
+ describe('Clear Selection', () => {
230
+ it('should clear all selections', () => {
231
+ const { result } = renderHook(() => useSelection(defaultProps))
232
+
233
+ act(() => {
234
+ result.current.toggleRow('1')
235
+ result.current.toggleRow('2')
236
+ })
237
+
238
+ expect(result.current.selectedIds.size).toBe(2)
239
+
240
+ act(() => {
241
+ result.current.clearSelection()
242
+ })
243
+
244
+ expect(result.current.selectedIds.size).toBe(0)
245
+ expect(result.current.selectAllPages).toBe(false)
246
+ })
247
+
248
+ it('should clear selectAllPages flag', () => {
249
+ const { result } = renderHook(() => useSelection(defaultProps))
250
+
251
+ act(() => {
252
+ result.current.selectAllPagesToggle()
253
+ })
254
+
255
+ expect(result.current.selectAllPages).toBe(true)
256
+
257
+ act(() => {
258
+ result.current.clearSelection()
259
+ })
260
+
261
+ expect(result.current.selectAllPages).toBe(false)
262
+ })
263
+
264
+ it('should call callbacks when clearing', () => {
265
+ const onSelectionChange = jest.fn()
266
+ const onSelectAllPages = jest.fn()
267
+ const { result } = renderHook(() =>
268
+ useSelection({ ...defaultProps, onSelectionChange, onSelectAllPages })
269
+ )
270
+
271
+ act(() => {
272
+ result.current.toggleRow('1')
273
+ })
274
+
275
+ onSelectionChange.mockClear()
276
+
277
+ act(() => {
278
+ result.current.clearSelection()
279
+ })
280
+
281
+ expect(onSelectionChange).toHaveBeenCalledWith(expect.any(Set))
282
+ expect(onSelectionChange.mock.calls[0][0].size).toBe(0)
283
+ expect(onSelectAllPages).toHaveBeenCalledWith(false)
284
+ })
285
+ })
286
+
287
+ describe('Get Selected Count', () => {
288
+ it('should return correct count for individual selections', () => {
289
+ const { result } = renderHook(() => useSelection(defaultProps))
290
+
291
+ expect(result.current.getSelectedCount()).toBe(0)
292
+
293
+ act(() => {
294
+ result.current.toggleRow('1')
295
+ })
296
+
297
+ expect(result.current.getSelectedCount()).toBe(1)
298
+
299
+ act(() => {
300
+ result.current.toggleRow('2')
301
+ })
302
+
303
+ expect(result.current.getSelectedCount()).toBe(2)
304
+ })
305
+
306
+ it('should return totalCount when selectAllPages is true', () => {
307
+ const { result } = renderHook(() => useSelection(defaultProps))
308
+
309
+ act(() => {
310
+ result.current.selectAllPagesToggle()
311
+ })
312
+
313
+ expect(result.current.getSelectedCount()).toBe(10) // totalCount
314
+ })
315
+ })
316
+
317
+ describe('Edge Cases', () => {
318
+ it('should handle empty current page data', () => {
319
+ const { result } = renderHook(() =>
320
+ useSelection({ ...defaultProps, currentPageData: [] })
321
+ )
322
+
323
+ act(() => {
324
+ result.current.toggleAll()
325
+ })
326
+
327
+ expect(result.current.selectedIds.size).toBe(0)
328
+ })
329
+
330
+ it('should handle selecting non-existent row', () => {
331
+ const { result } = renderHook(() => useSelection(defaultProps))
332
+
333
+ act(() => {
334
+ result.current.toggleRow('non-existent')
335
+ })
336
+
337
+ expect(result.current.selectedIds.has('non-existent')).toBe(true)
338
+ expect(result.current.selectedIds.size).toBe(1)
339
+ })
340
+
341
+ it('should maintain selection across page changes', () => {
342
+ const { result, rerender } = renderHook(
343
+ ({ data }) => useSelection({ ...defaultProps, currentPageData: data }),
344
+ { initialProps: { data: mockData } }
345
+ )
346
+
347
+ act(() => {
348
+ result.current.toggleRow('1')
349
+ result.current.toggleRow('2')
350
+ })
351
+
352
+ expect(result.current.selectedIds.size).toBe(2)
353
+
354
+ // Simulate page change
355
+ const newPageData = [
356
+ { id: '4', name: 'Item 4' },
357
+ { id: '5', name: 'Item 5' },
358
+ ]
359
+ rerender({ data: newPageData })
360
+
361
+ // Previous selections should be maintained
362
+ expect(result.current.selectedIds.size).toBe(2)
363
+ expect(result.current.selectedIds.has('1')).toBe(true)
364
+ expect(result.current.selectedIds.has('2')).toBe(true)
365
+ })
366
+ })
367
+ })