@object-ui/plugin-grid 0.3.1 → 2.0.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.
- package/.turbo/turbo-build.log +30 -0
- package/CHANGELOG.md +15 -0
- package/README.md +97 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1117 -303
- package/dist/index.umd.cjs +5 -2
- package/dist/packages/plugin-grid/src/ListColumnExtensions.test.d.ts +0 -0
- package/dist/packages/plugin-grid/src/ListColumnSchema.test.d.ts +1 -0
- package/dist/{plugin-grid → packages/plugin-grid}/src/ObjectGrid.d.ts +7 -1
- package/dist/packages/plugin-grid/src/ObjectGrid.msw.test.d.ts +0 -0
- package/dist/packages/plugin-grid/src/VirtualGrid.d.ts +35 -0
- package/dist/packages/plugin-grid/src/VirtualGrid.test.d.ts +8 -0
- package/dist/packages/plugin-grid/src/__tests__/VirtualGrid.test.d.ts +0 -0
- package/dist/packages/plugin-grid/src/index.d.ts +10 -0
- package/dist/packages/plugin-grid/src/index.test.d.ts +1 -0
- package/package.json +11 -8
- package/src/ListColumnExtensions.test.tsx +374 -0
- package/src/ListColumnSchema.test.ts +88 -0
- package/src/ObjectGrid.msw.test.tsx +130 -0
- package/src/ObjectGrid.tsx +341 -117
- package/src/VirtualGrid.test.tsx +23 -0
- package/src/VirtualGrid.tsx +183 -0
- package/src/__tests__/VirtualGrid.test.tsx +438 -0
- package/src/index.test.tsx +29 -0
- package/src/index.tsx +33 -5
- package/vite.config.ts +18 -0
- package/vitest.config.ts +13 -0
- package/vitest.setup.ts +1 -0
- package/dist/plugin-grid/src/index.d.ts +0 -3
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ListColumn Zod Schema Tests
|
|
3
|
+
*
|
|
4
|
+
* Verifies that the ListColumnSchema Zod definition includes link and action properties.
|
|
5
|
+
*/
|
|
6
|
+
import { describe, it, expect } from 'vitest';
|
|
7
|
+
import { ListColumnSchema } from '@object-ui/types/zod';
|
|
8
|
+
|
|
9
|
+
describe('ListColumnSchema (Zod)', () => {
|
|
10
|
+
it('should accept link property', () => {
|
|
11
|
+
const result = ListColumnSchema.safeParse({
|
|
12
|
+
field: 'name',
|
|
13
|
+
link: true,
|
|
14
|
+
});
|
|
15
|
+
expect(result.success).toBe(true);
|
|
16
|
+
if (result.success) {
|
|
17
|
+
expect(result.data.link).toBe(true);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should accept action property', () => {
|
|
22
|
+
const result = ListColumnSchema.safeParse({
|
|
23
|
+
field: 'status',
|
|
24
|
+
action: 'toggleStatus',
|
|
25
|
+
});
|
|
26
|
+
expect(result.success).toBe(true);
|
|
27
|
+
if (result.success) {
|
|
28
|
+
expect(result.data.action).toBe('toggleStatus');
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should accept all properties together', () => {
|
|
33
|
+
const result = ListColumnSchema.safeParse({
|
|
34
|
+
field: 'name',
|
|
35
|
+
label: 'Full Name',
|
|
36
|
+
width: 200,
|
|
37
|
+
align: 'left',
|
|
38
|
+
hidden: false,
|
|
39
|
+
sortable: true,
|
|
40
|
+
resizable: true,
|
|
41
|
+
wrap: false,
|
|
42
|
+
type: 'text',
|
|
43
|
+
link: true,
|
|
44
|
+
action: 'viewDetail',
|
|
45
|
+
});
|
|
46
|
+
expect(result.success).toBe(true);
|
|
47
|
+
if (result.success) {
|
|
48
|
+
expect(result.data.link).toBe(true);
|
|
49
|
+
expect(result.data.action).toBe('viewDetail');
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should make link optional', () => {
|
|
54
|
+
const result = ListColumnSchema.safeParse({
|
|
55
|
+
field: 'name',
|
|
56
|
+
});
|
|
57
|
+
expect(result.success).toBe(true);
|
|
58
|
+
if (result.success) {
|
|
59
|
+
expect(result.data.link).toBeUndefined();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should make action optional', () => {
|
|
64
|
+
const result = ListColumnSchema.safeParse({
|
|
65
|
+
field: 'name',
|
|
66
|
+
});
|
|
67
|
+
expect(result.success).toBe(true);
|
|
68
|
+
if (result.success) {
|
|
69
|
+
expect(result.data.action).toBeUndefined();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should reject non-boolean link', () => {
|
|
74
|
+
const result = ListColumnSchema.safeParse({
|
|
75
|
+
field: 'name',
|
|
76
|
+
link: 'yes',
|
|
77
|
+
});
|
|
78
|
+
expect(result.success).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should reject non-string action', () => {
|
|
82
|
+
const result = ListColumnSchema.safeParse({
|
|
83
|
+
field: 'name',
|
|
84
|
+
action: 42,
|
|
85
|
+
});
|
|
86
|
+
expect(result.success).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeAll, afterAll, afterEach } from 'vitest';
|
|
2
|
+
import { render, screen, waitFor, within } from '@testing-library/react';
|
|
3
|
+
import '@testing-library/jest-dom';
|
|
4
|
+
import { ObjectGrid } from './ObjectGrid';
|
|
5
|
+
import { ObjectStackAdapter } from '@object-ui/data-objectstack';
|
|
6
|
+
import { setupServer } from 'msw/node';
|
|
7
|
+
import { http, HttpResponse } from 'msw';
|
|
8
|
+
import { registerAllFields } from '@object-ui/fields';
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { ContactObject } from '../../../examples/crm/src/objects/contact.object';
|
|
11
|
+
|
|
12
|
+
registerAllFields();
|
|
13
|
+
|
|
14
|
+
const BASE_URL = process.env.OBJECTSTACK_API_URL || 'http://localhost';
|
|
15
|
+
|
|
16
|
+
// --- Mock Data ---
|
|
17
|
+
|
|
18
|
+
const mockSchema = ContactObject;
|
|
19
|
+
|
|
20
|
+
const mockData = {
|
|
21
|
+
value: [
|
|
22
|
+
{ _id: '1', name: 'John Doe', email: 'john@example.com', company: 'Acme Inc' },
|
|
23
|
+
{ _id: '2', name: 'Jane Smith', email: 'jane@test.com', company: 'Globex' },
|
|
24
|
+
{ _id: '3', name: 'Bob Wilson', email: 'bob@test.com', company: 'Acme Inc' }
|
|
25
|
+
],
|
|
26
|
+
count: 3,
|
|
27
|
+
'@odata.count': 3
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// --- MSW Setup ---
|
|
31
|
+
|
|
32
|
+
const handlers = [
|
|
33
|
+
// .well-known discovery endpoint (used by client.connect())
|
|
34
|
+
http.get(`${BASE_URL}/.well-known/objectstack`, () => {
|
|
35
|
+
return HttpResponse.json({
|
|
36
|
+
name: 'ObjectStack API',
|
|
37
|
+
version: '1.0',
|
|
38
|
+
endpoints: {
|
|
39
|
+
data: '/api/v1/data',
|
|
40
|
+
metadata: '/api/v1/meta'
|
|
41
|
+
},
|
|
42
|
+
capabilities: {
|
|
43
|
+
graphql: false,
|
|
44
|
+
search: false,
|
|
45
|
+
websockets: false,
|
|
46
|
+
files: true,
|
|
47
|
+
analytics: false,
|
|
48
|
+
hub: false
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
// OPTIONS handler for CORS preflight
|
|
54
|
+
http.options(`${BASE_URL}/*`, () => {
|
|
55
|
+
return new HttpResponse(null, {
|
|
56
|
+
status: 200,
|
|
57
|
+
headers: {
|
|
58
|
+
'Access-Control-Allow-Origin': '*',
|
|
59
|
+
'Access-Control-Allow-Methods': 'GET,HEAD,POST,PUT,DELETE,CONNECT,OPTIONS,TRACE,PATCH',
|
|
60
|
+
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
}),
|
|
64
|
+
|
|
65
|
+
// Health check / Connection check
|
|
66
|
+
http.get(`${BASE_URL}/api/v1`, () => {
|
|
67
|
+
return HttpResponse.json({ status: 'ok', version: '1.0.0' });
|
|
68
|
+
}),
|
|
69
|
+
|
|
70
|
+
// Schema: /api/v1/metadata/object/:name and /api/v1/meta/object/:name (client uses /meta)
|
|
71
|
+
http.get(`${BASE_URL}/api/v1/metadata/object/contact`, () => {
|
|
72
|
+
return HttpResponse.json(mockSchema);
|
|
73
|
+
}),
|
|
74
|
+
http.get(`${BASE_URL}/api/v1/meta/object/contact`, () => {
|
|
75
|
+
return HttpResponse.json(mockSchema);
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
// Data Query: /api/v1/data/contact
|
|
79
|
+
http.get(`${BASE_URL}/api/v1/data/contact`, () => {
|
|
80
|
+
return HttpResponse.json(mockData);
|
|
81
|
+
})
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const server = setupServer(...handlers);
|
|
85
|
+
|
|
86
|
+
// --- Test Suite ---
|
|
87
|
+
|
|
88
|
+
describe('ObjectGrid with ObjectStack/MSW', () => {
|
|
89
|
+
// Only start MSW if we are NOT using a real server
|
|
90
|
+
if (!process.env.OBJECTSTACK_API_URL) {
|
|
91
|
+
beforeAll(() => server.listen());
|
|
92
|
+
afterEach(() => server.resetHandlers());
|
|
93
|
+
afterAll(() => server.close());
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const dataSource = new ObjectStackAdapter({
|
|
97
|
+
baseUrl: BASE_URL,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('loads schema and data rows', async () => {
|
|
101
|
+
render(
|
|
102
|
+
<ObjectGrid
|
|
103
|
+
schema={{
|
|
104
|
+
type: 'object-grid',
|
|
105
|
+
objectName: 'contact',
|
|
106
|
+
columns: ['name', 'email', 'company'] // Explicit columns or auto-derived
|
|
107
|
+
}}
|
|
108
|
+
dataSource={dataSource}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Verify Column Headers (from schema or props)
|
|
113
|
+
await waitFor(() => {
|
|
114
|
+
// Changed from 'Full Name' to 'Name' to match CRM example schema
|
|
115
|
+
expect(screen.getByText('Name')).toBeInTheDocument();
|
|
116
|
+
});
|
|
117
|
+
expect(screen.getByText('Email')).toBeInTheDocument();
|
|
118
|
+
expect(screen.getByText('Company')).toBeInTheDocument();
|
|
119
|
+
|
|
120
|
+
// Verify Data Rows
|
|
121
|
+
await waitFor(() => {
|
|
122
|
+
expect(screen.getByText('John Doe')).toBeInTheDocument();
|
|
123
|
+
});
|
|
124
|
+
expect(screen.getByText('jane@test.com')).toBeInTheDocument();
|
|
125
|
+
expect(screen.getByText('Globex')).toBeInTheDocument();
|
|
126
|
+
|
|
127
|
+
// Check Row Count (if grid displays it)
|
|
128
|
+
// expect(screen.getByText(/3 records/i)).toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
});
|