@shohojdhara/atomix 0.3.6 → 0.3.8

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 (74) hide show
  1. package/README.md +3 -3
  2. package/dist/atomix.css +77 -0
  3. package/dist/atomix.css.map +1 -1
  4. package/dist/atomix.min.css +77 -0
  5. package/dist/atomix.min.css.map +1 -1
  6. package/dist/charts.js +50 -142
  7. package/dist/charts.js.map +1 -1
  8. package/dist/core.d.ts +2 -2
  9. package/dist/core.js +179 -274
  10. package/dist/core.js.map +1 -1
  11. package/dist/forms.js +50 -142
  12. package/dist/forms.js.map +1 -1
  13. package/dist/heavy.js +179 -274
  14. package/dist/heavy.js.map +1 -1
  15. package/dist/index.d.ts +1255 -1226
  16. package/dist/index.esm.js +2806 -2958
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/index.js +3113 -3269
  19. package/dist/index.js.map +1 -1
  20. package/dist/index.min.js +1 -1
  21. package/dist/index.min.js.map +1 -1
  22. package/dist/theme.d.ts +313 -667
  23. package/dist/theme.js +1818 -2589
  24. package/dist/theme.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
  27. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
  28. package/src/components/Button/Button.tsx +85 -167
  29. package/src/components/DataTable/DataTable.stories.tsx +238 -0
  30. package/src/components/DataTable/DataTable.test.tsx +450 -0
  31. package/src/components/DataTable/DataTable.tsx +384 -61
  32. package/src/components/DatePicker/DatePicker.tsx +29 -38
  33. package/src/components/Upload/Upload.tsx +539 -40
  34. package/src/lib/composables/useAtomixGlass.ts +7 -7
  35. package/src/lib/composables/useDataTable.ts +355 -15
  36. package/src/lib/composables/useDatePicker.ts +19 -0
  37. package/src/lib/config/loader.ts +2 -3
  38. package/src/lib/constants/components.ts +17 -0
  39. package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
  40. package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
  41. package/src/lib/theme/adapters/index.ts +1 -4
  42. package/src/lib/theme/config/configLoader.ts +82 -223
  43. package/src/lib/theme/config/loader.ts +15 -21
  44. package/src/lib/theme/constants/constants.ts +1 -1
  45. package/src/lib/theme/core/ThemeRegistry.ts +75 -279
  46. package/src/lib/theme/core/composeTheme.ts +30 -88
  47. package/src/lib/theme/core/createTheme.ts +88 -51
  48. package/src/lib/theme/core/createThemeObject.ts +2 -2
  49. package/src/lib/theme/core/index.ts +15 -2
  50. package/src/lib/theme/errors/errors.ts +1 -1
  51. package/src/lib/theme/generators/generateCSSNested.ts +131 -0
  52. package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
  53. package/src/lib/theme/generators/index.ts +6 -0
  54. package/src/lib/theme/index.ts +45 -27
  55. package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
  56. package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
  57. package/src/lib/theme/runtime/ThemeProvider.tsx +393 -544
  58. package/src/lib/theme/runtime/index.ts +1 -0
  59. package/src/lib/theme/runtime/useTheme.ts +1 -1
  60. package/src/lib/theme/runtime/useThemeTokens.ts +122 -0
  61. package/src/lib/theme/test/testTheme.ts +2 -1
  62. package/src/lib/theme/types.ts +14 -14
  63. package/src/lib/theme/utils/componentTheming.ts +140 -0
  64. package/src/lib/theme/utils/domUtils.ts +57 -15
  65. package/src/lib/theme/utils/injectCSS.ts +0 -1
  66. package/src/lib/theme/utils/naming.ts +100 -0
  67. package/src/lib/theme/utils/themeHelpers.ts +1 -39
  68. package/src/lib/theme/utils/themeUtils.ts +1 -170
  69. package/src/lib/types/components.ts +145 -0
  70. package/src/lib/utils/componentUtils.ts +1 -1
  71. package/src/lib/utils/dataTableExport.ts +143 -0
  72. package/src/lib/utils/memoryMonitor.ts +3 -3
  73. package/src/lib/utils/themeNaming.ts +135 -0
  74. package/src/styles/06-components/_components.data-table.scss +95 -0
