@object-ui/plugin-grid 3.3.0 → 3.3.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/CHANGELOG.md +23 -0
- package/README.md +21 -1
- package/dist/index.js +631 -599
- package/dist/index.umd.cjs +8 -8
- package/package.json +44 -12
- package/.turbo/turbo-build.log +0 -32
- package/src/FormulaBar.tsx +0 -151
- package/src/GroupRow.tsx +0 -69
- package/src/ImportWizard.tsx +0 -412
- package/src/InlineEditing.tsx +0 -235
- package/src/ListColumnExtensions.test.tsx +0 -373
- package/src/ListColumnSchema.test.ts +0 -88
- package/src/ObjectGrid.EdgeCases.stories.tsx +0 -147
- package/src/ObjectGrid.msw.test.tsx +0 -130
- package/src/ObjectGrid.stories.tsx +0 -139
- package/src/ObjectGrid.tsx +0 -1598
- package/src/SplitPaneGrid.tsx +0 -120
- package/src/VirtualGrid.tsx +0 -183
- package/src/__tests__/GroupRow.test.tsx +0 -206
- package/src/__tests__/ImportPreview.test.tsx +0 -171
- package/src/__tests__/InlineEditing.test.tsx +0 -360
- package/src/__tests__/VirtualGrid.test.tsx +0 -438
- package/src/__tests__/accessibility.test.tsx +0 -254
- package/src/__tests__/accessorKey-inference.test.tsx +0 -132
- package/src/__tests__/airtable-style.test.tsx +0 -508
- package/src/__tests__/column-features.test.tsx +0 -490
- package/src/__tests__/grid-export.test.tsx +0 -121
- package/src/__tests__/mobile-card-view.test.tsx +0 -355
- package/src/__tests__/objectdef-enrichment.test.tsx +0 -566
- package/src/__tests__/performance-benchmark.test.tsx +0 -182
- package/src/__tests__/phase11-features.test.tsx +0 -418
- package/src/__tests__/row-bulk-actions.test.tsx +0 -413
- package/src/__tests__/row-height.test.tsx +0 -160
- package/src/__tests__/useGroupedData.test.ts +0 -165
- package/src/__tests__/view-states.test.tsx +0 -203
- package/src/components/BulkActionBar.tsx +0 -66
- package/src/components/RowActionMenu.tsx +0 -91
- package/src/index.test.tsx +0 -29
- package/src/index.tsx +0 -99
- package/src/useCellClipboard.ts +0 -136
- package/src/useColumnSummary.ts +0 -128
- package/src/useGradientColor.ts +0 -103
- package/src/useGroupReorder.ts +0 -123
- package/src/useGroupedData.ts +0 -187
- package/src/useRowColor.ts +0 -74
- package/tsconfig.json +0 -9
- package/vite.config.ts +0 -58
- package/vitest.config.ts +0 -13
- package/vitest.setup.ts +0 -1
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RowActionMenu & BulkActionBar component tests
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
5
|
-
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
6
|
-
import userEvent from '@testing-library/user-event';
|
|
7
|
-
import '@testing-library/jest-dom';
|
|
8
|
-
import React from 'react';
|
|
9
|
-
|
|
10
|
-
import { RowActionMenu, formatActionLabel } from '../components/RowActionMenu';
|
|
11
|
-
import { BulkActionBar } from '../components/BulkActionBar';
|
|
12
|
-
import { ObjectGrid } from '../ObjectGrid';
|
|
13
|
-
import { registerAllFields } from '@object-ui/fields';
|
|
14
|
-
import { ActionProvider } from '@object-ui/react';
|
|
15
|
-
|
|
16
|
-
registerAllFields();
|
|
17
|
-
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Test data
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
const testRow = { id: '1', name: 'Alice', amount: 100 };
|
|
22
|
-
const testData = [
|
|
23
|
-
{ id: '1', name: 'Alice', amount: 100 },
|
|
24
|
-
{ id: '2', name: 'Bob', amount: 200 },
|
|
25
|
-
{ id: '3', name: 'Charlie', amount: 300 },
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// ---------------------------------------------------------------------------
|
|
29
|
-
// Helper
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
function renderGrid(opts?: Record<string, any>) {
|
|
32
|
-
const schema: any = {
|
|
33
|
-
type: 'object-grid' as const,
|
|
34
|
-
objectName: 'test_object',
|
|
35
|
-
columns: [
|
|
36
|
-
{ field: 'name', label: 'Name' },
|
|
37
|
-
{ field: 'amount', label: 'Amount', type: 'number' },
|
|
38
|
-
],
|
|
39
|
-
data: { provider: 'value', items: testData },
|
|
40
|
-
...opts,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return render(
|
|
44
|
-
<ActionProvider>
|
|
45
|
-
<ObjectGrid schema={schema} />
|
|
46
|
-
</ActionProvider>
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// =========================================================================
|
|
51
|
-
// formatActionLabel
|
|
52
|
-
// =========================================================================
|
|
53
|
-
describe('formatActionLabel', () => {
|
|
54
|
-
it('formats single word actions', () => {
|
|
55
|
-
expect(formatActionLabel('delete')).toBe('Delete');
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
it('formats underscore-separated actions', () => {
|
|
59
|
-
expect(formatActionLabel('send_email')).toBe('Send Email');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('formats multi-word actions', () => {
|
|
63
|
-
expect(formatActionLabel('bulk_archive_items')).toBe('Bulk Archive Items');
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// =========================================================================
|
|
68
|
-
// RowActionMenu component
|
|
69
|
-
// =========================================================================
|
|
70
|
-
describe('RowActionMenu', () => {
|
|
71
|
-
it('renders trigger button with "..." icon', () => {
|
|
72
|
-
render(<RowActionMenu row={testRow} rowActions={['archive']} />);
|
|
73
|
-
const trigger = screen.getByTestId('row-action-trigger');
|
|
74
|
-
expect(trigger).toBeInTheDocument();
|
|
75
|
-
expect(screen.getByText('Open menu')).toBeInTheDocument();
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('shows custom row actions in dropdown on click', async () => {
|
|
79
|
-
const user = userEvent.setup();
|
|
80
|
-
render(<RowActionMenu row={testRow} rowActions={['archive', 'send_email']} />);
|
|
81
|
-
|
|
82
|
-
await user.click(screen.getByTestId('row-action-trigger'));
|
|
83
|
-
|
|
84
|
-
await waitFor(() => {
|
|
85
|
-
expect(screen.getByTestId('row-action-archive')).toBeInTheDocument();
|
|
86
|
-
expect(screen.getByTestId('row-action-send_email')).toBeInTheDocument();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
expect(screen.getByText('Archive')).toBeInTheDocument();
|
|
90
|
-
expect(screen.getByText('Send Email')).toBeInTheDocument();
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it('shows edit and delete items when canEdit/canDelete are true', async () => {
|
|
94
|
-
const user = userEvent.setup();
|
|
95
|
-
const onEdit = vi.fn();
|
|
96
|
-
const onDelete = vi.fn();
|
|
97
|
-
|
|
98
|
-
render(
|
|
99
|
-
<RowActionMenu
|
|
100
|
-
row={testRow}
|
|
101
|
-
canEdit
|
|
102
|
-
canDelete
|
|
103
|
-
onEdit={onEdit}
|
|
104
|
-
onDelete={onDelete}
|
|
105
|
-
/>
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
await user.click(screen.getByTestId('row-action-trigger'));
|
|
109
|
-
|
|
110
|
-
await waitFor(() => {
|
|
111
|
-
expect(screen.getByText('Edit')).toBeInTheDocument();
|
|
112
|
-
expect(screen.getByText('Delete')).toBeInTheDocument();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it('calls onEdit with the row when edit is clicked', async () => {
|
|
117
|
-
const user = userEvent.setup();
|
|
118
|
-
const onEdit = vi.fn();
|
|
119
|
-
render(<RowActionMenu row={testRow} canEdit onEdit={onEdit} />);
|
|
120
|
-
|
|
121
|
-
await user.click(screen.getByTestId('row-action-trigger'));
|
|
122
|
-
await waitFor(() => {
|
|
123
|
-
expect(screen.getByText('Edit')).toBeInTheDocument();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
await user.click(screen.getByText('Edit'));
|
|
127
|
-
expect(onEdit).toHaveBeenCalledWith(testRow);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('calls onDelete with the row when delete is clicked', async () => {
|
|
131
|
-
const user = userEvent.setup();
|
|
132
|
-
const onDelete = vi.fn();
|
|
133
|
-
render(<RowActionMenu row={testRow} canDelete onDelete={onDelete} />);
|
|
134
|
-
|
|
135
|
-
await user.click(screen.getByTestId('row-action-trigger'));
|
|
136
|
-
await waitFor(() => {
|
|
137
|
-
expect(screen.getByText('Delete')).toBeInTheDocument();
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
await user.click(screen.getByText('Delete'));
|
|
141
|
-
expect(onDelete).toHaveBeenCalledWith(testRow);
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
it('calls onAction with action name and row', async () => {
|
|
145
|
-
const user = userEvent.setup();
|
|
146
|
-
const onAction = vi.fn();
|
|
147
|
-
render(<RowActionMenu row={testRow} rowActions={['archive']} onAction={onAction} />);
|
|
148
|
-
|
|
149
|
-
await user.click(screen.getByTestId('row-action-trigger'));
|
|
150
|
-
await waitFor(() => {
|
|
151
|
-
expect(screen.getByTestId('row-action-archive')).toBeInTheDocument();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
await user.click(screen.getByTestId('row-action-archive'));
|
|
155
|
-
expect(onAction).toHaveBeenCalledWith('archive', testRow);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('does not show edit/delete when canEdit/canDelete are false', async () => {
|
|
159
|
-
const user = userEvent.setup();
|
|
160
|
-
render(<RowActionMenu row={testRow} rowActions={['archive']} />);
|
|
161
|
-
|
|
162
|
-
await user.click(screen.getByTestId('row-action-trigger'));
|
|
163
|
-
await waitFor(() => {
|
|
164
|
-
expect(screen.getByTestId('row-action-archive')).toBeInTheDocument();
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
expect(screen.queryByText('Edit')).not.toBeInTheDocument();
|
|
168
|
-
expect(screen.queryByText('Delete')).not.toBeInTheDocument();
|
|
169
|
-
});
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// =========================================================================
|
|
173
|
-
// BulkActionBar component
|
|
174
|
-
// =========================================================================
|
|
175
|
-
describe('BulkActionBar', () => {
|
|
176
|
-
it('renders nothing when no rows are selected', () => {
|
|
177
|
-
const { container } = render(
|
|
178
|
-
<BulkActionBar selectedRows={[]} actions={['delete', 'archive']} />
|
|
179
|
-
);
|
|
180
|
-
expect(container.innerHTML).toBe('');
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
it('renders nothing when actions array is empty', () => {
|
|
184
|
-
const { container } = render(
|
|
185
|
-
<BulkActionBar selectedRows={testData} actions={[]} />
|
|
186
|
-
);
|
|
187
|
-
expect(container.innerHTML).toBe('');
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('renders selected count and action buttons', () => {
|
|
191
|
-
render(
|
|
192
|
-
<BulkActionBar selectedRows={testData} actions={['delete', 'archive']} />
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
expect(screen.getByTestId('bulk-actions-bar')).toBeInTheDocument();
|
|
196
|
-
expect(screen.getByText('3 selected')).toBeInTheDocument();
|
|
197
|
-
expect(screen.getByTestId('bulk-action-delete')).toBeInTheDocument();
|
|
198
|
-
expect(screen.getByTestId('bulk-action-archive')).toBeInTheDocument();
|
|
199
|
-
expect(screen.getByText('Delete')).toBeInTheDocument();
|
|
200
|
-
expect(screen.getByText('Archive')).toBeInTheDocument();
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
it('shows Clear button', () => {
|
|
204
|
-
render(
|
|
205
|
-
<BulkActionBar selectedRows={testData} actions={['delete']} />
|
|
206
|
-
);
|
|
207
|
-
expect(screen.getByText('Clear')).toBeInTheDocument();
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
it('calls onAction with action and selected rows', () => {
|
|
211
|
-
const onAction = vi.fn();
|
|
212
|
-
render(
|
|
213
|
-
<BulkActionBar
|
|
214
|
-
selectedRows={testData}
|
|
215
|
-
actions={['delete', 'archive']}
|
|
216
|
-
onAction={onAction}
|
|
217
|
-
/>
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
fireEvent.click(screen.getByTestId('bulk-action-delete'));
|
|
221
|
-
expect(onAction).toHaveBeenCalledWith('delete', testData);
|
|
222
|
-
|
|
223
|
-
fireEvent.click(screen.getByTestId('bulk-action-archive'));
|
|
224
|
-
expect(onAction).toHaveBeenCalledWith('archive', testData);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('calls onClearSelection when Clear is clicked', () => {
|
|
228
|
-
const onClear = vi.fn();
|
|
229
|
-
render(
|
|
230
|
-
<BulkActionBar
|
|
231
|
-
selectedRows={testData}
|
|
232
|
-
actions={['delete']}
|
|
233
|
-
onClearSelection={onClear}
|
|
234
|
-
/>
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
fireEvent.click(screen.getByText('Clear'));
|
|
238
|
-
expect(onClear).toHaveBeenCalled();
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
it('formats action labels correctly', () => {
|
|
242
|
-
render(
|
|
243
|
-
<BulkActionBar selectedRows={[testRow]} actions={['send_email', 'bulk_archive']} />
|
|
244
|
-
);
|
|
245
|
-
|
|
246
|
-
expect(screen.getByText('Send Email')).toBeInTheDocument();
|
|
247
|
-
expect(screen.getByText('Bulk Archive')).toBeInTheDocument();
|
|
248
|
-
});
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
// =========================================================================
|
|
252
|
-
// RowActionMenu integration in ObjectGrid
|
|
253
|
-
// =========================================================================
|
|
254
|
-
describe('RowActionMenu in ObjectGrid', () => {
|
|
255
|
-
it('renders row action triggers when rowActions configured', async () => {
|
|
256
|
-
renderGrid({
|
|
257
|
-
rowActions: ['archive', 'send_email'],
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
await waitFor(() => {
|
|
261
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Each row should have a row action trigger
|
|
265
|
-
const triggers = screen.getAllByTestId('row-action-trigger');
|
|
266
|
-
expect(triggers.length).toBe(3); // 3 data rows
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('shows rowActions items in dropdown on click', async () => {
|
|
270
|
-
const user = userEvent.setup();
|
|
271
|
-
renderGrid({
|
|
272
|
-
rowActions: ['archive', 'send_email'],
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
await waitFor(() => {
|
|
276
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
const triggers = screen.getAllByTestId('row-action-trigger');
|
|
280
|
-
await user.click(triggers[0]);
|
|
281
|
-
|
|
282
|
-
await waitFor(() => {
|
|
283
|
-
expect(screen.getByTestId('row-action-archive')).toBeInTheDocument();
|
|
284
|
-
expect(screen.getByTestId('row-action-send_email')).toBeInTheDocument();
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
it('renders Actions column header when rowActions configured', async () => {
|
|
289
|
-
renderGrid({
|
|
290
|
-
rowActions: ['archive'],
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
await waitFor(() => {
|
|
294
|
-
expect(screen.getByText('Actions')).toBeInTheDocument();
|
|
295
|
-
});
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
it('does not render Actions column when no rowActions and no operations', async () => {
|
|
299
|
-
renderGrid();
|
|
300
|
-
|
|
301
|
-
await waitFor(() => {
|
|
302
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
expect(screen.queryByText('Actions')).not.toBeInTheDocument();
|
|
306
|
-
});
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
// =========================================================================
|
|
310
|
-
// BulkActionBar integration in ObjectGrid
|
|
311
|
-
// =========================================================================
|
|
312
|
-
describe('BulkActionBar in ObjectGrid', () => {
|
|
313
|
-
it('does not render bulk actions bar when no batchActions configured', async () => {
|
|
314
|
-
renderGrid();
|
|
315
|
-
|
|
316
|
-
await waitFor(() => {
|
|
317
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
expect(screen.queryByTestId('bulk-actions-bar')).not.toBeInTheDocument();
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
it('does not render bulk actions bar when batchActions configured but no rows selected', async () => {
|
|
324
|
-
renderGrid({
|
|
325
|
-
batchActions: ['delete', 'archive'],
|
|
326
|
-
selection: { type: 'multiple' },
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
await waitFor(() => {
|
|
330
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
expect(screen.queryByTestId('bulk-actions-bar')).not.toBeInTheDocument();
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
it('also accepts bulkActions as an alias for batchActions', async () => {
|
|
337
|
-
renderGrid({
|
|
338
|
-
bulkActions: ['export'],
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
await waitFor(() => {
|
|
342
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
// Without selection, bar will not appear, but schema acceptance is verified
|
|
346
|
-
expect(screen.queryByTestId('bulk-actions-bar')).not.toBeInTheDocument();
|
|
347
|
-
});
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
// =========================================================================
|
|
351
|
-
// onRowSelect callback propagation
|
|
352
|
-
// =========================================================================
|
|
353
|
-
describe('ObjectGrid onRowSelect callback', () => {
|
|
354
|
-
it('accepts onRowSelect prop and renders with selection enabled', async () => {
|
|
355
|
-
const onRowSelect = vi.fn();
|
|
356
|
-
const schema: any = {
|
|
357
|
-
type: 'object-grid' as const,
|
|
358
|
-
objectName: 'test_object',
|
|
359
|
-
columns: [
|
|
360
|
-
{ field: 'name', label: 'Name' },
|
|
361
|
-
{ field: 'amount', label: 'Amount', type: 'number' },
|
|
362
|
-
],
|
|
363
|
-
data: { provider: 'value', items: testData },
|
|
364
|
-
selection: { type: 'multiple' },
|
|
365
|
-
};
|
|
366
|
-
|
|
367
|
-
render(
|
|
368
|
-
<ActionProvider>
|
|
369
|
-
<ObjectGrid schema={schema} onRowSelect={onRowSelect} />
|
|
370
|
-
</ActionProvider>
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
await waitFor(() => {
|
|
374
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
375
|
-
});
|
|
376
|
-
|
|
377
|
-
// Grid renders checkboxes when selection is enabled
|
|
378
|
-
const checkboxes = screen.getAllByRole('checkbox');
|
|
379
|
-
expect(checkboxes.length).toBeGreaterThan(0);
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it('wires onRowSelect to internal onSelectionChange', async () => {
|
|
383
|
-
const onRowSelect = vi.fn();
|
|
384
|
-
const schema: any = {
|
|
385
|
-
type: 'object-grid' as const,
|
|
386
|
-
objectName: 'test_object',
|
|
387
|
-
columns: [
|
|
388
|
-
{ field: 'name', label: 'Name' },
|
|
389
|
-
],
|
|
390
|
-
data: { provider: 'value', items: testData },
|
|
391
|
-
selection: { type: 'multiple' },
|
|
392
|
-
};
|
|
393
|
-
|
|
394
|
-
render(
|
|
395
|
-
<ActionProvider>
|
|
396
|
-
<ObjectGrid schema={schema} onRowSelect={onRowSelect} />
|
|
397
|
-
</ActionProvider>
|
|
398
|
-
);
|
|
399
|
-
|
|
400
|
-
await waitFor(() => {
|
|
401
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
// Click the "select all" header checkbox to trigger selection
|
|
405
|
-
const checkboxes = screen.getAllByRole('checkbox');
|
|
406
|
-
fireEvent.click(checkboxes[0]); // First checkbox is typically "select all"
|
|
407
|
-
|
|
408
|
-
// onRowSelect should have been invoked via onSelectionChange
|
|
409
|
-
await waitFor(() => {
|
|
410
|
-
expect(onRowSelect).toHaveBeenCalled();
|
|
411
|
-
});
|
|
412
|
-
});
|
|
413
|
-
});
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectGrid Row Height Tests
|
|
3
|
-
*
|
|
4
|
-
* Validates all 5 rowHeight enum values ('compact' | 'short' | 'medium' | 'tall' | 'extra_tall')
|
|
5
|
-
* are correctly supported in state initialization, cell class mapping, cycle toggle, and icon selection.
|
|
6
|
-
*/
|
|
7
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
8
|
-
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
9
|
-
import '@testing-library/jest-dom';
|
|
10
|
-
import React from 'react';
|
|
11
|
-
import { ObjectGrid } from '../ObjectGrid';
|
|
12
|
-
import { registerAllFields } from '@object-ui/fields';
|
|
13
|
-
import { ActionProvider } from '@object-ui/react';
|
|
14
|
-
|
|
15
|
-
registerAllFields();
|
|
16
|
-
|
|
17
|
-
const mockData = [
|
|
18
|
-
{ id: '1', name: 'Alice', role: 'Engineer' },
|
|
19
|
-
{ id: '2', name: 'Bob', role: 'Designer' },
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
function renderGrid(opts?: Record<string, any>) {
|
|
23
|
-
const schema: any = {
|
|
24
|
-
type: 'object-grid' as const,
|
|
25
|
-
objectName: 'test_object',
|
|
26
|
-
columns: [
|
|
27
|
-
{ field: 'name', label: 'Name' },
|
|
28
|
-
{ field: 'role', label: 'Role' },
|
|
29
|
-
],
|
|
30
|
-
data: { provider: 'value', items: mockData },
|
|
31
|
-
...opts,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return render(
|
|
35
|
-
<ActionProvider>
|
|
36
|
-
<ObjectGrid schema={schema} />
|
|
37
|
-
</ActionProvider>
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// =========================================================================
|
|
42
|
-
// Row height toggle visibility
|
|
43
|
-
// =========================================================================
|
|
44
|
-
describe('Row height toggle visibility', () => {
|
|
45
|
-
it('should show row height toggle when rowHeight is set in schema', async () => {
|
|
46
|
-
renderGrid({ rowHeight: 'medium' });
|
|
47
|
-
|
|
48
|
-
await waitFor(() => {
|
|
49
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
expect(screen.getByTitle(/Row height:/)).toBeInTheDocument();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should not show row height toggle when rowHeight is not set', async () => {
|
|
56
|
-
renderGrid();
|
|
57
|
-
|
|
58
|
-
await waitFor(() => {
|
|
59
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
expect(screen.queryByTitle(/Row height:/)).not.toBeInTheDocument();
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// =========================================================================
|
|
67
|
-
// Row height initialization for all 5 enum values
|
|
68
|
-
// =========================================================================
|
|
69
|
-
describe('Row height initialization', () => {
|
|
70
|
-
const allHeights = ['compact', 'short', 'medium', 'tall', 'extra_tall'] as const;
|
|
71
|
-
|
|
72
|
-
allHeights.forEach((height) => {
|
|
73
|
-
it(`should initialize with rowHeight="${height}"`, async () => {
|
|
74
|
-
renderGrid({ rowHeight: height });
|
|
75
|
-
|
|
76
|
-
await waitFor(() => {
|
|
77
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
const toggle = screen.getByTitle(`Row height: ${height}`);
|
|
81
|
-
expect(toggle).toBeInTheDocument();
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
// =========================================================================
|
|
87
|
-
// Row height cycle through all 5 values
|
|
88
|
-
// =========================================================================
|
|
89
|
-
describe('Row height cycle', () => {
|
|
90
|
-
it('should cycle through all 5 heights: compact → short → medium → tall → extra_tall → compact', async () => {
|
|
91
|
-
renderGrid({ rowHeight: 'compact' });
|
|
92
|
-
|
|
93
|
-
await waitFor(() => {
|
|
94
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
const getToggle = () => screen.getByTitle(/Row height:/);
|
|
98
|
-
|
|
99
|
-
// Start: compact
|
|
100
|
-
expect(getToggle()).toHaveAttribute('title', 'Row height: compact');
|
|
101
|
-
|
|
102
|
-
// Click: compact → short
|
|
103
|
-
fireEvent.click(getToggle());
|
|
104
|
-
expect(getToggle()).toHaveAttribute('title', 'Row height: short');
|
|
105
|
-
|
|
106
|
-
// Click: short → medium
|
|
107
|
-
fireEvent.click(getToggle());
|
|
108
|
-
expect(getToggle()).toHaveAttribute('title', 'Row height: medium');
|
|
109
|
-
|
|
110
|
-
// Click: medium → tall
|
|
111
|
-
fireEvent.click(getToggle());
|
|
112
|
-
expect(getToggle()).toHaveAttribute('title', 'Row height: tall');
|
|
113
|
-
|
|
114
|
-
// Click: tall → extra_tall
|
|
115
|
-
fireEvent.click(getToggle());
|
|
116
|
-
expect(getToggle()).toHaveAttribute('title', 'Row height: extra_tall');
|
|
117
|
-
|
|
118
|
-
// Click: extra_tall → compact (wraps around)
|
|
119
|
-
fireEvent.click(getToggle());
|
|
120
|
-
expect(getToggle()).toHaveAttribute('title', 'Row height: compact');
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// =========================================================================
|
|
125
|
-
// Row height label display
|
|
126
|
-
// =========================================================================
|
|
127
|
-
describe('Row height label display', () => {
|
|
128
|
-
const allHeights = ['compact', 'short', 'medium', 'tall', 'extra_tall'] as const;
|
|
129
|
-
|
|
130
|
-
allHeights.forEach((height) => {
|
|
131
|
-
it(`should display "${height}" label in the toggle button`, async () => {
|
|
132
|
-
renderGrid({ rowHeight: height });
|
|
133
|
-
|
|
134
|
-
await waitFor(() => {
|
|
135
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
// The label text is rendered as-is in the DOM (CSS `capitalize` handles visual casing)
|
|
139
|
-
const toggle = screen.getByTitle(`Row height: ${height}`);
|
|
140
|
-
expect(toggle).toHaveTextContent(height);
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
// =========================================================================
|
|
146
|
-
// Default rowHeight fallback
|
|
147
|
-
// =========================================================================
|
|
148
|
-
describe('Row height default', () => {
|
|
149
|
-
it('should default to "medium" when rowHeight is defined but empty', async () => {
|
|
150
|
-
// When rowHeight is present (truthy or not), toggle shows.
|
|
151
|
-
// When schema.rowHeight is undefined, no toggle is shown — default 'medium' is internal.
|
|
152
|
-
renderGrid({ rowHeight: 'medium' });
|
|
153
|
-
|
|
154
|
-
await waitFor(() => {
|
|
155
|
-
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
expect(screen.getByTitle('Row height: medium')).toBeInTheDocument();
|
|
159
|
-
});
|
|
160
|
-
});
|