@evoke-platform/ui-components 1.10.0-testing.11 → 1.10.0-testing.12

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.
Files changed (25) hide show
  1. package/dist/published/components/custom/FormV2/FormRenderer.d.ts +2 -1
  2. package/dist/published/components/custom/FormV2/FormRenderer.js +3 -1
  3. package/dist/published/components/custom/FormV2/FormRendererContainer.js +82 -13
  4. package/dist/published/components/custom/FormV2/components/Footer.d.ts +1 -0
  5. package/dist/published/components/custom/FormV2/components/Footer.js +3 -3
  6. package/dist/published/components/custom/FormV2/components/FormContext.d.ts +2 -1
  7. package/dist/published/components/custom/FormV2/components/FormFieldTypes/AddressFields.js +30 -13
  8. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Criteria.js +16 -3
  9. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/Document.js +16 -4
  10. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.d.ts +1 -0
  11. package/dist/published/components/custom/FormV2/components/FormFieldTypes/DocumentFiles/DocumentList.js +16 -3
  12. package/dist/published/components/custom/FormV2/components/FormFieldTypes/Image.js +31 -5
  13. package/dist/published/components/custom/FormV2/components/FormFieldTypes/UserProperty.js +15 -3
  14. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/ObjectPropertyInput.js +70 -18
  15. package/dist/published/components/custom/FormV2/components/FormFieldTypes/relatedObjectFiles/RelatedObjectInstance.js +37 -15
  16. package/dist/published/components/custom/FormV2/components/Header.d.ts +1 -0
  17. package/dist/published/components/custom/FormV2/components/Header.js +42 -4
  18. package/dist/published/components/custom/FormV2/components/RecursiveEntryRenderer.js +12 -3
  19. package/dist/published/components/custom/FormV2/components/utils.js +2 -0
  20. package/dist/published/components/custom/FormV2/tests/FormRendererContainer.test.js +449 -1
  21. package/dist/published/components/custom/FormV2/tests/test-data.d.ts +1 -0
  22. package/dist/published/components/custom/FormV2/tests/test-data.js +138 -0
  23. package/dist/published/stories/FormRenderer.stories.d.ts +8 -4
  24. package/dist/published/theme/hooks.d.ts +4 -3
  25. package/package.json +1 -1
@@ -8,7 +8,7 @@ import React from 'react';
8
8
  import { MemoryRouter } from 'react-router-dom';
9
9
  import { expect, it } from 'vitest';
10
10
  import FormRendererContainer from '../FormRendererContainer';
11
- import { createSpecialtyForm, licenseObject, npLicense, npSpecialtyType1, npSpecialtyType2, rnLicense, rnSpecialtyType1, rnSpecialtyType2, specialtyObject, specialtyTypeObject, } from './test-data';
11
+ import { createSpecialtyForm, licenseForm, licenseObject, npLicense, npSpecialtyType1, npSpecialtyType2, rnLicense, rnSpecialtyType1, rnSpecialtyType2, specialtyObject, specialtyTypeObject, } from './test-data';
12
12
  expect.extend(matchers);
13
13
  // Mock ResizeObserver
14
14
  global.ResizeObserver = class ResizeObserver {
@@ -94,6 +94,454 @@ describe('FormRendererContainer', () => {
94
94
  });
95
95
  });
96
96
  });
