@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.
- package/README.md +3 -3
- package/dist/atomix.css +77 -0
- package/dist/atomix.css.map +1 -1
- package/dist/atomix.min.css +77 -0
- package/dist/atomix.min.css.map +1 -1
- package/dist/charts.js +50 -142
- package/dist/charts.js.map +1 -1
- package/dist/core.d.ts +2 -2
- package/dist/core.js +179 -274
- package/dist/core.js.map +1 -1
- package/dist/forms.js +50 -142
- package/dist/forms.js.map +1 -1
- package/dist/heavy.js +179 -274
- package/dist/heavy.js.map +1 -1
- package/dist/index.d.ts +1255 -1226
- package/dist/index.esm.js +2806 -2958
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +3113 -3269
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/theme.d.ts +313 -667
- package/dist/theme.js +1818 -2589
- package/dist/theme.js.map +1 -1
- package/package.json +1 -1
- package/src/components/AtomixGlass/AtomixGlass.tsx +128 -356
- package/src/components/AtomixGlass/AtomixGlassContainer.tsx +1 -1
- package/src/components/Button/Button.tsx +85 -167
- package/src/components/DataTable/DataTable.stories.tsx +238 -0
- package/src/components/DataTable/DataTable.test.tsx +450 -0
- package/src/components/DataTable/DataTable.tsx +384 -61
- package/src/components/DatePicker/DatePicker.tsx +29 -38
- package/src/components/Upload/Upload.tsx +539 -40
- package/src/lib/composables/useAtomixGlass.ts +7 -7
- package/src/lib/composables/useDataTable.ts +355 -15
- package/src/lib/composables/useDatePicker.ts +19 -0
- package/src/lib/config/loader.ts +2 -3
- package/src/lib/constants/components.ts +17 -0
- package/src/lib/hooks/usePerformanceMonitor.ts +1 -1
- package/src/lib/theme/adapters/cssVariableMapper.ts +29 -14
- package/src/lib/theme/adapters/index.ts +1 -4
- package/src/lib/theme/config/configLoader.ts +82 -223
- package/src/lib/theme/config/loader.ts +15 -21
- package/src/lib/theme/constants/constants.ts +1 -1
- package/src/lib/theme/core/ThemeRegistry.ts +75 -279
- package/src/lib/theme/core/composeTheme.ts +30 -88
- package/src/lib/theme/core/createTheme.ts +88 -51
- package/src/lib/theme/core/createThemeObject.ts +2 -2
- package/src/lib/theme/core/index.ts +15 -2
- package/src/lib/theme/errors/errors.ts +1 -1
- package/src/lib/theme/generators/generateCSSNested.ts +131 -0
- package/src/lib/theme/generators/generateCSSVariables.ts +24 -16
- package/src/lib/theme/generators/index.ts +6 -0
- package/src/lib/theme/index.ts +45 -27
- package/src/lib/theme/runtime/ThemeApplicator.ts +6 -109
- package/src/lib/theme/runtime/ThemeErrorBoundary.tsx +1 -1
- package/src/lib/theme/runtime/ThemeProvider.tsx +393 -544
- package/src/lib/theme/runtime/index.ts +1 -0
- package/src/lib/theme/runtime/useTheme.ts +1 -1
- package/src/lib/theme/runtime/useThemeTokens.ts +122 -0
- package/src/lib/theme/test/testTheme.ts +2 -1
- package/src/lib/theme/types.ts +14 -14
- package/src/lib/theme/utils/componentTheming.ts +140 -0
- package/src/lib/theme/utils/domUtils.ts +57 -15
- package/src/lib/theme/utils/injectCSS.ts +0 -1
- package/src/lib/theme/utils/naming.ts +100 -0
- package/src/lib/theme/utils/themeHelpers.ts +1 -39
- package/src/lib/theme/utils/themeUtils.ts +1 -170
- package/src/lib/types/components.ts +145 -0
- package/src/lib/utils/componentUtils.ts +1 -1
- package/src/lib/utils/dataTableExport.ts +143 -0
- package/src/lib/utils/memoryMonitor.ts +3 -3
- package/src/lib/utils/themeNaming.ts +135 -0
- 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
|
+
|