@object-ui/plugin-grid 3.1.5 → 3.3.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.
- package/CHANGELOG.md +34 -0
- package/README.md +21 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +649 -623
- package/dist/index.umd.cjs +8 -8
- package/package.json +45 -13
- 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 -1596
- 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 -57
- package/vitest.config.ts +0 -13
- package/vitest.setup.ts +0 -1
- /package/dist/{plugin-grid → packages/plugin-grid}/src/FormulaBar.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/GroupRow.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/ImportWizard.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/InlineEditing.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/ObjectGrid.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/SplitPaneGrid.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/VirtualGrid.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/components/BulkActionBar.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/components/RowActionMenu.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/index.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/useCellClipboard.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/useColumnSummary.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/useGradientColor.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/useGroupReorder.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/useGroupedData.d.ts +0 -0
- /package/dist/{plugin-grid → packages/plugin-grid}/src/useRowColor.d.ts +0 -0
|
@@ -1,566 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ObjectDef Field Enrichment Tests
|
|
3
|
-
*
|
|
4
|
-
* Tests that ObjectGrid properly merges objectDef field definitions
|
|
5
|
-
* into column metadata for type-aware rendering, covering:
|
|
6
|
-
* - String[] columns enriched with objectDef field types
|
|
7
|
-
* - ListColumn[] columns enriched with objectDef options (with colors)
|
|
8
|
-
* - Currency, percent, date, select field formatting from objectDef
|
|
9
|
-
*/
|
|
10
|
-
import { describe, it, expect, vi } from 'vitest';
|
|
11
|
-
import { render, screen, waitFor } from '@testing-library/react';
|
|
12
|
-
import '@testing-library/jest-dom';
|
|
13
|
-
import React from 'react';
|
|
14
|
-
|
|
15
|
-
import { ObjectGrid } from '../ObjectGrid';
|
|
16
|
-
import { registerAllFields } from '@object-ui/fields';
|
|
17
|
-
import { ActionProvider } from '@object-ui/react';
|
|
18
|
-
|
|
19
|
-
registerAllFields();
|
|
20
|
-
|
|
21
|
-
// --- Opportunity-like mock data ---
|
|
22
|
-
const opportunityData = [
|
|
23
|
-
{ id: '1', name: 'Enterprise License', amount: 150000, stage: 'closed_won', close_date: '2024-01-15T00:00:00.000Z', probability: 100, forecast_category: 'commit' },
|
|
24
|
-
{ id: '2', name: 'E-Com Integration', amount: 12000, stage: 'closed_lost', close_date: '2024-02-10T00:00:00.000Z', probability: 0, forecast_category: 'omitted' },
|
|
25
|
-
{ id: '3', name: 'Tower Expansion', amount: 75000, stage: 'negotiation', close_date: '2024-02-28T00:00:00.000Z', probability: 80, forecast_category: 'best_case' },
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
// --- Mock objectSchema that resembles the Opportunity object definition ---
|
|
29
|
-
const opportunitySchema = {
|
|
30
|
-
name: 'opportunity',
|
|
31
|
-
fields: {
|
|
32
|
-
name: { name: 'name', type: 'text', label: 'Opportunity Name', required: true },
|
|
33
|
-
amount: { name: 'amount', type: 'currency', label: 'Amount', currency: 'USD' },
|
|
34
|
-
stage: {
|
|
35
|
-
name: 'stage', type: 'select', label: 'Stage',
|
|
36
|
-
options: [
|
|
37
|
-
{ value: 'prospecting', label: 'Prospecting', color: 'purple' },
|
|
38
|
-
{ value: 'qualification', label: 'Qualification', color: 'indigo' },
|
|
39
|
-
{ value: 'proposal', label: 'Proposal', color: 'blue' },
|
|
40
|
-
{ value: 'negotiation', label: 'Negotiation', color: 'yellow' },
|
|
41
|
-
{ value: 'closed_won', label: 'Closed Won', color: 'green' },
|
|
42
|
-
{ value: 'closed_lost', label: 'Closed Lost', color: 'red' },
|
|
43
|
-
],
|
|
44
|
-
},
|
|
45
|
-
close_date: { name: 'close_date', type: 'date', label: 'Close Date' },
|
|
46
|
-
probability: { name: 'probability', type: 'percent', label: 'Probability' },
|
|
47
|
-
forecast_category: {
|
|
48
|
-
name: 'forecast_category', type: 'select', label: 'Forecast Category',
|
|
49
|
-
options: [
|
|
50
|
-
{ value: 'pipeline', label: 'Pipeline', color: 'blue' },
|
|
51
|
-
{ value: 'best_case', label: 'Best Case', color: 'green' },
|
|
52
|
-
{ value: 'commit', label: 'Commit', color: 'purple' },
|
|
53
|
-
{ value: 'omitted', label: 'Omitted', color: 'gray' },
|
|
54
|
-
],
|
|
55
|
-
},
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
// Mock DataSource for async schema fetch tests
|
|
60
|
-
function createMockDataSource(schema: any, data: any[]) {
|
|
61
|
-
return {
|
|
62
|
-
getObjectSchema: vi.fn().mockResolvedValue(schema),
|
|
63
|
-
find: vi.fn().mockResolvedValue({ data }),
|
|
64
|
-
create: vi.fn(),
|
|
65
|
-
update: vi.fn(),
|
|
66
|
-
delete: vi.fn(),
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ---------------------------------------------------------------------------
|
|
71
|
-
// Helper
|
|
72
|
-
// ---------------------------------------------------------------------------
|
|
73
|
-
function renderGrid(opts: Record<string, any>) {
|
|
74
|
-
const schema: any = {
|
|
75
|
-
type: 'object-grid' as const,
|
|
76
|
-
objectName: 'opportunity',
|
|
77
|
-
data: { provider: 'value', items: opportunityData },
|
|
78
|
-
...opts,
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
return render(
|
|
82
|
-
<ActionProvider>
|
|
83
|
-
<ObjectGrid schema={schema} {...(opts.dataSource ? { dataSource: opts.dataSource } : {})} />
|
|
84
|
-
</ActionProvider>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// =========================================================================
|
|
89
|
-
// String[] columns with inline data + objectSchema stub
|
|
90
|
-
// =========================================================================
|
|
91
|
-
describe('String[] columns with inline data', () => {
|
|
92
|
-
it('should apply type inference for string columns (currency, date, percent, select)', async () => {
|
|
93
|
-
// With inline data, objectSchema is not fetched, so inference applies
|
|
94
|
-
renderGrid({
|
|
95
|
-
columns: ['name', 'amount', 'stage', 'close_date', 'probability'],
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
await waitFor(() => {
|
|
99
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Amount should be formatted (inference detects "amount" as currency)
|
|
103
|
-
await waitFor(() => {
|
|
104
|
-
expect(screen.getByText('$150,000.00')).toBeInTheDocument();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// Stage should be rendered as badge (inference detects "stage" as select)
|
|
108
|
-
await waitFor(() => {
|
|
109
|
-
// Inferred select auto-generates options from data with humanized labels
|
|
110
|
-
const badge = screen.getByText('Closed Won');
|
|
111
|
-
expect(badge).toBeInTheDocument();
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should right-align numeric columns for string[] format', async () => {
|
|
116
|
-
renderGrid({
|
|
117
|
-
columns: ['name', 'amount', 'probability'],
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
await waitFor(() => {
|
|
121
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// Amount column should have right alignment (currency type)
|
|
125
|
-
await waitFor(() => {
|
|
126
|
-
const amountCell = screen.getByText('$150,000.00');
|
|
127
|
-
expect(amountCell).toBeInTheDocument();
|
|
128
|
-
});
|
|
129
|
-
});
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
// =========================================================================
|
|
133
|
-
// ListColumn[] with objectDef enrichment (inline data + objectSchema set)
|
|
134
|
-
// =========================================================================
|
|
135
|
-
describe('ListColumn[] columns with objectDef enrichment', () => {
|
|
136
|
-
it('should render select fields with colored badges from objectDef options', async () => {
|
|
137
|
-
// Use ListColumn[] with type: 'select' but no options — objectDef should provide them
|
|
138
|
-
// For inline data, objectSchema isn't fetched, so we simulate by passing options in ListColumn
|
|
139
|
-
renderGrid({
|
|
140
|
-
columns: [
|
|
141
|
-
{ field: 'name', label: 'Name' },
|
|
142
|
-
{
|
|
143
|
-
field: 'stage', label: 'Stage', type: 'select',
|
|
144
|
-
options: [
|
|
145
|
-
{ value: 'closed_won', label: 'Closed Won', color: 'green' },
|
|
146
|
-
{ value: 'closed_lost', label: 'Closed Lost', color: 'red' },
|
|
147
|
-
{ value: 'negotiation', label: 'Negotiation', color: 'yellow' },
|
|
148
|
-
],
|
|
149
|
-
},
|
|
150
|
-
],
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
await waitFor(() => {
|
|
154
|
-
expect(screen.getByText('Closed Won')).toBeInTheDocument();
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
// Verify colored badge rendering
|
|
158
|
-
const closedWonBadge = screen.getByText('Closed Won');
|
|
159
|
-
expect(closedWonBadge).toHaveClass('bg-green-100');
|
|
160
|
-
|
|
161
|
-
const closedLostBadge = screen.getByText('Closed Lost');
|
|
162
|
-
expect(closedLostBadge).toHaveClass('bg-red-100');
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
it('should render currency fields with proper formatting from objectDef', async () => {
|
|
166
|
-
renderGrid({
|
|
167
|
-
columns: [
|
|
168
|
-
{ field: 'name', label: 'Name' },
|
|
169
|
-
{ field: 'amount', label: 'Amount', type: 'currency' },
|
|
170
|
-
],
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
await waitFor(() => {
|
|
174
|
-
expect(screen.getByText('$150,000.00')).toBeInTheDocument();
|
|
175
|
-
expect(screen.getByText('$12,000.00')).toBeInTheDocument();
|
|
176
|
-
expect(screen.getByText('$75,000.00')).toBeInTheDocument();
|
|
177
|
-
});
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
it('should render percent fields with % formatting', async () => {
|
|
181
|
-
renderGrid({
|
|
182
|
-
columns: [
|
|
183
|
-
{ field: 'name', label: 'Name' },
|
|
184
|
-
{ field: 'probability', label: 'Probability', type: 'percent' },
|
|
185
|
-
],
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
await waitFor(() => {
|
|
189
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
// Percent fields should show percentage values
|
|
193
|
-
await waitFor(() => {
|
|
194
|
-
const percentTexts = screen.getAllByText(/%/);
|
|
195
|
-
expect(percentTexts.length).toBeGreaterThan(0);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// =========================================================================
|
|
201
|
-
// DataSource-based schema fetch with string[] columns
|
|
202
|
-
// =========================================================================
|
|
203
|
-
describe('String[] columns with DataSource schema fetch', () => {
|
|
204
|
-
it('should fetch full schema and use objectDef field types for string[] columns', async () => {
|
|
205
|
-
const mockDataSource = createMockDataSource(opportunitySchema, opportunityData);
|
|
206
|
-
|
|
207
|
-
const schema: any = {
|
|
208
|
-
type: 'object-grid' as const,
|
|
209
|
-
objectName: 'opportunity',
|
|
210
|
-
columns: ['name', 'amount', 'stage', 'close_date', 'probability', 'forecast_category'],
|
|
211
|
-
data: { provider: 'object', object: 'opportunity' },
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
render(
|
|
215
|
-
<ActionProvider>
|
|
216
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
217
|
-
</ActionProvider>
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
// Wait for schema and data to load
|
|
221
|
-
await waitFor(() => {
|
|
222
|
-
expect(mockDataSource.getObjectSchema).toHaveBeenCalledWith('opportunity');
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
await waitFor(() => {
|
|
226
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Amount should be formatted as currency (from objectDef type: 'currency')
|
|
230
|
-
await waitFor(() => {
|
|
231
|
-
expect(screen.getByText('$150,000.00')).toBeInTheDocument();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
// Stage should render with label from objectDef options
|
|
235
|
-
await waitFor(() => {
|
|
236
|
-
expect(screen.getByText('Closed Won')).toBeInTheDocument();
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// Stage badge should have color from objectDef options
|
|
240
|
-
const closedWonBadge = screen.getByText('Closed Won');
|
|
241
|
-
expect(closedWonBadge).toHaveClass('bg-green-100');
|
|
242
|
-
|
|
243
|
-
// Forecast category should also render with colored badges
|
|
244
|
-
const commitBadge = screen.getByText('Commit');
|
|
245
|
-
expect(commitBadge).toHaveClass('bg-purple-100');
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
it('should use objectDef labels for string[] column headers', async () => {
|
|
249
|
-
const mockDataSource = createMockDataSource(opportunitySchema, opportunityData);
|
|
250
|
-
|
|
251
|
-
const schema: any = {
|
|
252
|
-
type: 'object-grid' as const,
|
|
253
|
-
objectName: 'opportunity',
|
|
254
|
-
columns: ['name', 'amount', 'close_date'],
|
|
255
|
-
data: { provider: 'object', object: 'opportunity' },
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
render(
|
|
259
|
-
<ActionProvider>
|
|
260
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
261
|
-
</ActionProvider>
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
await waitFor(() => {
|
|
265
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
// Headers should use objectDef labels
|
|
269
|
-
expect(screen.getByText('Opportunity Name')).toBeInTheDocument();
|
|
270
|
-
expect(screen.getByText('Amount')).toBeInTheDocument();
|
|
271
|
-
expect(screen.getByText('Close Date')).toBeInTheDocument();
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
// =========================================================================
|
|
276
|
-
// ListColumn[] with DataSource schema fetch (objectDef merge)
|
|
277
|
-
// =========================================================================
|
|
278
|
-
describe('ListColumn[] with DataSource objectDef merge', () => {
|
|
279
|
-
it('should merge objectDef options into ListColumn fieldMeta for colored badges', async () => {
|
|
280
|
-
const mockDataSource = createMockDataSource(opportunitySchema, opportunityData);
|
|
281
|
-
|
|
282
|
-
const schema: any = {
|
|
283
|
-
type: 'object-grid' as const,
|
|
284
|
-
objectName: 'opportunity',
|
|
285
|
-
// ListColumn without options — objectDef should provide them
|
|
286
|
-
columns: [
|
|
287
|
-
{ field: 'name', label: 'Name' },
|
|
288
|
-
{ field: 'stage', label: 'Stage' },
|
|
289
|
-
{ field: 'forecast_category', label: 'Forecast' },
|
|
290
|
-
],
|
|
291
|
-
data: { provider: 'object', object: 'opportunity' },
|
|
292
|
-
};
|
|
293
|
-
|
|
294
|
-
render(
|
|
295
|
-
<ActionProvider>
|
|
296
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
297
|
-
</ActionProvider>
|
|
298
|
-
);
|
|
299
|
-
|
|
300
|
-
await waitFor(() => {
|
|
301
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
// Stage badges should have colors from objectDef
|
|
305
|
-
await waitFor(() => {
|
|
306
|
-
const closedWonBadge = screen.getByText('Closed Won');
|
|
307
|
-
expect(closedWonBadge).toHaveClass('bg-green-100');
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
// Forecast badges should have colors from objectDef
|
|
311
|
-
const commitBadge = screen.getByText('Commit');
|
|
312
|
-
expect(commitBadge).toHaveClass('bg-purple-100');
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
it('should prefer explicit ListColumn options over objectDef options', async () => {
|
|
316
|
-
const mockDataSource = createMockDataSource(opportunitySchema, opportunityData);
|
|
317
|
-
|
|
318
|
-
const schema: any = {
|
|
319
|
-
type: 'object-grid' as const,
|
|
320
|
-
objectName: 'opportunity',
|
|
321
|
-
columns: [
|
|
322
|
-
{ field: 'name', label: 'Name' },
|
|
323
|
-
{
|
|
324
|
-
field: 'stage', label: 'Stage',
|
|
325
|
-
// Explicit options override objectDef
|
|
326
|
-
options: [
|
|
327
|
-
{ value: 'closed_won', label: 'Won!', color: 'blue' },
|
|
328
|
-
{ value: 'closed_lost', label: 'Lost', color: 'gray' },
|
|
329
|
-
{ value: 'negotiation', label: 'Negotiating', color: 'orange' },
|
|
330
|
-
],
|
|
331
|
-
},
|
|
332
|
-
],
|
|
333
|
-
data: { provider: 'object', object: 'opportunity' },
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
render(
|
|
337
|
-
<ActionProvider>
|
|
338
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
339
|
-
</ActionProvider>
|
|
340
|
-
);
|
|
341
|
-
|
|
342
|
-
await waitFor(() => {
|
|
343
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
// Should use explicit ListColumn options, not objectDef
|
|
347
|
-
await waitFor(() => {
|
|
348
|
-
expect(screen.getByText('Won!')).toBeInTheDocument();
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
const wonBadge = screen.getByText('Won!');
|
|
352
|
-
expect(wonBadge).toHaveClass('bg-blue-100');
|
|
353
|
-
});
|
|
354
|
-
});
|
|
355
|
-
|
|
356
|
-
// =========================================================================
|
|
357
|
-
// Inline data + DataSource: schema should be fetched for type-aware rendering
|
|
358
|
-
// (Regression test for: inline data skipping objectSchema load)
|
|
359
|
-
// =========================================================================
|
|
360
|
-
describe('Inline data with DataSource schema fetch', () => {
|
|
361
|
-
it('should fetch objectSchema even when data is inline (provider: value)', async () => {
|
|
362
|
-
const mockDataSource = createMockDataSource(opportunitySchema, []);
|
|
363
|
-
|
|
364
|
-
const schema: any = {
|
|
365
|
-
type: 'object-grid' as const,
|
|
366
|
-
objectName: 'opportunity',
|
|
367
|
-
data: { provider: 'value', items: opportunityData },
|
|
368
|
-
columns: ['name', 'amount', 'stage', 'close_date', 'probability'],
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
render(
|
|
372
|
-
<ActionProvider>
|
|
373
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
374
|
-
</ActionProvider>
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
// Schema should be fetched even with inline data
|
|
378
|
-
await waitFor(() => {
|
|
379
|
-
expect(mockDataSource.getObjectSchema).toHaveBeenCalledWith('opportunity');
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
// Data should render
|
|
383
|
-
await waitFor(() => {
|
|
384
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
385
|
-
});
|
|
386
|
-
|
|
387
|
-
// Amount should be formatted as currency using objectSchema field type
|
|
388
|
-
await waitFor(() => {
|
|
389
|
-
expect(screen.getByText('$150,000.00')).toBeInTheDocument();
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
// Stage should render with labels from objectSchema options
|
|
393
|
-
await waitFor(() => {
|
|
394
|
-
expect(screen.getByText('Closed Won')).toBeInTheDocument();
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
// Stage badge should have color from objectSchema
|
|
398
|
-
const closedWonBadge = screen.getByText('Closed Won');
|
|
399
|
-
expect(closedWonBadge).toHaveClass('bg-green-100');
|
|
400
|
-
|
|
401
|
-
// find() should NOT have been called (data is inline)
|
|
402
|
-
expect(mockDataSource.find).not.toHaveBeenCalled();
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it('should use objectSchema labels for column headers with inline data', async () => {
|
|
406
|
-
const mockDataSource = createMockDataSource(opportunitySchema, []);
|
|
407
|
-
|
|
408
|
-
const schema: any = {
|
|
409
|
-
type: 'object-grid' as const,
|
|
410
|
-
objectName: 'opportunity',
|
|
411
|
-
data: { provider: 'value', items: opportunityData },
|
|
412
|
-
columns: ['name', 'amount', 'close_date'],
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
render(
|
|
416
|
-
<ActionProvider>
|
|
417
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
418
|
-
</ActionProvider>
|
|
419
|
-
);
|
|
420
|
-
|
|
421
|
-
await waitFor(() => {
|
|
422
|
-
expect(mockDataSource.getObjectSchema).toHaveBeenCalledWith('opportunity');
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// Headers should use objectSchema labels (not raw field names)
|
|
426
|
-
await waitFor(() => {
|
|
427
|
-
expect(screen.getByText('Opportunity Name')).toBeInTheDocument();
|
|
428
|
-
});
|
|
429
|
-
expect(screen.getByText('Amount')).toBeInTheDocument();
|
|
430
|
-
expect(screen.getByText('Close Date')).toBeInTheDocument();
|
|
431
|
-
});
|
|
432
|
-
|
|
433
|
-
it('should render lookup/select fields with CellRenderers when inline data + objectSchema', async () => {
|
|
434
|
-
const lookupSchema = {
|
|
435
|
-
name: 'order',
|
|
436
|
-
fields: {
|
|
437
|
-
name: { name: 'name', type: 'text', label: 'Order' },
|
|
438
|
-
status: {
|
|
439
|
-
name: 'status', type: 'select', label: 'Status',
|
|
440
|
-
options: [
|
|
441
|
-
{ value: 'pending', label: 'Pending', color: 'yellow' },
|
|
442
|
-
{ value: 'shipped', label: 'Shipped', color: 'blue' },
|
|
443
|
-
{ value: 'delivered', label: 'Delivered', color: 'green' },
|
|
444
|
-
],
|
|
445
|
-
},
|
|
446
|
-
assigned_to: { name: 'assigned_to', type: 'lookup', label: 'Assigned To' },
|
|
447
|
-
},
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
const inlineOrderData = [
|
|
451
|
-
{ id: 'o1', name: 'Order 001', status: 'pending', assigned_to: 'Alice' },
|
|
452
|
-
{ id: 'o2', name: 'Order 002', status: 'shipped', assigned_to: 'Bob' },
|
|
453
|
-
{ id: 'o3', name: 'Order 003', status: 'delivered', assigned_to: 'Charlie' },
|
|
454
|
-
];
|
|
455
|
-
|
|
456
|
-
const mockDataSource = createMockDataSource(lookupSchema, []);
|
|
457
|
-
|
|
458
|
-
const schema: any = {
|
|
459
|
-
type: 'object-grid' as const,
|
|
460
|
-
objectName: 'order',
|
|
461
|
-
data: { provider: 'value', items: inlineOrderData },
|
|
462
|
-
columns: ['name', 'status', 'assigned_to'],
|
|
463
|
-
};
|
|
464
|
-
|
|
465
|
-
render(
|
|
466
|
-
<ActionProvider>
|
|
467
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
468
|
-
</ActionProvider>
|
|
469
|
-
);
|
|
470
|
-
|
|
471
|
-
// Schema should be fetched
|
|
472
|
-
await waitFor(() => {
|
|
473
|
-
expect(mockDataSource.getObjectSchema).toHaveBeenCalledWith('order');
|
|
474
|
-
});
|
|
475
|
-
|
|
476
|
-
// Status should render with select renderer (colored badges)
|
|
477
|
-
await waitFor(() => {
|
|
478
|
-
expect(screen.getByText('Pending')).toBeInTheDocument();
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
const pendingBadge = screen.getByText('Pending');
|
|
482
|
-
expect(pendingBadge).toHaveClass('bg-yellow-100');
|
|
483
|
-
|
|
484
|
-
const shippedBadge = screen.getByText('Shipped');
|
|
485
|
-
expect(shippedBadge).toHaveClass('bg-blue-100');
|
|
486
|
-
|
|
487
|
-
// find() should NOT be called
|
|
488
|
-
expect(mockDataSource.find).not.toHaveBeenCalled();
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
it('should enrich legacy inline data fallback (no columns) with objectSchema', async () => {
|
|
492
|
-
const mockDataSource = createMockDataSource(opportunitySchema, []);
|
|
493
|
-
|
|
494
|
-
const schema: any = {
|
|
495
|
-
type: 'object-grid' as const,
|
|
496
|
-
objectName: 'opportunity',
|
|
497
|
-
data: { provider: 'value', items: opportunityData },
|
|
498
|
-
// No columns specified — should auto-derive from data keys + objectSchema
|
|
499
|
-
};
|
|
500
|
-
|
|
501
|
-
render(
|
|
502
|
-
<ActionProvider>
|
|
503
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
504
|
-
</ActionProvider>
|
|
505
|
-
);
|
|
506
|
-
|
|
507
|
-
// Schema should still be fetched
|
|
508
|
-
await waitFor(() => {
|
|
509
|
-
expect(mockDataSource.getObjectSchema).toHaveBeenCalledWith('opportunity');
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// Data should render with objectSchema-enriched columns
|
|
513
|
-
await waitFor(() => {
|
|
514
|
-
expect(screen.getByText('Enterprise License')).toBeInTheDocument();
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
// Amount should be formatted (objectSchema type: currency)
|
|
518
|
-
await waitFor(() => {
|
|
519
|
-
expect(screen.getByText('$150,000.00')).toBeInTheDocument();
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
// find() should NOT be called
|
|
523
|
-
expect(mockDataSource.find).not.toHaveBeenCalled();
|
|
524
|
-
});
|
|
525
|
-
|
|
526
|
-
it('should enrich ListColumn[] with objectSchema types when inline data + dataSource', async () => {
|
|
527
|
-
const mockDataSource = createMockDataSource(opportunitySchema, []);
|
|
528
|
-
|
|
529
|
-
const schema: any = {
|
|
530
|
-
type: 'object-grid' as const,
|
|
531
|
-
objectName: 'opportunity',
|
|
532
|
-
data: { provider: 'value', items: opportunityData },
|
|
533
|
-
// ListColumn[] without explicit type — objectSchema should provide types
|
|
534
|
-
columns: [
|
|
535
|
-
{ field: 'name', label: 'Name' },
|
|
536
|
-
{ field: 'stage', label: 'Stage' },
|
|
537
|
-
{ field: 'amount', label: 'Amount' },
|
|
538
|
-
],
|
|
539
|
-
};
|
|
540
|
-
|
|
541
|
-
render(
|
|
542
|
-
<ActionProvider>
|
|
543
|
-
<ObjectGrid schema={schema} dataSource={mockDataSource} />
|
|
544
|
-
</ActionProvider>
|
|
545
|
-
);
|
|
546
|
-
|
|
547
|
-
// Schema should be fetched
|
|
548
|
-
await waitFor(() => {
|
|
549
|
-
expect(mockDataSource.getObjectSchema).toHaveBeenCalledWith('opportunity');
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
// Stage should render with colored badge from objectSchema select options
|
|
553
|
-
await waitFor(() => {
|
|
554
|
-
const closedWonBadge = screen.getByText('Closed Won');
|
|
555
|
-
expect(closedWonBadge).toHaveClass('bg-green-100');
|
|
556
|
-
});
|
|
557
|
-
|
|
558
|
-
// Amount should be formatted as currency from objectSchema type
|
|
559
|
-
await waitFor(() => {
|
|
560
|
-
expect(screen.getByText('$150,000.00')).toBeInTheDocument();
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
// find() should NOT be called
|
|
564
|
-
expect(mockDataSource.find).not.toHaveBeenCalled();
|
|
565
|
-
});
|
|
566
|
-
});
|