@@ -0,0 +1,450 @@
1
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import { DataTable } from './DataTable';
4
+ import { DataTableColumn } from '../../lib/types/components';
5
+
6
+ // Mock dependencies
7
+ vi.mock('../AtomixGlass/AtomixGlass', () => ({
8
+ AtomixGlass: ({ children }: any) => <div>{children}</div>,
9
+ }));
10
+
11
+ vi.mock('../Spinner/Spinner', () => ({
12
+ Spinner: () => <div data-testid="spinner">Loading...</div>,
13
+ }));
14
+
15
+ vi.mock('../Icon/Icon', () => ({
16
+ Icon: ({ name }: any) => <span data-testid={`icon-${name}`}>{name}</span>,
17
+ }));
18
+
19
+ vi.mock('../Pagination/Pagination', () => ({
20
+ Pagination: ({ currentPage, totalPages, onPageChange }: any) => (
21
+ <div data-testid="pagination">
22
+ <button onClick={() => onPageChange(currentPage - 1)}>Prev</button>
23
+ <span>{currentPage} / {totalPages}</span>
24
+ <button onClick={() => onPageChange(currentPage + 1)}>Next</button>
25
+ </div>
26
+ ),
27
+ }));
28
+
29
+ vi.mock('../Form/Checkbox', () => ({
30
+ Checkbox: ({ checked, onChange, label, ...props }: any) => (
31
+ <label>
32
+ <input
33
+ type="checkbox"
34
+ checked={checked}
35
+ onChange={onChange}
36
+ data-testid={props['data-testid'] || 'checkbox'}
37
+ {...props}
38
+ />
39
+ {label}
40
+ </label>
41
+ ),
42
+ }));
43
+
44
+ vi.mock('../Dropdown/Dropdown', () => ({
45
+ Dropdown: ({ children, menu, trigger }: any) => (
46
+ <div data-testid="dropdown">
47
+ {children}
48
+ {trigger === 'click' && <div data-testid="dropdown-menu">{menu}</div>}
49
+ </div>
50
+ ),
51
+ DropdownItem: ({ children, onClick }: any) => (
52
+ <div data-testid="dropdown-item" onClick={onClick}>
53
+ {children}
54
+ </div>
55
+ ),
56
+ DropdownDivider: () => <hr data-testid="dropdown-divider" />,
57
+ }));
58
+
59
+ vi.mock('../Button/Button', () => ({
60
+ Button: ({ children, onClick, ...props }: any) => (
61
+ <button onClick={onClick} data-testid="button" {...props}>
62
+ {children}
63
+ </button>
64
+ ),
65
+ }));
66
+
67
+ // Sample data and columns
68
+ const sampleData = [
69
+ { id: 1, name: 'John Doe', email: 'john@example.com', role: 'Admin', status: 'Active' },
70
+ { id: 2, name: 'Jane Smith', email: 'jane@example.com', role: 'User', status: 'Inactive' },
71
+ { id: 3, name: 'Bob Johnson', email: 'bob@example.com', role: 'User', status: 'Active' },
72
+ ];
73
+
74
+ const sampleColumns: DataTableColumn[] = [
75
+ { key: 'id', title: 'ID', sortable: true },
76
+ { key: 'name', title: 'Name', sortable: true, filterable: true },
77
+ { key: 'email', title: 'Email', sortable: true },
78
+ { key: 'role', title: 'Role', sortable: true, filterable: true },
79
+ { key: 'status', title: 'Status', sortable: true },
80
+ ];
81
+
82
+ describe('DataTable Component', () => {
83
+ describe('Basic Rendering', () => {
84
+ it('renders table with data', () => {
85
+ render(<DataTable data={sampleData} columns={sampleColumns} />);
86
+
87
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
88
+ expect(screen.getByText('jane@example.com')).toBeInTheDocument();
89
+ expect(screen.getByText('Bob Johnson')).toBeInTheDocument();
90
+ });
91
+
92
+ it('renders column headers', () => {
93
+ render(<DataTable data={sampleData} columns={sampleColumns} />);
94
+
95
+ expect(screen.getByText('ID')).toBeInTheDocument();
96
+ expect(screen.getByText('Name')).toBeInTheDocument();
97
+ expect(screen.getByText('Email')).toBeInTheDocument();
98
+ expect(screen.getByText('Role')).toBeInTheDocument();
99
+ expect(screen.getByText('Status')).toBeInTheDocument();
100
+ });
101
+
102
+ it('displays empty message when no data', () => {
103
+ render(
104
+ <DataTable
105
+ data={[]}
106
+ columns={sampleColumns}
107
+ emptyMessage="No records found"
108
+ />
109
+ );
110
+
111
+ expect(screen.getByText('No records found')).toBeInTheDocument();
112
+ });
113
+
114
+ it('displays loading state', () => {
115
+ render(
116
+ <DataTable
117
+ data={sampleData}
118
+ columns={sampleColumns}
119
+ loading={true}
120
+ />
121
+ );
122
+
123
+ expect(screen.getByTestId('spinner')).toBeInTheDocument();
124
+ });
125
+ });
126
+
127
+ describe('Sorting', () => {
128
+ it('renders sortable columns when sortable is enabled', () => {
129
+ render(
130
+ <DataTable
131
+ data={sampleData}
132
+ columns={sampleColumns}
133
+ sortable={true}
134
+ />
135
+ );
136
+
137
+ const nameHeader = screen.getByText('Name').closest('th');
138
+ expect(nameHeader).toHaveClass('c-data-table__header-cell--sortable');
139
+ });
140
+
141
+ it('calls onSort when column header is clicked', () => {
142
+ const handleSort = vi.fn();
143
+ render(
144
+ <DataTable
145
+ data={sampleData}
146
+ columns={sampleColumns}
147
+ sortable={true}
148
+ onSort={handleSort}
149
+ />
150
+ );
151
+
152
+ const nameHeader = screen.getByText('Name').closest('th');
153
+ fireEvent.click(nameHeader!);
154
+
155
+ expect(handleSort).toHaveBeenCalledWith(
156
+ expect.objectContaining({
157
+ key: 'name',
158
+ direction: 'asc',
159
+ })
160
+ );
161
+ });
162
+ });
163
+
164
+ describe('Filtering', () => {
165
+ it('renders search input when filterable is enabled', () => {
166
+ render(
167
+ <DataTable
168
+ data={sampleData}
169
+ columns={sampleColumns}
170
+ filterable={true}
171
+ />
172
+ );
173
+
174
+ const searchInput = screen.getByPlaceholderText('Search...');
175
+ expect(searchInput).toBeInTheDocument();
176
+ });
177
+
178
+ it('filters data when search query is entered', async () => {
179
+ render(
180
+ <DataTable
181
+ data={sampleData}
182
+ columns={sampleColumns}
183
+ filterable={true}
184
+ />
185
+ );
186
+
187
+ const searchInput = screen.getByPlaceholderText('Search...');
188
+ fireEvent.change(searchInput, { target: { value: 'John' } });
189
+
190
+ await waitFor(() => {
191
+ expect(screen.getByText('John Doe')).toBeInTheDocument();
192
+ expect(screen.queryByText('Jane Smith')).not.toBeInTheDocument();
193
+ });
194
+ });
195
+
196
+ it('renders column filters when columnFilters is enabled', () => {
197
+ render(
198
+ <DataTable
199
+ data={sampleData}
200
+ columns={sampleColumns}
201
+ columnFilters={true}
202
+ />
203
+ );
204
+
205
+ const columnFilters = screen.getAllByPlaceholderText('Filter...');
206
+ expect(columnFilters.length).toBeGreaterThan(0);
207
+ });
208
+ });
209
+
210
+ describe('Pagination', () => {
211
+ it('renders pagination when paginated is enabled', () => {
212
+ render(
213
+ <DataTable
214
+ data={sampleData}
215
+ columns={sampleColumns}
216
+ paginated={true}
217
+ pageSize={2}
218
+ />
219
+ );
220
+
221
+ expect(screen.getByTestId('pagination')).toBeInTheDocument();
222
+ });
223
+
224
+ it('does not render pagination when paginated is false', () => {
225
+ render(
226
+ <DataTable
227
+ data={sampleData}
228
+ columns={sampleColumns}
229
+ paginated={false}
230
+ />
231
+ );
232
+
233
+ expect(screen.queryByTestId('pagination')).not.toBeInTheDocument();
234
+ });
235
+ });
236
+
237
+ describe('Row Selection', () => {
238
+ it('renders selection checkboxes when selectionMode is multiple', () => {
239
+ render(
240
+ <DataTable
241
+ data={sampleData}
242
+ columns={sampleColumns}
243
+ selectionMode="multiple"
244
+ />
245
+ );
246
+
247
+ const checkboxes = screen.getAllByTestId('checkbox');
248
+ expect(checkboxes.length).toBeGreaterThan(0);
249
+ });
250
+
251
+ it('renders select all checkbox in header for multiple selection', () => {
252
+ render(
253
+ <DataTable
254
+ data={sampleData}
255
+ columns={sampleColumns}
256
+ selectionMode="multiple"
257
+ />
258
+ );
259
+
260
+ const checkboxes = screen.getAllByTestId('checkbox');
261
+ // Should have at least one checkbox (select all)
262
+ expect(checkboxes.length).toBeGreaterThanOrEqual(1);
263
+ });
264
+
265
+ it('calls onSelectionChange when row is selected', () => {
266
+ const handleSelectionChange = vi.fn();
267
+ render(
268
+ <DataTable
269
+ data={sampleData}
270
+ columns={sampleColumns}
271
+ selectionMode="multiple"
272
+ onSelectionChange={handleSelectionChange}
273
+ />
274
+ );
275
+
276
+ const checkboxes = screen.getAllByTestId('checkbox');
277
+ // Click the first row checkbox (skip select all if present)
278
+ const rowCheckbox = checkboxes[checkboxes.length > sampleData.length ? 1 : 0];
279
+ fireEvent.change(rowCheckbox, { target: { checked: true } });
280
+
281
+ expect(handleSelectionChange).toHaveBeenCalled();
282
+ });
283
+ });
284
+
285
+ describe('Row Click', () => {
286
+ it('calls onRowClick when row is clicked', () => {
287
+ const handleRowClick = vi.fn();
288
+ render(
289
+ <DataTable
290
+ data={sampleData}
291
+ columns={sampleColumns}
292
+ onRowClick={handleRowClick}
293
+ />
294
+ );
295
+
296
+ const row = screen.getByText('John Doe').closest('tr');
297
+ fireEvent.click(row!);
298
+
299
+ expect(handleRowClick).toHaveBeenCalledWith(sampleData[0]);
300
+ });
301
+ });
302
+
303
+ describe('Export Functionality', () => {
304
+ it('renders export dropdown when exportable is enabled', () => {
305
+ render(
306
+ <DataTable
307
+ data={sampleData}
308
+ columns={sampleColumns}
309
+ exportable={true}
310
+ />
311
+ );
312
+
313
+ const exportButton = screen.getByText('Export');
314
+ expect(exportButton).toBeInTheDocument();
315
+ });
316
+
317
+ it('does not render export when exportable is false', () => {
318
+ render(
319
+ <DataTable
320
+ data={sampleData}
321
+ columns={sampleColumns}
322
+ exportable={false}
323
+ />
324
+ );
325
+
326
+ expect(screen.queryByText('Export')).not.toBeInTheDocument();
327
+ });
328
+ });
329
+
330
+ describe('Column Visibility', () => {
331
+ it('renders column visibility dropdown when showColumnVisibility is enabled', () => {
332
+ render(
333
+ <DataTable
334
+ data={sampleData}
335
+ columns={sampleColumns}
336
+ showColumnVisibility={true}
337
+ />
338
+ );
339
+
340
+ const columnsButton = screen.getByText('Columns');
341
+ expect(columnsButton).toBeInTheDocument();
342
+ });
343
+ });
344
+
345
+ describe('Styling Variants', () => {
346
+ it('applies striped class when striped is enabled', () => {
347
+ const { container } = render(
348
+ <DataTable
349
+ data={sampleData}
350
+ columns={sampleColumns}
351
+ striped={true}
352
+ />
353
+ );
354
+
355
+ const table = container.querySelector('.c-data-table');
356
+ expect(table).toHaveClass('c-data-table--striped');
357
+ });
358
+
359
+ it('applies bordered class when bordered is enabled', () => {
360
+ const { container } = render(
361
+ <DataTable
362
+ data={sampleData}
363
+ columns={sampleColumns}
364
+ bordered={true}
365
+ />
366
+ );
367
+
368
+ const table = container.querySelector('.c-data-table');
369
+ expect(table).toHaveClass('c-data-table--bordered');
370
+ });
371
+
372
+ it('applies dense class when dense is enabled', () => {
373
+ const { container } = render(
374
+ <DataTable
375
+ data={sampleData}
376
+ columns={sampleColumns}
377
+ dense={true}
378
+ />
379
+ );
380
+
381
+ const table = container.querySelector('.c-data-table');
382
+ expect(table).toHaveClass('c-data-table--dense');
383
+ });
384
+
385
+ it('applies sticky header class when stickyHeader is enabled', () => {
386
+ const { container } = render(
387
+ <DataTable
388
+ data={sampleData}
389
+ columns={sampleColumns}
390
+ stickyHeader={true}
391
+ />
392
+ );
393
+
394
+ const table = container.querySelector('.c-data-table');
395
+ expect(table).toHaveClass('c-data-table--sticky-header');
396
+ });
397
+ });
398
+
399
+ describe('Custom Rendering', () => {
400
+ it('uses custom render function for cells', () => {
401
+ const columnsWithRender: DataTableColumn[] = [
402
+ {
403
+ key: 'name',
404
+ title: 'Name',
405
+ render: (value) => <strong>{value}</strong>,
406
+ },
407
+ ];
408
+
409
+ render(
410
+ <DataTable
411
+ data={[{ name: 'John Doe' }]}
412
+ columns={columnsWithRender}
413
+ />
414
+ );
415
+
416
+ const strongElement = screen.getByText('John Doe').closest('strong');
417
+ expect(strongElement).toBeInTheDocument();
418
+ });
419
+ });
420
+
421
+ describe('Accessibility', () => {
422
+ it('applies correct ARIA attributes for sortable columns', () => {
423
+ render(
424
+ <DataTable
425
+ data={sampleData}
426
+ columns={sampleColumns}
427
+ sortable={true}
428
+ />
429
+ );
430
+
431
+ const nameHeader = screen.getByText('Name').closest('th');
432
+ expect(nameHeader).toHaveAttribute('aria-sort');
433
+ });
434
+
435
+ it('applies role="button" to clickable rows', () => {
436
+ const handleRowClick = vi.fn();
437
+ render(
438
+ <DataTable
439
+ data={sampleData}
440
+ columns={sampleColumns}
441
+ onRowClick={handleRowClick}
442
+ />
443
+ );
444
+
445
+ const row = screen.getByText('John Doe').closest('tr');
446
+ expect(row).toHaveAttribute('role', 'button');
447
+ });
448
+ });
449
+ });
450
+