97
+ describe('autosave functionality', () => {
98
+ it('should trigger autosave when field loses focus', async () => {
99
+ const user = userEvent.setup();
100
+ const autosaveActionSpy = vi.fn();
101
+ server.use(http.get('/api/data/objects/specialty/instances/test-instance', () => {
102
+ return HttpResponse.json({
103
+ id: 'test-instance',
104
+ name: 'Original Name',
105
+ specialtyType: null,
106
+ license: null,
107
+ });
108
+ }), http.get('/api/data/objects/specialty/instances/test-instance/object', () => {
109
+ return HttpResponse.json(specialtyObject);
110
+ }), http.get('/api/data/forms/specialtyForm', () => {
111
+ return HttpResponse.json({
112
+ ...createSpecialtyForm,
113
+ actionId: '_update',
114
+ autosaveActionId: '_autosave',
115
+ });
116
+ }), http.post('/api/data/objects/specialty/instances/test-instance/actions', async ({ request }) => {
117
+ const body = (await request.json());
118
+ autosaveActionSpy(body);
119
+ return HttpResponse.json({
120
+ id: 'test-instance',
121
+ name: body.input.name,
122
+ specialtyType: null,
123
+ license: null,
124
+ });
125
+ }));
126
+ render(React.createElement(MemoryRouter, null,
127
+ React.createElement(FormRendererContainer, { objectId: 'specialty', formId: 'specialtyForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-instance' })));
128
+ await waitFor(() => {
129
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
130
+ });
131
+ const nameField = await screen.findByRole('textbox', { name: 'Name' });
132
+ // Clear the existing value and type new value
133
+ await user.clear(nameField);
134
+ await user.type(nameField, 'Test Specialty');
135
+ await user.tab(); // Blur the field
136
+ // Verify the data being saved
137
+ expect(autosaveActionSpy).toHaveBeenCalledWith(expect.objectContaining({
138
+ actionId: '_autosave',
139
+ input: expect.objectContaining({
140
+ name: 'Test Specialty',
141
+ }),
142
+ }));
143
+ });
144
+ it('should show saving indicator during autosave', async () => {
145
+ const user = userEvent.setup();
146
+ let resolveSave;
147
+ const savePromise = new Promise((resolve) => {
148
+ resolveSave = resolve;
149
+ });
150
+ const autosaveActionSpy = vi.fn();
151
+ server.use(http.get('/api/data/objects/specialty/instances/test-instance', () => {
152
+ return HttpResponse.json({
153
+ id: 'test-instance',
154
+ name: '',
155
+ specialtyType: null,
156
+ license: null,
157
+ });
158
+ }), http.get('/api/data/objects/specialty/instances/test-instance/object', () => {
159
+ return HttpResponse.json(specialtyObject);
160
+ }), http.get('/api/data/forms/specialtyForm', () => {
161
+ return HttpResponse.json({
162
+ ...createSpecialtyForm,
163
+ actionId: '_update',
164
+ autosaveActionId: '_autosave',
165
+ });
166
+ }), http.post('/api/data/objects/specialty/instances/test-instance/actions', async ({ request }) => {
167
+ const body = (await request.json());
168
+ autosaveActionSpy(body);
169
+ await savePromise; // Wait for manual resolution
170
+ return HttpResponse.json({
171
+ id: 'test-instance',
172
+ name: body.input.name,
173
+ specialtyType: null,
174
+ license: null,
175
+ });
176
+ }));
177
+ render(React.createElement(MemoryRouter, null,
178
+ React.createElement(FormRendererContainer, { objectId: 'specialty', formId: 'specialtyForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-instance' })));
179
+ await waitFor(() => {
180
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
181
+ });
182
+ const nameField = await screen.findByRole('textbox', { name: 'Name' });
183
+ await user.type(nameField, 'Test Specialty');
184
+ await user.tab();
185
+ // Should show saving indicator
186
+ await waitFor(() => {
187
+ expect(screen.getByText('Saving')).toBeInTheDocument();
188
+ });
189
+ // Verify the API was called
190
+ expect(autosaveActionSpy).toHaveBeenCalled();
191
+ // Complete the save
192
+ resolveSave({
193
+ id: 'test-instance',
194
+ name: 'Test Specialty',
195
+ specialtyType: null,
196
+ license: null,
197
+ });
198
+ // Saving indicator should disappear
199
+ await waitFor(() => {
200
+ expect(screen.queryByText('Saving')).not.toBeInTheDocument();
201
+ });
202
+ });
203
+ it('should hide saving indicator after autosave fails', async () => {
204
+ const user = userEvent.setup();
205
+ const autosaveActionSpy = vi.fn();
206
+ server.use(http.get('/api/data/objects/specialty/instances/test-instance', () => {
207
+ return HttpResponse.json({
208
+ id: 'test-instance',
209
+ name: '',
210
+ specialtyType: null,
211
+ license: null,
212
+ });
213
+ }), http.get('/api/data/objects/specialty/instances/test-instance/object', () => {
214
+ return HttpResponse.json(specialtyObject);
215
+ }), http.get('/api/data/forms/specialtyForm', () => {
216
+ return HttpResponse.json({
217
+ ...createSpecialtyForm,
218
+ actionId: '_update',
219
+ autosaveActionId: '_autosave',
220
+ });
221
+ }), http.post('/api/data/objects/specialty/instances/test-instance/actions', () => {
222
+ autosaveActionSpy();
223
+ return HttpResponse.json({ error: 'Save failed' }, { status: 500 });
224
+ }));
225
+ render(React.createElement(MemoryRouter, null,
226
+ React.createElement(FormRendererContainer, { objectId: 'specialty', formId: 'specialtyForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-instance' })));
227
+ await waitFor(() => {
228
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
229
+ });
230
+ const nameField = await screen.findByRole('textbox', { name: 'Name' });
231
+ await user.type(nameField, 'Test Specialty');
232
+ await user.tab();
233
+ // Wait for autosave to complete
234
+ await waitFor(() => {
235
+ expect(autosaveActionSpy).toHaveBeenCalled();
236
+ });
237
+ // Saving indicator should be hidden after error
238
+ await waitFor(() => {
239
+ expect(screen.queryByText('Saving')).not.toBeInTheDocument();
240
+ });
241
+ });
242
+ it('should not trigger autosave when field value has not changed', async () => {
243
+ const user = userEvent.setup();
244
+ const autosaveActionSpy = vi.fn();
245
+ server.use(http.get('/api/data/objects/specialty/instances/test-instance', () => {
246
+ return HttpResponse.json({
247
+ id: 'test-instance',
248
+ name: 'Original Name',
249
+ specialtyType: null,
250
+ license: null,
251
+ });
252
+ }), http.get('/api/data/objects/specialty/instances/test-instance/object', () => {
253
+ return HttpResponse.json(specialtyObject);
254
+ }), http.get('/api/data/forms/specialtyForm', () => {
255
+ return HttpResponse.json({
256
+ ...createSpecialtyForm,
257
+ actionId: '_update',
258
+ autosaveActionId: '_autosave',
259
+ });
260
+ }), http.post('/api/data/objects/specialty/instances/test-instance/actions', async ({ request }) => {
261
+ const body = (await request.json());
262
+ autosaveActionSpy(body);
263
+ return HttpResponse.json({
264
+ id: 'test-instance',
265
+ name: body.input.name,
266
+ specialtyType: null,
267
+ license: null,
268
+ });
269
+ }));
270
+ render(React.createElement(MemoryRouter, null,
271
+ React.createElement(FormRendererContainer, { objectId: 'specialty', formId: 'specialtyForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-instance' })));
272
+ await waitFor(() => {
273
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
274
+ });
275
+ const nameField = await screen.findByRole('textbox', { name: 'Name' });
276
+ // Click into the field and blur it without changing value
277
+ await user.click(nameField);
278
+ await user.tab(); // Blur the field
279
+ await waitFor(() => {
280
+ expect(nameField).not.toHaveFocus();
281
+ });
282
+ // Wait a bit to ensure no autosave is triggered
283
+ await new Promise((r) => setTimeout(r, 500));
284
+ // Verify autosave was NOT called since value didn't change
285
+ expect(autosaveActionSpy).not.toHaveBeenCalled();
286
+ });
287
+ it('should trigger autosave when address field loses focus after change', async () => {
288
+ const user = userEvent.setup();
289
+ const autosaveActionSpy = vi.fn();
290
+ server.use(http.get('/api/data/objects/license/instances/test-license', () => {
291
+ return HttpResponse.json({
292
+ id: 'test-license',
293
+ name: 'RN-123456',
294
+ address: {
295
+ line1: '123 Main St',
296
+ city: 'Boston',
297
+ state: 'MA',
298
+ zipCode: '02101',
299
+ },
300
+ });
301
+ }), http.get('/api/data/objects/license/instances/test-license/object', () => {
302
+ return HttpResponse.json(licenseObject);
303
+ }), http.get('/api/data/forms/licenseForm', () => {
304
+ return HttpResponse.json({
305
+ ...licenseForm,
306
+ autosaveActionId: '_autosave',
307
+ });
308
+ }), http.get('/api/data/locations/search', () => {
309
+ return HttpResponse.json([]);
310
+ }), http.post('/api/data/objects/license/instances/test-license/actions', async ({ request }) => {
311
+ const body = (await request.json());
312
+ autosaveActionSpy(body);
313
+ return HttpResponse.json({
314
+ id: 'test-license',
315
+ name: body.input.name || 'RN-123456',
316
+ address: body.input.address || {
317
+ line1: '123 Main St',
318
+ city: 'Boston',
319
+ state: 'MA',
320
+ zipCode: '02101',
321
+ },
322
+ });
323
+ }));
324
+ render(React.createElement(MemoryRouter, null,
325
+ React.createElement(FormRendererContainer, { objectId: 'license', formId: 'licenseForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-license' })));
326
+ await waitFor(() => {
327
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
328
+ });
329
+ // Find the city field
330
+ const cityField = await screen.findByRole('textbox', { name: 'City' });
331
+ // Clear and type new value
332
+ await user.clear(cityField);
333
+ await user.type(cityField, 'Cambridge');
334
+ await user.tab(); // Blur the field
335
+ // Verify autosave was eventually called and the final call contains the updated city
336
+ await waitFor(() => {
337
+ expect(autosaveActionSpy).toHaveBeenCalled();
338
+ });
339
+ const lastCall = autosaveActionSpy.mock.lastCall[0];
340
+ expect(lastCall).toEqual(expect.objectContaining({
341
+ input: expect.objectContaining({
342
+ address: expect.objectContaining({
343
+ city: 'Cambridge',
344
+ }),
345
+ }),
346
+ }));
347
+ });
348
+ it('should not trigger autosave when address field loses focus without changes', async () => {
349
+ const user = userEvent.setup();
350
+ const autosaveActionSpy = vi.fn();
351
+ server.use(http.get('/api/data/objects/license/instances/test-license', () => {
352
+ return HttpResponse.json({
353
+ id: 'test-license',
354
+ name: 'RN-123456',
355
+ address: {
356
+ line1: '123 Main St',
357
+ city: 'Boston',
358
+ state: 'MA',
359
+ zipCode: '02101',
360
+ },
361
+ });
362
+ }), http.get('/api/data/objects/license/instances/test-license/object', () => {
363
+ return HttpResponse.json(licenseObject);
364
+ }), http.get('/api/data/objects/license/effective', (req) => {
365
+ const sanitizedVersion = new URL(req.request.url).searchParams.get('sanitizedVersion');
366
+ if (sanitizedVersion === 'true') {
367
+ return HttpResponse.json(licenseObject);
368
+ }
369
+ }), http.get('/api/data/forms/licenseForm', () => {
370
+ return HttpResponse.json({
371
+ ...licenseForm,
372
+ autosaveActionId: '_autosave',
373
+ });
374
+ }), http.get('/api/data/locations/search', () => {
375
+ return HttpResponse.json([]);
376
+ }), http.post('/api/data/objects/license/instances/test-license/actions', async ({ request }) => {
377
+ const body = (await request.json());
378
+ autosaveActionSpy(body);
379
+ return HttpResponse.json({
380
+ id: 'test-license',
381
+ name: 'RN-123456',
382
+ address: body.input.address,
383
+ });
384
+ }));
385
+ render(React.createElement(MemoryRouter, null,
386
+ React.createElement(FormRendererContainer, { objectId: 'license', formId: 'licenseForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-license' })));
387
+ await waitFor(() => {
388
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
389
+ });
390
+ // Find the city field
391
+ const cityField = await screen.findByRole('textbox', { name: 'City' });
392
+ // Click into field and blur without changing
393
+ await user.click(cityField);
394
+ await user.tab();
395
+ // Wait to ensure no autosave is triggered
396
+ await new Promise((r) => setTimeout(r, 500));
397
+ // Verify autosave was NOT called since value didn't change
398
+ expect(autosaveActionSpy).not.toHaveBeenCalled();
399
+ });
400
+ it('should trigger autosave for multiple address fields when line1 autocompletes', async () => {
401
+ const user = userEvent.setup();
402
+ const autosaveActionSpy = vi.fn();
403
+ server.use(http.get('/api/data/objects/license/instances/test-license', () => {
404
+ return HttpResponse.json({
405
+ id: 'test-license',
406
+ name: 'RN-123456',
407
+ address: {
408
+ line1: '',
409
+ city: '',
410
+ state: '',
411
+ zipCode: '',
412
+ },
413
+ });
414
+ }), http.get('/api/data/objects/license/instances/test-license/object', () => {
415
+ return HttpResponse.json(licenseObject);
416
+ }), http.get('/api/data/objects/license/effective', (req) => {
417
+ const sanitizedVersion = new URL(req.request.url).searchParams.get('sanitizedVersion');
418
+ if (sanitizedVersion === 'true') {
419
+ return HttpResponse.json(licenseObject);
420
+ }
421
+ }), http.get('/api/data/forms/licenseForm', () => {
422
+ return HttpResponse.json({
423
+ ...licenseForm,
424
+ autosaveActionId: '_autosave',
425
+ });
426
+ }), http.get('/api/data/locations/search', () => {
427
+ return HttpResponse.json([
428
+ {
429
+ address: {
430
+ line1: '456 Oak Street',
431
+ city: 'Springfield',
432
+ state: 'MA',
433
+ zipCode: '01101',
434
+ county: 'Hampden',
435
+ },
436
+ },
437
+ ]);
438
+ }), http.post('/api/data/objects/license/instances/test-license/actions', async ({ request }) => {
439
+ const body = (await request.json());
440
+ autosaveActionSpy(body);
441
+ return HttpResponse.json({
442
+ id: 'test-license',
443
+ name: 'RN-123456',
444
+ address: body.input.address,
445
+ });
446
+ }));
447
+ render(React.createElement(MemoryRouter, null,
448
+ React.createElement(FormRendererContainer, { objectId: 'license', formId: 'licenseForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-license' })));
449
+ await waitFor(() => {
450
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
451
+ });
452
+ // Find the line1 field (it's a searchbox because of address autocomplete)
453
+ const line1Field = await screen.findByRole('searchbox', { name: 'Address Line 1' });
454
+ // Type to trigger autocomplete
455
+ await user.type(line1Field, '456');
456
+ // Wait for and select the autocomplete option
457
+ const autocompleteOption = await screen.findByText('456 Oak Street');
458
+ await user.click(autocompleteOption);
459
+ // Verify autosave was eventually called and the final call contains the expected address values
460
+ await waitFor(() => {
461
+ expect(autosaveActionSpy).toHaveBeenCalled();
462
+ });
463
+ // The autosave is triggered twice when selecting the autocomplete option,
464
+ // once by the selection and once by the onBlur event. We want to verify the last call
465
+ // has the correct data.
466
+ const lastCall = autosaveActionSpy.mock.lastCall[0];
467
+ expect(lastCall).toEqual(expect.objectContaining({
468
+ input: expect.objectContaining({
469
+ address: expect.objectContaining({
470
+ line1: '456 Oak Street',
471
+ city: 'Springfield',
472
+ state: 'MA',
473
+ zipCode: '01101',
474
+ }),
475
+ }),
476
+ }));
477
+ });
478
+ it('should hide discard changes button when autosave is configured', async () => {
479
+ server.use(http.get('/api/data/objects/specialty/instances/test-instance', () => {
480
+ return HttpResponse.json({
481
+ id: 'test-instance',
482
+ name: '',
483
+ specialtyType: null,
484
+ license: null,
485
+ });
486
+ }), http.get('/api/data/objects/specialty/instances/test-instance/object', () => {
487
+ return HttpResponse.json(specialtyObject);
488
+ }), http.get('/api/data/forms/specialtyForm', () => {
489
+ return HttpResponse.json({
490
+ ...createSpecialtyForm,
491
+ actionId: '_update',
492
+ autosaveActionId: '_autosave',
493
+ });
494
+ }));
495
+ render(React.createElement(MemoryRouter, null,
496
+ React.createElement(FormRendererContainer, { objectId: 'specialty', formId: 'specialtyForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-instance' })));
497
+ await waitFor(() => {
498
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
499
+ });
500
+ // When autosaveActionId is present the discard button should be hidden
501
+ expect(screen.queryByRole('button', { name: /discard/i })).not.toBeInTheDocument();
502
+ });
503
+ it('should not trigger autosave when field changes but auto save is not configured', async () => {
504
+ server.use(http.get('/api/data/objects/specialty/instances/test-instance', () => {
505
+ return HttpResponse.json({
506
+ id: 'test-instance',
507
+ name: '',
508
+ specialtyType: null,
509
+ license: null,
510
+ });
511
+ }), http.get('/api/data/objects/specialty/instances/test-instance/object', () => {
512
+ return HttpResponse.json(specialtyObject);
513
+ }), http.get('/api/data/forms/specialtyForm', () => {
514
+ return HttpResponse.json({
515
+ ...createSpecialtyForm,
516
+ actionId: '_update',
517
+ });
518
+ }));
519
+ const autosaveActionSpy = vi.fn();
520
+ server.use(http.post('/api/data/objects/specialty/instances/test-instance/actions', async ({ request }) => {
521
+ const body = (await request.json());
522
+ autosaveActionSpy(body);
523
+ return HttpResponse.json({
524
+ id: 'test-instance',
525
+ ...body.input,
526
+ });
527
+ }));
528
+ const user = userEvent.setup();
529
+ render(React.createElement(MemoryRouter, null,
530
+ React.createElement(FormRendererContainer, { objectId: 'specialty', formId: 'specialtyForm', dataType: 'objectInstances', actionId: '_update', instanceId: 'test-instance' })));
531
+ await waitFor(() => {
532
+ expect(screen.queryByText('Loading...')).not.toBeInTheDocument();
533
+ });
534
+ // Change a field value
535
+ const nameInput = screen.getByRole('textbox', { name: /name/i });
536
+ await user.clear(nameInput);
537
+ await user.type(nameInput, 'Test Specialty');
538
+ await user.tab();
539
+ // Wait a bit to ensure no autosave happens
540
+ await new Promise((resolve) => setTimeout(resolve, 100));
541
+ // Verify autosave was not triggered
542
+ expect(autosaveActionSpy).not.toHaveBeenCalled();
543
+ });
544
+ });
97
545
  it('should display a submit button', async () => {
98
546
  const form = {
99
547
  id: 'simpleForm',
@@ -23,3 +23,4 @@ export declare const users: {
23
23
  email: string;
24
24
  name: string;
25
25
  }[];
26
+ export declare const licenseForm: EvokeForm;
@@ -166,6 +166,11 @@ export const licenseObject = {
166
166
  type: 'object',
167
167
  objectId: 'licenseType',
168
168
  },
169
+ {
170
+ id: 'address',
171
+ name: 'Licensee Address',
172
+ type: 'address',
173
+ },
169
174
  ],
170
175
  actions: [
171
176
  {
@@ -173,6 +178,96 @@ export const licenseObject = {
173
178
  name: 'Update',
174
179
  type: 'update',
175
180
  outputEvent: 'License Updated',
181
+ parameters: [
182
+ {
183
+ id: 'name',
184
+ name: 'License Number',
185
+ type: 'string',
186
+ },
187
+ {
188
+ id: 'status',
189
+ name: 'Status',
190
+ type: 'string',
191
+ },
192
+ {
193
+ id: 'address.line1',
194
+ name: 'Address Line 1',
195
+ type: 'string',
196
+ },
197
+ {
198
+ id: 'address.line2',
199
+ name: 'Address Line 2',
200
+ type: 'string',
201
+ },
202
+ {
203
+ id: 'address.city',
204
+ name: 'City',
205
+ type: 'string',
206
+ },
207
+ {
208
+ id: 'address.county',
209
+ name: 'County',
210
+ type: 'string',
211
+ },
212
+ {
213
+ id: 'address.state',
214
+ name: 'State',
215
+ type: 'string',
216
+ },
217
+ {
218
+ id: 'address.zipCode',
219
+ name: 'Zip Code',
220
+ type: 'string',
221
+ },
222
+ ],
223
+ },
224
+ {
225
+ id: '_autosave',
226
+ name: 'Autosave',
227
+ type: 'update',
228
+ outputEvent: 'License Autosaved',
229
+ parameters: [
230
+ {
231
+ id: 'name',
232
+ name: 'License Number',
233
+ type: 'string',
234
+ },
235
+ {
236
+ id: 'status',
237
+ name: 'Status',
238
+ type: 'string',
239
+ },
240
+ {
241
+ id: 'address.line1',
242
+ name: 'Address Line 1',
243
+ type: 'string',
244
+ },
245
+ {
246
+ id: 'address.line2',
247
+ name: 'Address Line 2',
248
+ type: 'string',
249
+ },
250
+ {
251
+ id: 'address.city',
252
+ name: 'City',
253
+ type: 'string',
254
+ },
255
+ {
256
+ id: 'address.county',
257
+ name: 'County',
258
+ type: 'string',
259
+ },
260
+ {
261
+ id: 'address.state',
262
+ name: 'State',
263
+ type: 'string',
264
+ },
265
+ {
266
+ id: 'address.zipCode',
267
+ name: 'Zip Code',
268
+ type: 'string',
269
+ },
270
+ ],
176
271
  },
177
272
  {
178
273
  id: '_delete',
@@ -528,3 +623,46 @@ export const users = [
528
623
  name: 'User 2',
529
624
  },
530
625
  ];
626
+ export const licenseForm = {
627
+ id: 'licenseForm',
628
+ name: 'License Form',
629
+ objectId: 'license',
630
+ actionId: '_update',
631
+ entries: [
632
+ {
633
+ parameterId: 'name',
634
+ type: 'input',
635
+ display: {
636
+ label: 'License Number',
637
+ },
638
+ },
639
+ {
640
+ parameterId: 'address.line1',
641
+ type: 'input',
642
+ display: {
643
+ label: 'Address Line 1',
644
+ },
645
+ },
646
+ {
647
+ parameterId: 'address.city',
648
+ type: 'input',
649
+ display: {
650
+ label: 'City',
651
+ },
652
+ },
653
+ {
654
+ parameterId: 'address.state',
655
+ type: 'input',
656
+ display: {
657
+ label: 'State',
658
+ },
659
+ },
660
+ {
661
+ parameterId: 'address.zipCode',
662
+ type: 'input',
663
+ display: {
664
+ label: 'Zip Code',
665
+ },
666
+ },
667
+ ],
668
+ };
@@ -9,7 +9,8 @@ declare const _default: import("@storybook/types").ComponentAnnotations<import("
9
9
  form: import("@evoke-platform/context").EvokeForm;
10
10
  title?: React.ReactNode;
11
11
  instance?: import("../components/custom/FormV2/components/types").Document | import("@evoke-platform/context").ObjectInstance | undefined;
12
- onChange: (id: string, value: unknown) => void;
12
+ onChange: (id: string, value: unknown) => void | Promise<void>;
13
+ onAutosave?: ((fieldId: string) => void | Promise<void>) | undefined;
13
14
  associatedObject?: {
14
15
  instanceId?: string | undefined;
15
16
  propertyId?: string | undefined;
@@ -29,7 +30,8 @@ export declare const Editable: import("@storybook/types").AnnotatedStoryFn<impor
29
30
  form: import("@evoke-platform/context").EvokeForm;
30
31
  title?: React.ReactNode;
31
32
  instance?: import("../components/custom/FormV2/components/types").Document | import("@evoke-platform/context").ObjectInstance | undefined;
32
- onChange: (id: string, value: unknown) => void;
33
+ onChange: (id: string, value: unknown) => void | Promise<void>;
34
+ onAutosave?: ((fieldId: string) => void | Promise<void>) | undefined;
33
35
  associatedObject?: {
34
36
  instanceId?: string | undefined;
35
37
  propertyId?: string | undefined;
@@ -48,7 +50,8 @@ export declare const NoButtons: import("@storybook/types").AnnotatedStoryFn<impo
48
50
  form: import("@evoke-platform/context").EvokeForm;
49
51
  title?: React.ReactNode;
50
52
  instance?: import("../components/custom/FormV2/components/types").Document | import("@evoke-platform/context").ObjectInstance | undefined;
51
- onChange: (id: string, value: unknown) => void;
53
+ onChange: (id: string, value: unknown) => void | Promise<void>;
54
+ onAutosave?: ((fieldId: string) => void | Promise<void>) | undefined;
52
55
  associatedObject?: {
53
56
  instanceId?: string | undefined;
54
57
  propertyId?: string | undefined;
@@ -67,7 +70,8 @@ export declare const DocumentForm: import("@storybook/types").AnnotatedStoryFn<i
67
70
  form: import("@evoke-platform/context").EvokeForm;
68
71
  title?: React.ReactNode;
69
72
  instance?: import("../components/custom/FormV2/components/types").Document | import("@evoke-platform/context").ObjectInstance | undefined;
70
- onChange: (id: string, value: unknown) => void;
73
+ onChange: (id: string, value: unknown) => void | Promise<void>;
74
+ onAutosave?: ((fieldId: string) => void | Promise<void>) | undefined;
71
75
  associatedObject?: {
72
76
  instanceId?: string | undefined;
73
77
  propertyId?: string | undefined;