@object-ui/types 0.3.0 → 0.5.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/README.md +19 -11
- package/dist/api-types.d.ts +7 -0
- package/dist/api-types.d.ts.map +1 -1
- package/dist/api-types.js +4 -6
- package/dist/app.d.ts +17 -0
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +4 -3
- package/dist/base.d.ts +7 -0
- package/dist/base.d.ts.map +1 -1
- package/dist/base.js +4 -6
- package/dist/blocks.d.ts +332 -0
- package/dist/blocks.d.ts.map +1 -0
- package/dist/blocks.js +8 -0
- package/dist/complex.d.ts +68 -1
- package/dist/complex.d.ts.map +1 -1
- package/dist/complex.js +4 -5
- package/dist/crud.d.ts +181 -3
- package/dist/crud.d.ts.map +1 -1
- package/dist/crud.js +4 -6
- package/dist/data-display.d.ts +54 -2
- package/dist/data-display.d.ts.map +1 -1
- package/dist/data-display.js +4 -5
- package/dist/data-protocol.d.ts +1268 -0
- package/dist/data-protocol.d.ts.map +1 -0
- package/dist/data-protocol.js +8 -0
- package/dist/data.d.ts +22 -1
- package/dist/data.d.ts.map +1 -1
- package/dist/data.js +4 -6
- package/dist/disclosure.d.ts +70 -1
- package/dist/disclosure.d.ts.map +1 -1
- package/dist/disclosure.js +4 -5
- package/dist/feedback.d.ts +68 -1
- package/dist/feedback.d.ts.map +1 -1
- package/dist/feedback.js +4 -5
- package/dist/field-types.d.ts +728 -0
- package/dist/field-types.d.ts.map +1 -0
- package/dist/field-types.js +8 -0
- package/dist/form.d.ts +123 -1
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +4 -5
- package/dist/index.d.ts +48 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -37
- package/dist/layout.d.ts +66 -16
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +4 -6
- package/dist/navigation.d.ts +102 -2
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +4 -5
- package/dist/objectql.d.ts +491 -54
- package/dist/objectql.d.ts.map +1 -1
- package/dist/objectql.js +4 -6
- package/dist/overlay.d.ts +31 -1
- package/dist/overlay.d.ts.map +1 -1
- package/dist/overlay.js +4 -5
- package/dist/plugin-scope.d.ts +194 -0
- package/dist/plugin-scope.d.ts.map +1 -0
- package/dist/plugin-scope.js +8 -0
- package/dist/registry.d.ts +7 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +7 -0
- package/dist/reports.d.ts +336 -0
- package/dist/reports.d.ts.map +1 -0
- package/dist/reports.js +8 -0
- package/dist/theme.d.ts +289 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +8 -0
- package/dist/ui-action.d.ts +175 -0
- package/dist/ui-action.d.ts.map +1 -0
- package/dist/ui-action.js +8 -0
- package/dist/views.d.ts +417 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +8 -0
- package/dist/zod/app.zod.d.ts +120 -0
- package/dist/zod/app.zod.d.ts.map +1 -0
- package/dist/zod/app.zod.js +60 -0
- package/dist/zod/base.zod.d.ts +202 -0
- package/dist/zod/base.zod.d.ts.map +1 -0
- package/dist/zod/base.zod.js +198 -0
- package/dist/zod/blocks.zod.d.ts +834 -0
- package/dist/zod/blocks.zod.d.ts.map +1 -0
- package/dist/zod/blocks.zod.js +145 -0
- package/dist/zod/complex.zod.d.ts +742 -0
- package/dist/zod/complex.zod.d.ts.map +1 -0
- package/dist/zod/complex.zod.js +233 -0
- package/dist/zod/crud.zod.d.ts +598 -0
- package/dist/zod/crud.zod.d.ts.map +1 -0
- package/dist/zod/crud.zod.js +230 -0
- package/dist/zod/data-display.zod.d.ts +996 -0
- package/dist/zod/data-display.zod.d.ts.map +1 -0
- package/dist/zod/data-display.zod.js +266 -0
- package/dist/zod/disclosure.zod.d.ts +267 -0
- package/dist/zod/disclosure.zod.d.ts.map +1 -0
- package/dist/zod/disclosure.zod.js +84 -0
- package/dist/zod/feedback.zod.d.ts +538 -0
- package/dist/zod/feedback.zod.d.ts.map +1 -0
- package/dist/zod/feedback.zod.js +127 -0
- package/dist/zod/form.zod.d.ts +1308 -0
- package/dist/zod/form.zod.d.ts.map +1 -0
- package/dist/zod/form.zod.js +406 -0
- package/dist/zod/index.zod.d.ts +4985 -0
- package/dist/zod/index.zod.d.ts.map +1 -0
- package/dist/zod/index.zod.js +183 -0
- package/dist/zod/layout.zod.d.ts +1048 -0
- package/dist/zod/layout.zod.d.ts.map +1 -0
- package/dist/zod/layout.zod.js +241 -0
- package/dist/zod/navigation.zod.d.ts +486 -0
- package/dist/zod/navigation.zod.d.ts.map +1 -0
- package/dist/zod/navigation.zod.js +142 -0
- package/dist/zod/objectql.zod.d.ts +1261 -0
- package/dist/zod/objectql.zod.d.ts.map +1 -0
- package/dist/zod/objectql.zod.js +248 -0
- package/dist/zod/overlay.zod.d.ts +691 -0
- package/dist/zod/overlay.zod.d.ts.map +1 -0
- package/dist/zod/overlay.zod.js +179 -0
- package/dist/zod/reports.zod.d.ts +1628 -0
- package/dist/zod/reports.zod.d.ts.map +1 -0
- package/dist/zod/reports.zod.js +152 -0
- package/dist/zod/theme.zod.d.ts +611 -0
- package/dist/zod/theme.zod.d.ts.map +1 -0
- package/dist/zod/theme.zod.js +130 -0
- package/dist/zod/views.zod.d.ts +675 -0
- package/dist/zod/views.zod.d.ts.map +1 -0
- package/dist/zod/views.zod.js +159 -0
- package/package.json +9 -1
- package/src/__tests__/namespace-exports.test.ts +80 -0
- package/src/__tests__/phase2-schemas.test.ts +639 -0
- package/src/api-types.ts +8 -0
- package/src/app.ts +20 -0
- package/src/base.ts +8 -0
- package/src/blocks.ts +405 -0
- package/src/complex.ts +69 -1
- package/src/crud.ts +185 -3
- package/src/data-display.ts +60 -2
- package/src/data-protocol.ts +1679 -0
- package/src/data.ts +21 -1
- package/src/disclosure.ts +74 -1
- package/src/feedback.ts +76 -2
- package/src/field-types.ts +846 -0
- package/src/form.ts +131 -1
- package/src/index.ts +305 -8
- package/src/layout.ts +70 -15
- package/src/navigation.ts +109 -2
- package/src/objectql.ts +563 -59
- package/src/overlay.ts +35 -1
- package/src/plugin-scope.ts +210 -0
- package/src/registry.ts +8 -0
- package/src/reports.ts +408 -0
- package/src/theme.ts +351 -0
- package/src/ui-action.ts +276 -0
- package/src/views.ts +429 -0
- package/src/zod/README.md +329 -0
- package/src/zod/app.zod.ts +72 -0
- package/src/zod/base.zod.ts +229 -0
- package/src/zod/blocks.zod.ts +170 -0
- package/src/zod/complex.zod.ts +258 -0
- package/src/zod/crud.zod.ts +259 -0
- package/src/zod/data-display.zod.ts +290 -0
- package/src/zod/disclosure.zod.ts +92 -0
- package/src/zod/feedback.zod.ts +138 -0
- package/src/zod/form.zod.ts +434 -0
- package/src/zod/index.zod.ts +425 -0
- package/src/zod/layout.zod.ts +262 -0
- package/src/zod/navigation.zod.ts +159 -0
- package/src/zod/objectql.zod.ts +268 -0
- package/src/zod/overlay.zod.ts +196 -0
- package/src/zod/reports.zod.ts +183 -0
- package/src/zod/theme.zod.ts +155 -0
- package/src/zod/views.zod.ts +182 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Phase 2 Schema Definitions
|
|
3
|
+
* Testing AppSchema, ThemeSchema, ReportSchema, BlockSchema, and Enhanced ActionSchema
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect } from 'vitest';
|
|
6
|
+
import {
|
|
7
|
+
AppSchema,
|
|
8
|
+
AppActionSchema,
|
|
9
|
+
AppMenuItemSchema,
|
|
10
|
+
ThemeSchema,
|
|
11
|
+
ThemeSwitcherSchema,
|
|
12
|
+
ThemePreviewSchema,
|
|
13
|
+
ReportSchema,
|
|
14
|
+
ReportBuilderSchema,
|
|
15
|
+
ReportViewerSchema,
|
|
16
|
+
BlockSchema,
|
|
17
|
+
BlockLibrarySchema,
|
|
18
|
+
BlockEditorSchema,
|
|
19
|
+
BlockInstanceSchema,
|
|
20
|
+
ActionSchema,
|
|
21
|
+
ActionExecutionModeSchema,
|
|
22
|
+
ActionCallbackSchema,
|
|
23
|
+
ActionConditionSchema,
|
|
24
|
+
CRUDSchema,
|
|
25
|
+
DetailViewSchema,
|
|
26
|
+
ViewSwitcherSchema,
|
|
27
|
+
FilterUISchema,
|
|
28
|
+
SortUISchema,
|
|
29
|
+
AnyComponentSchema,
|
|
30
|
+
} from '../zod/index.zod';
|
|
31
|
+
|
|
32
|
+
describe('Phase 2: AppSchema Zod Validation', () => {
|
|
33
|
+
it('should validate a complete AppSchema', () => {
|
|
34
|
+
const appConfig = {
|
|
35
|
+
type: 'app',
|
|
36
|
+
name: 'my-crm',
|
|
37
|
+
title: 'My CRM Application',
|
|
38
|
+
description: 'Customer Relationship Management System',
|
|
39
|
+
logo: '/logo.png',
|
|
40
|
+
favicon: '/favicon.ico',
|
|
41
|
+
layout: 'sidebar',
|
|
42
|
+
menu: [
|
|
43
|
+
{
|
|
44
|
+
type: 'item',
|
|
45
|
+
label: 'Dashboard',
|
|
46
|
+
icon: 'LayoutDashboard',
|
|
47
|
+
path: '/dashboard',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
type: 'group',
|
|
51
|
+
label: 'Sales',
|
|
52
|
+
children: [
|
|
53
|
+
{
|
|
54
|
+
type: 'item',
|
|
55
|
+
label: 'Leads',
|
|
56
|
+
icon: 'Users',
|
|
57
|
+
path: '/leads',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'item',
|
|
61
|
+
label: 'Opportunities',
|
|
62
|
+
icon: 'Target',
|
|
63
|
+
path: '/opportunities',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
actions: [
|
|
69
|
+
{
|
|
70
|
+
type: 'user',
|
|
71
|
+
label: 'John Doe',
|
|
72
|
+
avatar: '/avatar.jpg',
|
|
73
|
+
description: 'john@example.com',
|
|
74
|
+
items: [
|
|
75
|
+
{ type: 'item', label: 'Profile', path: '/profile' },
|
|
76
|
+
{ type: 'item', label: 'Settings', path: '/settings' },
|
|
77
|
+
{ type: 'separator' },
|
|
78
|
+
{ type: 'item', label: 'Logout', path: '/logout' },
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const result = AppSchema.safeParse(appConfig);
|
|
85
|
+
expect(result.success).toBe(true);
|
|
86
|
+
if (result.success) {
|
|
87
|
+
expect(result.data.type).toBe('app');
|
|
88
|
+
expect(result.data.layout).toBe('sidebar');
|
|
89
|
+
expect(result.data.menu).toHaveLength(2);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should validate minimal AppSchema', () => {
|
|
94
|
+
const minimal = {
|
|
95
|
+
type: 'app',
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const result = AppSchema.safeParse(minimal);
|
|
99
|
+
expect(result.success).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('should reject invalid layout value', () => {
|
|
103
|
+
const invalid = {
|
|
104
|
+
type: 'app',
|
|
105
|
+
layout: 'invalid-layout',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const result = AppSchema.safeParse(invalid);
|
|
109
|
+
expect(result.success).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('Phase 2: ThemeSchema Zod Validation', () => {
|
|
114
|
+
it('should validate a complete ThemeSchema', () => {
|
|
115
|
+
const theme = {
|
|
116
|
+
type: 'theme',
|
|
117
|
+
mode: 'dark',
|
|
118
|
+
activeTheme: 'professional',
|
|
119
|
+
themes: [
|
|
120
|
+
{
|
|
121
|
+
name: 'professional',
|
|
122
|
+
label: 'Professional',
|
|
123
|
+
light: {
|
|
124
|
+
primary: '#3b82f6',
|
|
125
|
+
secondary: '#64748b',
|
|
126
|
+
background: '#ffffff',
|
|
127
|
+
foreground: '#0f172a',
|
|
128
|
+
},
|
|
129
|
+
dark: {
|
|
130
|
+
primary: '#60a5fa',
|
|
131
|
+
secondary: '#94a3b8',
|
|
132
|
+
background: '#0f172a',
|
|
133
|
+
foreground: '#f1f5f9',
|
|
134
|
+
},
|
|
135
|
+
typography: {
|
|
136
|
+
fontSans: ['Inter', 'sans-serif'],
|
|
137
|
+
fontSize: 16,
|
|
138
|
+
lineHeight: 1.5,
|
|
139
|
+
},
|
|
140
|
+
radius: {
|
|
141
|
+
default: '0.5rem',
|
|
142
|
+
lg: '1rem',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
allowSwitching: true,
|
|
147
|
+
persistPreference: true,
|
|
148
|
+
storageKey: 'app-theme',
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const result = ThemeSchema.safeParse(theme);
|
|
152
|
+
expect(result.success).toBe(true);
|
|
153
|
+
if (result.success) {
|
|
154
|
+
expect(result.data.mode).toBe('dark');
|
|
155
|
+
expect(result.data.themes).toHaveLength(1);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should validate ThemeSwitcherSchema', () => {
|
|
160
|
+
const switcher = {
|
|
161
|
+
type: 'theme-switcher',
|
|
162
|
+
variant: 'dropdown',
|
|
163
|
+
showMode: true,
|
|
164
|
+
showThemes: true,
|
|
165
|
+
lightIcon: 'Sun',
|
|
166
|
+
darkIcon: 'Moon',
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const result = ThemeSwitcherSchema.safeParse(switcher);
|
|
170
|
+
expect(result.success).toBe(true);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
describe('Phase 2: ReportSchema Zod Validation', () => {
|
|
175
|
+
it('should validate a complete ReportSchema', () => {
|
|
176
|
+
const report = {
|
|
177
|
+
type: 'report',
|
|
178
|
+
title: 'Monthly Sales Report',
|
|
179
|
+
description: 'Sales performance for the month',
|
|
180
|
+
fields: [
|
|
181
|
+
{
|
|
182
|
+
name: 'total_sales',
|
|
183
|
+
label: 'Total Sales',
|
|
184
|
+
type: 'number',
|
|
185
|
+
aggregation: 'sum',
|
|
186
|
+
format: 'currency',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'customer_count',
|
|
190
|
+
label: 'Customers',
|
|
191
|
+
type: 'number',
|
|
192
|
+
aggregation: 'count',
|
|
193
|
+
},
|
|
194
|
+
],
|
|
195
|
+
filters: [
|
|
196
|
+
{
|
|
197
|
+
field: 'date',
|
|
198
|
+
operator: 'between',
|
|
199
|
+
values: ['2024-01-01', '2024-01-31'],
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
groupBy: [
|
|
203
|
+
{
|
|
204
|
+
field: 'region',
|
|
205
|
+
label: 'Region',
|
|
206
|
+
sort: 'asc',
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
sections: [
|
|
210
|
+
{
|
|
211
|
+
type: 'summary',
|
|
212
|
+
title: 'Summary',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
type: 'chart',
|
|
216
|
+
title: 'Sales Trend',
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
type: 'table',
|
|
220
|
+
title: 'Detailed Data',
|
|
221
|
+
},
|
|
222
|
+
],
|
|
223
|
+
schedule: {
|
|
224
|
+
enabled: true,
|
|
225
|
+
frequency: 'monthly',
|
|
226
|
+
dayOfMonth: 1,
|
|
227
|
+
time: '09:00',
|
|
228
|
+
recipients: ['manager@example.com'],
|
|
229
|
+
formats: ['pdf', 'excel'],
|
|
230
|
+
},
|
|
231
|
+
showExportButtons: true,
|
|
232
|
+
showPrintButton: true,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
const result = ReportSchema.safeParse(report);
|
|
236
|
+
expect(result.success).toBe(true);
|
|
237
|
+
if (result.success) {
|
|
238
|
+
expect(result.data.fields).toHaveLength(2);
|
|
239
|
+
expect(result.data.schedule?.frequency).toBe('monthly');
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('should validate ReportBuilderSchema', () => {
|
|
244
|
+
const builder = {
|
|
245
|
+
type: 'report-builder',
|
|
246
|
+
showPreview: true,
|
|
247
|
+
onSave: 'handleSave',
|
|
248
|
+
onCancel: 'handleCancel',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const result = ReportBuilderSchema.safeParse(builder);
|
|
252
|
+
expect(result.success).toBe(true);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe('Phase 2: BlockSchema Zod Validation', () => {
|
|
257
|
+
it('should validate a complete BlockSchema', () => {
|
|
258
|
+
const block = {
|
|
259
|
+
type: 'block',
|
|
260
|
+
meta: {
|
|
261
|
+
name: 'hero-section',
|
|
262
|
+
label: 'Hero Section',
|
|
263
|
+
description: 'A customizable hero section with image and text',
|
|
264
|
+
category: 'Marketing',
|
|
265
|
+
icon: 'Layout',
|
|
266
|
+
tags: ['hero', 'landing', 'marketing'],
|
|
267
|
+
author: 'ObjectUI Team',
|
|
268
|
+
version: '1.0.0',
|
|
269
|
+
},
|
|
270
|
+
variables: [
|
|
271
|
+
{
|
|
272
|
+
name: 'title',
|
|
273
|
+
label: 'Title',
|
|
274
|
+
type: 'string',
|
|
275
|
+
defaultValue: 'Welcome',
|
|
276
|
+
required: true,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'subtitle',
|
|
280
|
+
label: 'Subtitle',
|
|
281
|
+
type: 'string',
|
|
282
|
+
defaultValue: 'Get started today',
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'showButton',
|
|
286
|
+
label: 'Show Button',
|
|
287
|
+
type: 'boolean',
|
|
288
|
+
defaultValue: true,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
slots: [
|
|
292
|
+
{
|
|
293
|
+
name: 'content',
|
|
294
|
+
label: 'Content',
|
|
295
|
+
description: 'Main content area',
|
|
296
|
+
required: false,
|
|
297
|
+
},
|
|
298
|
+
],
|
|
299
|
+
template: {
|
|
300
|
+
type: 'div',
|
|
301
|
+
className: 'hero',
|
|
302
|
+
children: [
|
|
303
|
+
{
|
|
304
|
+
type: 'text',
|
|
305
|
+
value: '${title}',
|
|
306
|
+
},
|
|
307
|
+
],
|
|
308
|
+
},
|
|
309
|
+
editable: true,
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const result = BlockSchema.safeParse(block);
|
|
313
|
+
expect(result.success).toBe(true);
|
|
314
|
+
if (result.success) {
|
|
315
|
+
expect(result.data.meta?.name).toBe('hero-section');
|
|
316
|
+
expect(result.data.variables).toHaveLength(3);
|
|
317
|
+
expect(result.data.slots).toHaveLength(1);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should validate BlockLibrarySchema', () => {
|
|
322
|
+
const library = {
|
|
323
|
+
type: 'block-library',
|
|
324
|
+
category: 'Marketing',
|
|
325
|
+
searchQuery: 'hero',
|
|
326
|
+
showPremium: true,
|
|
327
|
+
loading: false,
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const result = BlockLibrarySchema.safeParse(library);
|
|
331
|
+
expect(result.success).toBe(true);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('Phase 2: Enhanced ActionSchema Zod Validation', () => {
|
|
336
|
+
it('should validate ajax action type', () => {
|
|
337
|
+
const ajaxAction = {
|
|
338
|
+
type: 'action',
|
|
339
|
+
label: 'Load Data',
|
|
340
|
+
actionType: 'ajax',
|
|
341
|
+
api: '/api/data',
|
|
342
|
+
method: 'GET',
|
|
343
|
+
headers: {
|
|
344
|
+
'Authorization': 'Bearer token',
|
|
345
|
+
},
|
|
346
|
+
onSuccess: {
|
|
347
|
+
type: 'toast',
|
|
348
|
+
message: 'Data loaded successfully',
|
|
349
|
+
},
|
|
350
|
+
onFailure: {
|
|
351
|
+
type: 'message',
|
|
352
|
+
message: 'Failed to load data',
|
|
353
|
+
},
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const result = ActionSchema.safeParse(ajaxAction);
|
|
357
|
+
expect(result.success).toBe(true);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
it('should validate confirm action type', () => {
|
|
361
|
+
const confirmAction = {
|
|
362
|
+
type: 'action',
|
|
363
|
+
label: 'Delete Record',
|
|
364
|
+
actionType: 'confirm',
|
|
365
|
+
confirm: {
|
|
366
|
+
title: 'Confirm Deletion',
|
|
367
|
+
message: 'Are you sure you want to delete this record?',
|
|
368
|
+
confirmText: 'Delete',
|
|
369
|
+
cancelText: 'Cancel',
|
|
370
|
+
confirmVariant: 'destructive',
|
|
371
|
+
},
|
|
372
|
+
api: '/api/records/123',
|
|
373
|
+
method: 'DELETE',
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const result = ActionSchema.safeParse(confirmAction);
|
|
377
|
+
expect(result.success).toBe(true);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('should validate dialog action type', () => {
|
|
381
|
+
const dialogAction = {
|
|
382
|
+
type: 'action',
|
|
383
|
+
label: 'Edit Details',
|
|
384
|
+
actionType: 'dialog',
|
|
385
|
+
dialog: {
|
|
386
|
+
title: 'Edit Record',
|
|
387
|
+
size: 'lg',
|
|
388
|
+
content: {
|
|
389
|
+
type: 'form',
|
|
390
|
+
fields: [],
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
|
|
395
|
+
const result = ActionSchema.safeParse(dialogAction);
|
|
396
|
+
expect(result.success).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should validate action chaining', () => {
|
|
400
|
+
const chainedAction = {
|
|
401
|
+
type: 'action',
|
|
402
|
+
label: 'Process Order',
|
|
403
|
+
actionType: 'ajax',
|
|
404
|
+
api: '/api/orders/process',
|
|
405
|
+
method: 'POST',
|
|
406
|
+
chain: [
|
|
407
|
+
{
|
|
408
|
+
type: 'action',
|
|
409
|
+
label: 'Send Email',
|
|
410
|
+
actionType: 'ajax',
|
|
411
|
+
api: '/api/emails/send',
|
|
412
|
+
method: 'POST',
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
type: 'action',
|
|
416
|
+
label: 'Update Inventory',
|
|
417
|
+
actionType: 'ajax',
|
|
418
|
+
api: '/api/inventory/update',
|
|
419
|
+
method: 'PUT',
|
|
420
|
+
},
|
|
421
|
+
],
|
|
422
|
+
chainMode: 'sequential',
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
const result = ActionSchema.safeParse(chainedAction);
|
|
426
|
+
expect(result.success).toBe(true);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
it('should validate conditional action execution', () => {
|
|
430
|
+
const conditionalAction = {
|
|
431
|
+
type: 'action',
|
|
432
|
+
label: 'Approve',
|
|
433
|
+
actionType: 'button',
|
|
434
|
+
condition: {
|
|
435
|
+
expression: '${data.amount > 1000}',
|
|
436
|
+
then: {
|
|
437
|
+
type: 'action',
|
|
438
|
+
label: 'Require Manager Approval',
|
|
439
|
+
actionType: 'confirm',
|
|
440
|
+
},
|
|
441
|
+
else: {
|
|
442
|
+
type: 'action',
|
|
443
|
+
label: 'Auto Approve',
|
|
444
|
+
actionType: 'ajax',
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const result = ActionSchema.safeParse(conditionalAction);
|
|
450
|
+
expect(result.success).toBe(true);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
it('should validate action with tracking', () => {
|
|
454
|
+
const trackedAction = {
|
|
455
|
+
type: 'action',
|
|
456
|
+
label: 'Download Report',
|
|
457
|
+
actionType: 'ajax',
|
|
458
|
+
api: '/api/reports/download',
|
|
459
|
+
tracking: {
|
|
460
|
+
enabled: true,
|
|
461
|
+
event: 'report_downloaded',
|
|
462
|
+
metadata: {
|
|
463
|
+
reportType: 'sales',
|
|
464
|
+
format: 'pdf',
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const result = ActionSchema.safeParse(trackedAction);
|
|
470
|
+
expect(result.success).toBe(true);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('should validate action with retry configuration', () => {
|
|
474
|
+
const retryAction = {
|
|
475
|
+
type: 'action',
|
|
476
|
+
label: 'Submit',
|
|
477
|
+
actionType: 'ajax',
|
|
478
|
+
api: '/api/submit',
|
|
479
|
+
timeout: 30000,
|
|
480
|
+
retry: {
|
|
481
|
+
maxAttempts: 3,
|
|
482
|
+
delay: 1000,
|
|
483
|
+
},
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
const result = ActionSchema.safeParse(retryAction);
|
|
487
|
+
expect(result.success).toBe(true);
|
|
488
|
+
});
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
describe('Phase 2: View Schemas Zod Validation', () => {
|
|
492
|
+
it('should validate DetailViewSchema', () => {
|
|
493
|
+
const detailView = {
|
|
494
|
+
type: 'detail-view',
|
|
495
|
+
title: 'Customer Details',
|
|
496
|
+
api: '/api/customers/123',
|
|
497
|
+
layout: 'grid',
|
|
498
|
+
columns: 2,
|
|
499
|
+
sections: [
|
|
500
|
+
{
|
|
501
|
+
title: 'Basic Information',
|
|
502
|
+
fields: [
|
|
503
|
+
{
|
|
504
|
+
name: 'name',
|
|
505
|
+
label: 'Name',
|
|
506
|
+
type: 'text',
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
name: 'email',
|
|
510
|
+
label: 'Email',
|
|
511
|
+
type: 'text',
|
|
512
|
+
},
|
|
513
|
+
],
|
|
514
|
+
},
|
|
515
|
+
],
|
|
516
|
+
tabs: [
|
|
517
|
+
{
|
|
518
|
+
key: 'orders',
|
|
519
|
+
label: 'Orders',
|
|
520
|
+
content: {
|
|
521
|
+
type: 'table',
|
|
522
|
+
columns: [],
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
showBack: true,
|
|
527
|
+
showEdit: true,
|
|
528
|
+
showDelete: false,
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
const result = DetailViewSchema.safeParse(detailView);
|
|
532
|
+
expect(result.success).toBe(true);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it('should validate ViewSwitcherSchema', () => {
|
|
536
|
+
const viewSwitcher = {
|
|
537
|
+
type: 'view-switcher',
|
|
538
|
+
views: [
|
|
539
|
+
{
|
|
540
|
+
type: 'list',
|
|
541
|
+
label: 'List View',
|
|
542
|
+
icon: 'List',
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
type: 'grid',
|
|
546
|
+
label: 'Grid View',
|
|
547
|
+
icon: 'Grid',
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
type: 'kanban',
|
|
551
|
+
label: 'Kanban',
|
|
552
|
+
icon: 'Kanban',
|
|
553
|
+
},
|
|
554
|
+
],
|
|
555
|
+
defaultView: 'list',
|
|
556
|
+
variant: 'tabs',
|
|
557
|
+
position: 'top',
|
|
558
|
+
persistPreference: true,
|
|
559
|
+
storageKey: 'view-preference',
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
const result = ViewSwitcherSchema.safeParse(viewSwitcher);
|
|
563
|
+
expect(result.success).toBe(true);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('should validate FilterUISchema', () => {
|
|
567
|
+
const filterUI = {
|
|
568
|
+
type: 'filter-ui',
|
|
569
|
+
filters: [
|
|
570
|
+
{
|
|
571
|
+
field: 'status',
|
|
572
|
+
label: 'Status',
|
|
573
|
+
type: 'select',
|
|
574
|
+
options: [
|
|
575
|
+
{ label: 'Active', value: 'active' },
|
|
576
|
+
{ label: 'Inactive', value: 'inactive' },
|
|
577
|
+
],
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
field: 'created_at',
|
|
581
|
+
label: 'Created Date',
|
|
582
|
+
type: 'date-range',
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
showClear: true,
|
|
586
|
+
showApply: true,
|
|
587
|
+
layout: 'popover',
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const result = FilterUISchema.safeParse(filterUI);
|
|
591
|
+
expect(result.success).toBe(true);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('should validate SortUISchema', () => {
|
|
595
|
+
const sortUI = {
|
|
596
|
+
type: 'sort-ui',
|
|
597
|
+
fields: [
|
|
598
|
+
{
|
|
599
|
+
field: 'name',
|
|
600
|
+
label: 'Name',
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
field: 'created_at',
|
|
604
|
+
label: 'Created Date',
|
|
605
|
+
},
|
|
606
|
+
],
|
|
607
|
+
sort: [
|
|
608
|
+
{
|
|
609
|
+
field: 'created_at',
|
|
610
|
+
direction: 'desc',
|
|
611
|
+
},
|
|
612
|
+
],
|
|
613
|
+
multiple: false,
|
|
614
|
+
variant: 'dropdown',
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
const result = SortUISchema.safeParse(sortUI);
|
|
618
|
+
expect(result.success).toBe(true);
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
describe('Phase 2: AnyComponentSchema Union Type', () => {
|
|
623
|
+
it('should validate any Phase 2 schema through union type', () => {
|
|
624
|
+
const schemas = [
|
|
625
|
+
{ type: 'app', name: 'test-app' },
|
|
626
|
+
{ type: 'theme', mode: 'light' },
|
|
627
|
+
{ type: 'report', title: 'Test Report' },
|
|
628
|
+
{ type: 'block', meta: { name: 'test-block' } },
|
|
629
|
+
{ type: 'action', label: 'Test Action' },
|
|
630
|
+
{ type: 'detail-view', title: 'Test Detail' },
|
|
631
|
+
{ type: 'view-switcher', views: [] },
|
|
632
|
+
];
|
|
633
|
+
|
|
634
|
+
schemas.forEach((schema) => {
|
|
635
|
+
const result = AnyComponentSchema.safeParse(schema);
|
|
636
|
+
expect(result.success).toBe(true);
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
});
|
package/src/api-types.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* @object-ui/types - API and Event Schemas
|
|
3
11
|
*
|
package/src/app.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/**
|
|
2
10
|
* @object-ui/types - Application Schema
|
|
3
11
|
*
|
|
@@ -56,6 +64,18 @@ export interface AppSchema extends BaseSchema {
|
|
|
56
64
|
* Global Actions (User Profile, Settings, etc)
|
|
57
65
|
*/
|
|
58
66
|
actions?: AppAction[];
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Home page ID (ObjectStack Spec v0.7.1)
|
|
70
|
+
* Default page to navigate to after login
|
|
71
|
+
*/
|
|
72
|
+
homePageId?: string;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Required permissions (ObjectStack Spec v0.7.1)
|
|
76
|
+
* Permissions required to access this application
|
|
77
|
+
*/
|
|
78
|
+
requiredPermissions?: string[];
|
|
59
79
|
}
|
|
60
80
|
|
|
61
81
|
/**
|
package/src/base.ts
CHANGED