extended-dynamic-forms 0.2.0 → 0.2.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/README.md +427 -99
- package/dist/WizardForm.d.ts +4 -1
- package/dist/events/FormEventHub.d.ts +19 -2
- package/dist/index.es.js +6723 -6594
- package/dist/index.umd.js +22 -22
- package/dist/types/index.d.ts +6 -1
- package/dist/utils/fieldChangeDetector.d.ts +22 -0
- package/dist/vanilla-entry.d.ts +28 -0
- package/package.json +4 -4
- package/examples/conditional-v2-example.tsx +0 -64
- package/examples/dynamic-array-items-example.tsx +0 -211
- package/examples/form-devtools-example.tsx +0 -384
- package/examples/global-ui-rules-example.tsx +0 -371
- package/examples/vanilla-js/README.md +0 -212
- package/examples/vanilla-js/basic-form.html +0 -142
- package/examples/vanilla-js/conditional-form.html +0 -159
- package/examples/vanilla-js/wizard-form.html +0 -185
package/README.md
CHANGED
|
@@ -13,7 +13,6 @@
|
|
|
13
13
|
**Known Issues**:
|
|
14
14
|
- The project currently uses local file dependencies that require a specific directory structure to build
|
|
15
15
|
- The project requires RJSF packages from a sibling directory (../react-jsonschema-form/)
|
|
16
|
-
- Some playground demos use deprecated v1 conditional patterns (01-simple-conditionals.demo.tsx)
|
|
17
16
|
- Array drag-and-drop functionality mentioned in documentation is not implemented
|
|
18
17
|
- Test coverage is limited for UI components and event systems
|
|
19
18
|
- Several documented features have incomplete implementations
|
|
@@ -22,7 +21,7 @@
|
|
|
22
21
|
|
|
23
22
|
**A revolutionary extension library for [React JSON Schema Form (RJSF)](https://github.com/rjsf-team/react-jsonschema-form)** that transforms schema-driven forms into sophisticated, enterprise-ready applications with O(N) performance and professional UI components.
|
|
24
23
|
|
|
25
|
-
Built on top of the excellent RJSF v6 foundation, this library introduces the **Three Pillars
|
|
24
|
+
Built on top of the excellent RJSF v6 foundation, this library introduces the **Three Pillars Architecture** for conditional logic, multi-step wizard orchestration, central event systems with webhook integration, and a comprehensive suite of 25+ Ant Design components.
|
|
26
25
|
|
|
27
26
|
[](https://www.npmjs.com/package/extended-dynamic-forms)
|
|
28
27
|
[](https://github.com/user/extended-dynamic-forms/blob/main/LICENSE)
|
|
@@ -35,7 +34,7 @@ Built on top of the excellent RJSF v6 foundation, this library introduces the **
|
|
|
35
34
|
While [RJSF](https://github.com/rjsf-team/react-jsonschema-form) provides an excellent foundation for generating forms from JSON Schema, enterprise applications demand more sophisticated capabilities:
|
|
36
35
|
|
|
37
36
|
### 🚀 **Performance Revolution**
|
|
38
|
-
- **O(N) Conditional Logic**: Advanced Three Pillars
|
|
37
|
+
- **O(N) Conditional Logic**: Advanced Three Pillars architecture with optimized performance
|
|
39
38
|
- **Optimized Array Processing**: Per-item evaluation with dynamic array conditionals
|
|
40
39
|
- **Memory Efficient**: Immutable schema processing with intelligent caching
|
|
41
40
|
|
|
@@ -49,17 +48,16 @@ While [RJSF](https://github.com/rjsf-team/react-jsonschema-form) provides an exc
|
|
|
49
48
|
- **Dual Architecture Support**: Modern React hooks + vanilla JavaScript compatibility
|
|
50
49
|
- **Type-Safe Throughout**: Comprehensive TypeScript interfaces and validation
|
|
51
50
|
- **Rich Ecosystem**: Interactive playground with comprehensive real-world demos
|
|
52
|
-
- **Migration Ready**: Automated v1-to-v2 conversion utilities
|
|
53
51
|
|
|
54
52
|
This library transforms RJSF from a simple form generator into a complete enterprise form solution while preserving its declarative approach.
|
|
55
53
|
|
|
56
54
|
## Core Features
|
|
57
55
|
|
|
58
|
-
### 🚀 Three Pillars
|
|
56
|
+
### 🚀 Three Pillars Architecture with JsonLogic
|
|
59
57
|
- **UI Logic Pillar**: Control form appearance with JsonLogic conditions and JSON Patch operations
|
|
60
58
|
- **Schema Logic Pillar**: Modify data structure with JsonLogic rules when truly needed
|
|
61
59
|
- **Validation Logic Pillar**: Dynamic validation rules with JsonLogic conditions
|
|
62
|
-
- **O(N) Performance**: Superior array handling
|
|
60
|
+
- **O(N) Performance**: Superior array handling with optimized evaluation
|
|
63
61
|
- **JsonLogic-Only**: Secure, data-driven conditions without code execution
|
|
64
62
|
- **React Hooks**: Modern, declarative API with `useConditionalUi`, `useConditionalSchema`, `useConditionalValidation`
|
|
65
63
|
|
|
@@ -88,7 +86,7 @@ const uiRules: UiRule[] = [{
|
|
|
88
86
|
- **Full conditional logic support** with step-aware rule processing
|
|
89
87
|
- Built-in progress indicators and navigation
|
|
90
88
|
- Per-step validation
|
|
91
|
-
- Supports
|
|
89
|
+
- Supports Three Pillars architecture with JsonLogic conditions
|
|
92
90
|
|
|
93
91
|
**Step-Aware Conditional Logic**
|
|
94
92
|
|
|
@@ -111,7 +109,7 @@ const schema = {
|
|
|
111
109
|
}
|
|
112
110
|
};
|
|
113
111
|
|
|
114
|
-
// Use
|
|
112
|
+
// Use UiRules with JsonLogic to handle step-aware conditionals
|
|
115
113
|
const uiRules: UiRule[] = [{
|
|
116
114
|
name: 'Show SSN only for US Citizens in Personal step',
|
|
117
115
|
condition: {
|
|
@@ -130,6 +128,7 @@ const uiRules: UiRule[] = [{
|
|
|
130
128
|
- Webhook integration with retry logic
|
|
131
129
|
- Real-time form analytics capabilities
|
|
132
130
|
- Debounced event processing
|
|
131
|
+
- **Enhanced webhook support** for form-level events (submit, stepChange, validationError)
|
|
133
132
|
|
|
134
133
|
### 🎨 Ant Design Components
|
|
135
134
|
- Complete widget suite built with Ant Design v5
|
|
@@ -185,25 +184,7 @@ const uiRules: UiRule[] = [{
|
|
|
185
184
|
|
|
186
185
|
**Important Notes on Conditional Logic in Vanilla JS:**
|
|
187
186
|
- **JsonLogic is NOT supported in vanilla JS** - The JsonLogic runtime requires React hooks
|
|
188
|
-
-
|
|
189
|
-
- Subject to v1's O(M×N) performance for array conditionals
|
|
190
|
-
- For JsonLogic and v2 benefits, you must use the React-based API
|
|
191
|
-
|
|
192
|
-
```javascript
|
|
193
|
-
// Vanilla JS supports only v1 conditionals
|
|
194
|
-
ExtendedDynamicForms.createForm({
|
|
195
|
-
container: '#my-form',
|
|
196
|
-
schema: schema,
|
|
197
|
-
conditionals: {
|
|
198
|
-
rules: [
|
|
199
|
-
{
|
|
200
|
-
conditions: { showField: true },
|
|
201
|
-
event: { type: 'show', params: { field: 'conditionalField' } }
|
|
202
|
-
}
|
|
203
|
-
]
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
```
|
|
187
|
+
- For JsonLogic benefits, you must use the React-based API
|
|
207
188
|
|
|
208
189
|
**Perfect for:**
|
|
209
190
|
- WordPress/Drupal integrations
|
|
@@ -248,7 +229,7 @@ pnpm add extended-dynamic-forms
|
|
|
248
229
|
|
|
249
230
|
### Peer Dependencies
|
|
250
231
|
|
|
251
|
-
**Current Version**: `0.2.0` - Built for **RJSF v6** with Three Pillars
|
|
232
|
+
**Current Version**: `0.2.0` - Built for **RJSF v6** with Three Pillars architecture (JsonLogic-only).
|
|
252
233
|
|
|
253
234
|
```json
|
|
254
235
|
{
|
|
@@ -258,7 +239,7 @@ pnpm add extended-dynamic-forms
|
|
|
258
239
|
}
|
|
259
240
|
```
|
|
260
241
|
|
|
261
|
-
> **React 19 Support**: This library supports both React 18 and React 19. See [REACT-19-COMPATIBILITY.md](
|
|
242
|
+
> **React 19 Support**: This library supports both React 18 and React 19. See [REACT-19-COMPATIBILITY.md](archive/REACT-19-COMPATIBILITY.md) for migration details.
|
|
262
243
|
|
|
263
244
|
**RJSF Dependencies** (automatically handled):
|
|
264
245
|
- `@rjsf/core`: ^6.0.0+
|
|
@@ -270,7 +251,7 @@ pnpm add extended-dynamic-forms
|
|
|
270
251
|
|
|
271
252
|
**Additional Dependencies**:
|
|
272
253
|
- `dayjs`: ^1.11.13 (Date handling)
|
|
273
|
-
- `json-rules-engine-simplified`: ^0.2.0
|
|
254
|
+
- `json-rules-engine-simplified`: ^0.2.0
|
|
274
255
|
|
|
275
256
|
> **Note**: This library is specifically built for RJSF v6 to leverage the latest performance improvements and API enhancements. All examples and documentation assume RJSF v6.
|
|
276
257
|
|
|
@@ -315,18 +296,18 @@ function App() {
|
|
|
315
296
|
}
|
|
316
297
|
```
|
|
317
298
|
|
|
318
|
-
## Three Pillars
|
|
299
|
+
## Three Pillars Architecture with JsonLogic (Required Approach)
|
|
319
300
|
|
|
320
|
-
> ⚠️ **BREAKING CHANGE**: As of v0.2.0, the Three Pillars
|
|
301
|
+
> ⚠️ **BREAKING CHANGE**: As of v0.2.0, the Three Pillars system ONLY supports JsonLogic conditions. Function-based conditions are no longer supported for security and maintainability reasons.
|
|
321
302
|
|
|
322
|
-
The Three Pillars
|
|
303
|
+
The Three Pillars system with JsonLogic provides superior performance, cleaner separation of concerns, and better security. It consists of three independent pillars:
|
|
323
304
|
|
|
324
305
|
### 1. UI Logic Pillar (`useConditionalUi`)
|
|
325
306
|
|
|
326
307
|
Controls form appearance using JSON Patch operations with JsonLogic conditions:
|
|
327
308
|
|
|
328
309
|
```tsx
|
|
329
|
-
// Note:
|
|
310
|
+
// Note: conditionals require deep import for optimal tree-shaking
|
|
330
311
|
import { useConditionalUi, UiRule, JsonLogicRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
331
312
|
|
|
332
313
|
// Define UI rules with JsonLogic conditions and JSON Patch operations
|
|
@@ -463,11 +444,11 @@ function MyForm({ schema }) {
|
|
|
463
444
|
|
|
464
445
|
#### **The Performance Revolution: `createDynamicArrayItemsUiSchema`**
|
|
465
446
|
|
|
466
|
-
One of the most significant breakthroughs
|
|
447
|
+
One of the most significant breakthroughs is the `createDynamicArrayItemsUiSchema` function, which enables **true O(N) performance** for array conditionals. This is a fundamental improvement over traditional form libraries that struggle with dynamic array logic.
|
|
467
448
|
|
|
468
449
|
**How It Works:**
|
|
469
450
|
- **Per-Item Evaluation**: Each array item's conditionals are evaluated independently during render
|
|
470
|
-
- **No Rule Explosion**:
|
|
451
|
+
- **No Rule Explosion**: No rules are multiplied by array size
|
|
471
452
|
- **React-Optimized**: Leverages React's rendering cycle for optimal performance
|
|
472
453
|
|
|
473
454
|
> **Note**: `createDynamicArrayItemsUiSchema` still supports function-based conditions as it evaluates per-item during render. This is not a security risk as the functions are defined in your code, not from user input.
|
|
@@ -505,7 +486,7 @@ const uiSchema = {
|
|
|
505
486
|
// ❌ Traditional approach: O(M×N) - Rules × Array Items
|
|
506
487
|
// With 10 rules and 50 directors = 500 evaluations
|
|
507
488
|
|
|
508
|
-
// ✅
|
|
489
|
+
// ✅ Our approach: O(N) - Per-item evaluation
|
|
509
490
|
// With 50 directors = 50 evaluations (regardless of rule complexity)
|
|
510
491
|
```
|
|
511
492
|
|
|
@@ -559,7 +540,7 @@ function MyForm() {
|
|
|
559
540
|
|
|
560
541
|
### JsonLogic Conditions (Required)
|
|
561
542
|
|
|
562
|
-
As of v0.2.0,
|
|
543
|
+
As of v0.2.0, ONLY JsonLogic conditions are supported for security and maintainability:
|
|
563
544
|
|
|
564
545
|
```tsx
|
|
565
546
|
import { JsonLogicRule, UiRule } from 'extended-dynamic-forms/conditionals/v2';
|
|
@@ -588,7 +569,7 @@ const complexRule: UiRule = {
|
|
|
588
569
|
|
|
589
570
|
### Simple Wizard Form (Recommended)
|
|
590
571
|
|
|
591
|
-
The `WizardForm` component automatically detects multi-step forms and handles navigation. It fully supports conditional logic through
|
|
572
|
+
The `WizardForm` component automatically detects multi-step forms and handles navigation. It fully supports conditional logic through the Three Pillars architecture:
|
|
592
573
|
|
|
593
574
|
```tsx
|
|
594
575
|
import { useState } from 'react';
|
|
@@ -598,7 +579,7 @@ import schema from './schema.json';
|
|
|
598
579
|
import baseUiSchema from './ui-schema.json';
|
|
599
580
|
import webhooks from './webhooks.json';
|
|
600
581
|
|
|
601
|
-
// Example:
|
|
582
|
+
// Example: Three Pillars approach with JsonLogic (required)
|
|
602
583
|
const uiRules: UiRule[] = [
|
|
603
584
|
{
|
|
604
585
|
name: 'Show tax ID in company step when listed',
|
|
@@ -635,28 +616,9 @@ function App() {
|
|
|
635
616
|
);
|
|
636
617
|
}
|
|
637
618
|
|
|
638
|
-
// Alternative: v1 conditions approach (deprecated but still supported)
|
|
639
|
-
const v1Conditions = [
|
|
640
|
-
{
|
|
641
|
-
conditions: { "STEP_COMPANY.isListedCompany": true },
|
|
642
|
-
event: { type: "show", params: { field: "STEP_COMPANY.taxId" } }
|
|
643
|
-
},
|
|
644
|
-
{
|
|
645
|
-
conditions: { "STEP_DIRECTORS.directors[*].hasShareholding": true },
|
|
646
|
-
event: { type: "show", params: { field: "STEP_DIRECTORS.directors[*].shareholdingPercentage" } }
|
|
647
|
-
}
|
|
648
|
-
];
|
|
649
|
-
|
|
650
|
-
// Pass v1 conditions directly to WizardForm
|
|
651
|
-
<WizardForm
|
|
652
|
-
schema={schema}
|
|
653
|
-
uiSchema={uiSchema}
|
|
654
|
-
conditions={v1Conditions} // WizardForm handles step-aware transformation
|
|
655
|
-
onSubmit={(data) => console.log('Submitted:', data.formData)}
|
|
656
|
-
/>
|
|
657
619
|
```
|
|
658
620
|
|
|
659
|
-
### Basic Form with Three Pillars
|
|
621
|
+
### Basic Form with Three Pillars
|
|
660
622
|
|
|
661
623
|
```tsx
|
|
662
624
|
import { ExtendedForm } from 'extended-dynamic-forms';
|
|
@@ -801,73 +763,257 @@ import { ExtendedForm, type FormFieldEvent } from 'extended-dynamic-forms';
|
|
|
801
763
|
/>
|
|
802
764
|
```
|
|
803
765
|
|
|
804
|
-
### Webhook Integration
|
|
766
|
+
### Enhanced Webhook Integration
|
|
805
767
|
|
|
806
|
-
Configure multiple webhook endpoints to receive real-time form events:
|
|
768
|
+
Configure multiple webhook endpoints to receive real-time form events, including form-level events for submit, validation errors, and wizard step changes:
|
|
807
769
|
|
|
808
770
|
```tsx
|
|
809
|
-
import { ExtendedForm, type WebhookConfig } from 'extended-dynamic-forms';
|
|
771
|
+
import { ExtendedForm, WizardForm, type WebhookConfig } from 'extended-dynamic-forms';
|
|
810
772
|
|
|
773
|
+
// Comprehensive webhook configuration for all event types
|
|
811
774
|
const webhookConfigs: WebhookConfig[] = [
|
|
812
775
|
{
|
|
813
776
|
url: 'https://api.yourapp.com/form-events',
|
|
814
777
|
method: 'POST',
|
|
815
|
-
events: ['change', 'blur'], //
|
|
816
|
-
debounceMs: 500, // Wait 500ms before sending
|
|
778
|
+
events: ['change', 'blur', 'submit', 'validationError'], // Multiple event types
|
|
779
|
+
debounceMs: 500, // Wait 500ms before sending (field events)
|
|
817
780
|
retries: 3, // Retry failed requests
|
|
818
781
|
timeout: 5000, // 5 second timeout
|
|
819
782
|
headers: {
|
|
820
783
|
'Authorization': 'Bearer your-api-token',
|
|
821
784
|
'X-Form-ID': 'contact-form'
|
|
822
785
|
}
|
|
786
|
+
},
|
|
787
|
+
{
|
|
788
|
+
url: 'https://analytics.yourapp.com/wizard-tracking',
|
|
789
|
+
method: 'POST',
|
|
790
|
+
events: ['stepChange', 'submit'], // Wizard-specific events
|
|
791
|
+
debounceMs: 0, // No debouncing for navigation
|
|
792
|
+
headers: {
|
|
793
|
+
'X-Session-ID': sessionId
|
|
794
|
+
}
|
|
823
795
|
}
|
|
824
796
|
];
|
|
825
797
|
|
|
798
|
+
// Use with ExtendedForm - includes webhook status callbacks
|
|
826
799
|
<ExtendedForm
|
|
827
800
|
schema={schema}
|
|
828
801
|
uiSchema={uiSchema}
|
|
829
802
|
webhooks={webhookConfigs}
|
|
803
|
+
onWebhookStart={(webhook, event) => {
|
|
804
|
+
console.log('Webhook started:', webhook.url, event.eventType);
|
|
805
|
+
}}
|
|
806
|
+
onWebhookComplete={(webhook, event, response) => {
|
|
807
|
+
console.log('Webhook completed:', response.status);
|
|
808
|
+
}}
|
|
809
|
+
onWebhookError={(webhook, event, error) => {
|
|
810
|
+
console.error('Webhook failed:', error.message);
|
|
811
|
+
}}
|
|
812
|
+
/>
|
|
813
|
+
|
|
814
|
+
// Use with WizardForm - automatically tracks step changes
|
|
815
|
+
<WizardForm
|
|
816
|
+
schema={wizardSchema}
|
|
817
|
+
uiSchema={wizardUiSchema}
|
|
818
|
+
webhooks={webhookConfigs}
|
|
819
|
+
onWebhookError={(webhook, event, error) => {
|
|
820
|
+
// Handle webhook failures gracefully
|
|
821
|
+
notifyUser('Form data saved locally, will sync when online');
|
|
822
|
+
}}
|
|
830
823
|
/>
|
|
831
824
|
```
|
|
832
825
|
|
|
833
|
-
|
|
826
|
+
#### Webhook Event Types and Payloads
|
|
834
827
|
|
|
835
|
-
|
|
828
|
+
All webhook events follow a standardized payload structure with common fields plus event-specific data:
|
|
836
829
|
|
|
837
|
-
|
|
830
|
+
##### Field Events (existing)
|
|
831
|
+
```typescript
|
|
832
|
+
// Focus, Blur, Change events
|
|
833
|
+
{
|
|
834
|
+
eventType: 'focus' | 'blur' | 'change',
|
|
835
|
+
fieldId: 'email',
|
|
836
|
+
fieldValue: 'user@example.com',
|
|
837
|
+
schema: { type: 'string', format: 'email' },
|
|
838
|
+
fieldPath: ['contact', 'email'],
|
|
839
|
+
timestamp: 1234567890123,
|
|
840
|
+
formData: { /* complete form data */ },
|
|
841
|
+
source: 'extended-dynamic-forms'
|
|
842
|
+
}
|
|
843
|
+
```
|
|
838
844
|
|
|
839
|
-
|
|
845
|
+
##### Submit Event
|
|
846
|
+
```typescript
|
|
847
|
+
// Triggered after successful form submission
|
|
848
|
+
{
|
|
849
|
+
eventType: 'submit',
|
|
850
|
+
formData: {
|
|
851
|
+
name: 'John Doe',
|
|
852
|
+
email: 'john@example.com',
|
|
853
|
+
// ... all submitted data
|
|
854
|
+
},
|
|
855
|
+
timestamp: 1234567890123,
|
|
856
|
+
source: 'extended-dynamic-forms'
|
|
857
|
+
}
|
|
858
|
+
```
|
|
840
859
|
|
|
841
|
-
|
|
842
|
-
|
|
860
|
+
##### Step Change Event (WizardForm only)
|
|
861
|
+
```typescript
|
|
862
|
+
// Triggered when user navigates between wizard steps
|
|
863
|
+
{
|
|
864
|
+
eventType: 'stepChange',
|
|
865
|
+
fromStep: 0, // Previous step index
|
|
866
|
+
toStep: 1, // New step index
|
|
867
|
+
formData: { /* current form data */ },
|
|
868
|
+
timestamp: 1234567890123,
|
|
869
|
+
source: 'extended-dynamic-forms'
|
|
870
|
+
}
|
|
871
|
+
```
|
|
843
872
|
|
|
844
|
-
|
|
845
|
-
|
|
873
|
+
##### Validation Error Event
|
|
874
|
+
```typescript
|
|
875
|
+
// Triggered when form validation fails
|
|
876
|
+
{
|
|
877
|
+
eventType: 'validationError',
|
|
878
|
+
errors: [
|
|
846
879
|
{
|
|
847
|
-
|
|
848
|
-
|
|
880
|
+
property: '.email',
|
|
881
|
+
message: 'Email is required',
|
|
882
|
+
name: 'required',
|
|
883
|
+
stack: '.email Email is required',
|
|
884
|
+
schemaPath: '#/properties/email'
|
|
849
885
|
},
|
|
850
886
|
{
|
|
851
|
-
|
|
852
|
-
|
|
887
|
+
property: '.age',
|
|
888
|
+
message: 'Age must be at least 18',
|
|
889
|
+
name: 'minimum',
|
|
890
|
+
argument: 18,
|
|
891
|
+
stack: '.age Age must be at least 18',
|
|
892
|
+
schemaPath: '#/properties/age/minimum'
|
|
853
893
|
}
|
|
854
|
-
]
|
|
894
|
+
],
|
|
895
|
+
formData: { /* form data that failed validation */ },
|
|
896
|
+
timestamp: 1234567890123,
|
|
897
|
+
source: 'extended-dynamic-forms'
|
|
898
|
+
}
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
#### Advanced Webhook Examples
|
|
902
|
+
|
|
903
|
+
##### Form Analytics and Monitoring
|
|
904
|
+
```tsx
|
|
905
|
+
// Track user behavior and form completion rates
|
|
906
|
+
const analyticsWebhook: WebhookConfig = {
|
|
907
|
+
url: 'https://analytics.yourapp.com/form-events',
|
|
908
|
+
events: ['stepChange', 'submit', 'validationError'],
|
|
909
|
+
headers: {
|
|
910
|
+
'X-Analytics-Key': process.env.ANALYTICS_KEY
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
|
|
914
|
+
// Monitor form health and errors
|
|
915
|
+
const monitoringWebhook: WebhookConfig = {
|
|
916
|
+
url: 'https://monitoring.yourapp.com/form-errors',
|
|
917
|
+
events: ['validationError'], // Only track errors
|
|
918
|
+
method: 'POST',
|
|
919
|
+
retries: 5 // Ensure error tracking is reliable
|
|
920
|
+
};
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
##### Progressive Form Saving
|
|
924
|
+
```tsx
|
|
925
|
+
// Auto-save form progress
|
|
926
|
+
const autoSaveWebhook: WebhookConfig = {
|
|
927
|
+
url: 'https://api.yourapp.com/form-drafts',
|
|
928
|
+
events: ['change', 'stepChange'],
|
|
929
|
+
debounceMs: 2000, // Save every 2 seconds of inactivity
|
|
930
|
+
headers: {
|
|
931
|
+
'X-User-ID': userId,
|
|
932
|
+
'X-Draft': 'true'
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
// Final submission
|
|
937
|
+
const submitWebhook: WebhookConfig = {
|
|
938
|
+
url: 'https://api.yourapp.com/form-submissions',
|
|
939
|
+
events: ['submit'],
|
|
940
|
+
headers: {
|
|
941
|
+
'X-User-ID': userId,
|
|
942
|
+
'X-Final': 'true'
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
```
|
|
946
|
+
|
|
947
|
+
##### Conditional Webhook Logic
|
|
948
|
+
```tsx
|
|
949
|
+
// Different endpoints based on form data
|
|
950
|
+
function App() {
|
|
951
|
+
const [formData, setFormData] = useState({});
|
|
952
|
+
|
|
953
|
+
// Dynamic webhook configuration based on form state
|
|
954
|
+
const webhooks = useMemo(() => {
|
|
955
|
+
const configs: WebhookConfig[] = [];
|
|
956
|
+
|
|
957
|
+
// Add region-specific webhook
|
|
958
|
+
if (formData.country === 'USA') {
|
|
959
|
+
configs.push({
|
|
960
|
+
url: 'https://us-api.yourapp.com/forms',
|
|
961
|
+
events: ['submit'],
|
|
962
|
+
headers: { 'X-Region': 'US' }
|
|
963
|
+
});
|
|
964
|
+
} else if (formData.country === 'EU') {
|
|
965
|
+
configs.push({
|
|
966
|
+
url: 'https://eu-api.yourapp.com/forms',
|
|
967
|
+
events: ['submit'],
|
|
968
|
+
headers: { 'X-Region': 'EU', 'X-GDPR': 'true' }
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
return configs;
|
|
973
|
+
}, [formData.country]);
|
|
974
|
+
|
|
975
|
+
return (
|
|
976
|
+
<ExtendedForm
|
|
977
|
+
schema={schema}
|
|
978
|
+
formData={formData}
|
|
979
|
+
onChange={({ formData }) => setFormData(formData)}
|
|
980
|
+
webhooks={webhooks}
|
|
981
|
+
/>
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
##### Error Handling and Retry Strategies
|
|
987
|
+
```tsx
|
|
988
|
+
// Robust webhook configuration with fallbacks
|
|
989
|
+
const primaryWebhook: WebhookConfig = {
|
|
990
|
+
url: 'https://api.yourapp.com/primary',
|
|
991
|
+
events: ['submit'],
|
|
992
|
+
timeout: 5000,
|
|
993
|
+
retries: 3
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
// Fallback webhook for critical data
|
|
997
|
+
const fallbackWebhook: WebhookConfig = {
|
|
998
|
+
url: 'https://backup.yourapp.com/forms',
|
|
999
|
+
events: ['submit'],
|
|
1000
|
+
timeout: 10000,
|
|
1001
|
+
retries: 5
|
|
855
1002
|
};
|
|
856
1003
|
|
|
857
1004
|
<ExtendedForm
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1005
|
+
webhooks={[primaryWebhook, fallbackWebhook]}
|
|
1006
|
+
onWebhookError={(webhook, event, error) => {
|
|
1007
|
+
if (webhook.url === primaryWebhook.url) {
|
|
1008
|
+
console.log('Primary webhook failed, fallback will handle');
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Store locally for later sync
|
|
1012
|
+
localStorage.setItem(`pending-${Date.now()}`, JSON.stringify(event));
|
|
1013
|
+
}}
|
|
861
1014
|
/>
|
|
862
1015
|
```
|
|
863
1016
|
|
|
864
|
-
### Why Migrate to v2?
|
|
865
|
-
|
|
866
|
-
- **Performance**: O(N) vs O(M×N) for arrays
|
|
867
|
-
- **Maintainability**: Clear separation of concerns
|
|
868
|
-
- **Type Safety**: Full TypeScript support
|
|
869
|
-
- **Predictability**: No complex rule interactions
|
|
870
|
-
- **Modern React**: Hooks-based architecture
|
|
871
1017
|
|
|
872
1018
|
## API Reference
|
|
873
1019
|
|
|
@@ -890,8 +1036,11 @@ interface ExtendedFormProps<T = any, S extends RJSFSchema = RJSFSchema, F = any>
|
|
|
890
1036
|
// Webhook configuration
|
|
891
1037
|
webhooks?: WebhookConfig[];
|
|
892
1038
|
|
|
893
|
-
//
|
|
894
|
-
|
|
1039
|
+
// Webhook status callbacks
|
|
1040
|
+
onWebhookStart?: (webhook: WebhookConfig, event: any) => void;
|
|
1041
|
+
onWebhookComplete?: (webhook: WebhookConfig, event: any, response: any) => void;
|
|
1042
|
+
onWebhookError?: (webhook: WebhookConfig, event: any, error: Error) => void;
|
|
1043
|
+
|
|
895
1044
|
|
|
896
1045
|
// Form lifecycle events
|
|
897
1046
|
beforeSubmit?: (data: T) => Promise<T> | T;
|
|
@@ -901,7 +1050,69 @@ interface ExtendedFormProps<T = any, S extends RJSFSchema = RJSFSchema, F = any>
|
|
|
901
1050
|
}
|
|
902
1051
|
```
|
|
903
1052
|
|
|
904
|
-
###
|
|
1053
|
+
### WebhookConfig
|
|
1054
|
+
|
|
1055
|
+
```typescript
|
|
1056
|
+
interface WebhookConfig {
|
|
1057
|
+
url: string;
|
|
1058
|
+
method?: 'POST' | 'PUT' | 'PATCH';
|
|
1059
|
+
headers?: Record<string, string>;
|
|
1060
|
+
events?: ('focus' | 'blur' | 'change' | 'submit' | 'stepChange' | 'validationError')[];
|
|
1061
|
+
debounceMs?: number;
|
|
1062
|
+
retries?: number;
|
|
1063
|
+
timeout?: number;
|
|
1064
|
+
}
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
### Webhook Event Types
|
|
1068
|
+
|
|
1069
|
+
```typescript
|
|
1070
|
+
type WebhookEventType = 'focus' | 'blur' | 'change' | 'submit' | 'stepChange' | 'validationError';
|
|
1071
|
+
|
|
1072
|
+
// Base event structure
|
|
1073
|
+
interface BaseWebhookEvent {
|
|
1074
|
+
eventType: WebhookEventType;
|
|
1075
|
+
timestamp: number;
|
|
1076
|
+
formData: any;
|
|
1077
|
+
source: 'extended-dynamic-forms';
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
// Field events
|
|
1081
|
+
interface FieldWebhookEvent extends BaseWebhookEvent {
|
|
1082
|
+
eventType: 'focus' | 'blur' | 'change';
|
|
1083
|
+
fieldId: string;
|
|
1084
|
+
fieldValue: any;
|
|
1085
|
+
schema?: any;
|
|
1086
|
+
fieldPath?: string[];
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
// Submit event
|
|
1090
|
+
interface SubmitWebhookEvent extends BaseWebhookEvent {
|
|
1091
|
+
eventType: 'submit';
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
// Step change event
|
|
1095
|
+
interface StepChangeWebhookEvent extends BaseWebhookEvent {
|
|
1096
|
+
eventType: 'stepChange';
|
|
1097
|
+
fromStep: number;
|
|
1098
|
+
toStep: number;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// Validation error event
|
|
1102
|
+
interface ValidationErrorWebhookEvent extends BaseWebhookEvent {
|
|
1103
|
+
eventType: 'validationError';
|
|
1104
|
+
errors: Array<{
|
|
1105
|
+
property: string;
|
|
1106
|
+
message: string;
|
|
1107
|
+
name: string;
|
|
1108
|
+
argument?: any;
|
|
1109
|
+
stack: string;
|
|
1110
|
+
schemaPath: string;
|
|
1111
|
+
}>;
|
|
1112
|
+
}
|
|
1113
|
+
```
|
|
1114
|
+
|
|
1115
|
+
### Three Pillars Hooks
|
|
905
1116
|
|
|
906
1117
|
```typescript
|
|
907
1118
|
// UI Logic Pillar
|
|
@@ -1055,7 +1266,7 @@ Extended Dynamic Forms uses a layered architecture that cleanly extends RJSF:
|
|
|
1055
1266
|
|---------|------------|----------------------|
|
|
1056
1267
|
| **Form Generation** | ✅ JSON Schema | ✅ Enhanced JSON Schema |
|
|
1057
1268
|
| **UI Components** | Basic HTML | ✅ Comprehensive Ant Design Suite |
|
|
1058
|
-
| **Conditional Logic** | ❌ Manual | ✅ Three Pillars
|
|
1269
|
+
| **Conditional Logic** | ❌ Manual | ✅ Three Pillars (O(N)) |
|
|
1059
1270
|
| **Multi-Step Forms** | ❌ Manual | ✅ Automatic Wizard Detection |
|
|
1060
1271
|
| **Event System** | Basic onChange | ✅ Central Event Orchestration |
|
|
1061
1272
|
| **Webhook Integration** | ❌ None | ✅ Production-Ready with Retry |
|
|
@@ -1108,7 +1319,7 @@ npm run serve:examples # Serve vanilla JS examples
|
|
|
1108
1319
|
### 🧪 Testing
|
|
1109
1320
|
|
|
1110
1321
|
**Test Coverage**: Test suite focuses primarily on:
|
|
1111
|
-
- **Conditional Logic**: Comprehensive coverage for
|
|
1322
|
+
- **Conditional Logic**: Comprehensive coverage for conditional systems (16+ test files)
|
|
1112
1323
|
- **Wizard Forms**: Step navigation and validation
|
|
1113
1324
|
- **Performance**: Array conditional optimization
|
|
1114
1325
|
- **Note**: Limited coverage for UI components, event system, and utility functions
|
|
@@ -1133,7 +1344,7 @@ npm run test -- --grep "array" # Test array handling
|
|
|
1133
1344
|
src/
|
|
1134
1345
|
├── ExtendedForm.tsx # Main form wrapper
|
|
1135
1346
|
├── WizardForm.tsx # Multi-step orchestrator
|
|
1136
|
-
├── conditionals/ #
|
|
1347
|
+
├── conditionals/ # Conditional systems
|
|
1137
1348
|
│ ├── v2/ # Three Pillars architecture
|
|
1138
1349
|
│ └── DynamicArrayConditionalEngine.ts
|
|
1139
1350
|
├── events/ # Central event orchestration
|
|
@@ -1157,13 +1368,12 @@ npm run playground
|
|
|
1157
1368
|
|
|
1158
1369
|
### 📚 Demo Categories
|
|
1159
1370
|
|
|
1160
|
-
> **Note**: Some playground demos may be outdated.
|
|
1371
|
+
> **Note**: Some playground demos may be outdated.
|
|
1161
1372
|
|
|
1162
|
-
#### **Three Pillars
|
|
1373
|
+
#### **Three Pillars Demonstrations**
|
|
1163
1374
|
- **Global UI Rules**: Dynamic show/hide with JSON Patch operations
|
|
1164
1375
|
- **Dynamic Array Items**: Per-item conditional logic with O(N) performance
|
|
1165
1376
|
- **Conditional Validation**: Custom validation rules without schema changes
|
|
1166
|
-
- **Migration Examples**: Automated v1-to-v2 rule conversion
|
|
1167
1377
|
|
|
1168
1378
|
#### **🧙♂️ Wizard Form Showcases**
|
|
1169
1379
|
- **Australian Company Onboarding**: 5-step enterprise form with:
|
|
@@ -1176,7 +1386,9 @@ npm run playground
|
|
|
1176
1386
|
#### **🔗 Event System Demos**
|
|
1177
1387
|
- **Real-time Form Analytics**: Webhook integration with retry logic
|
|
1178
1388
|
- **Field-Level Event Handling**: Focus/blur/change event orchestration
|
|
1179
|
-
- **
|
|
1389
|
+
- **Form-Level Event Handling**: Submit, validation error, and step change events
|
|
1390
|
+
- **Webhook Configuration**: Multiple endpoints with custom headers and event filtering
|
|
1391
|
+
- **Webhook Status Tracking**: Callbacks for monitoring webhook success/failure
|
|
1180
1392
|
|
|
1181
1393
|
#### **🎨 Widget Showcase**
|
|
1182
1394
|
- **Enhanced Standard Widgets**: All RJSF widgets with Ant Design styling
|
|
@@ -1198,12 +1410,128 @@ wizard-form.html <!-- Multi-step onboarding -->
|
|
|
1198
1410
|
|
|
1199
1411
|
- **[Playground](https://extended-dynamic-forms-playground.vercel.app)** - Interactive demo environment
|
|
1200
1412
|
- **[Australian Company Onboarding](https://extended-dynamic-forms-playground.vercel.app/wizard/australian-company)** - Full enterprise form showcase
|
|
1201
|
-
- **[Three Pillars
|
|
1413
|
+
- **[Three Pillars Demo](https://extended-dynamic-forms-playground.vercel.app/conditionals/three-pillars)** - Modern conditional logic patterns
|
|
1202
1414
|
|
|
1203
1415
|
## Contributing
|
|
1204
1416
|
|
|
1205
1417
|
We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.
|
|
1206
1418
|
|
|
1419
|
+
## Migration Guide: Enhanced Webhook Events
|
|
1420
|
+
|
|
1421
|
+
### Upgrading to Enhanced Webhook System
|
|
1422
|
+
|
|
1423
|
+
The enhanced webhook system is **fully backward compatible**. Existing webhook configurations will continue to work without any changes. The new event types are additive and opt-in.
|
|
1424
|
+
|
|
1425
|
+
#### For Existing Webhook Users
|
|
1426
|
+
|
|
1427
|
+
Your current webhook configurations will continue to work exactly as before:
|
|
1428
|
+
|
|
1429
|
+
```typescript
|
|
1430
|
+
// This existing configuration still works perfectly
|
|
1431
|
+
const webhookConfig: WebhookConfig = {
|
|
1432
|
+
url: 'https://api.example.com/form-events',
|
|
1433
|
+
method: 'POST',
|
|
1434
|
+
debounceMs: 500
|
|
1435
|
+
};
|
|
1436
|
+
// Without the 'events' array, it receives ALL events (backward compatible)
|
|
1437
|
+
```
|
|
1438
|
+
|
|
1439
|
+
To take advantage of new event types:
|
|
1440
|
+
|
|
1441
|
+
```typescript
|
|
1442
|
+
// Option 1: Subscribe to specific new events only
|
|
1443
|
+
const webhookConfig: WebhookConfig = {
|
|
1444
|
+
url: 'https://api.example.com/form-events',
|
|
1445
|
+
events: ['submit', 'validationError'], // Only these new events
|
|
1446
|
+
debounceMs: 1000
|
|
1447
|
+
};
|
|
1448
|
+
|
|
1449
|
+
// Option 2: Mix old and new events
|
|
1450
|
+
const webhookConfig: WebhookConfig = {
|
|
1451
|
+
url: 'https://api.example.com/form-events',
|
|
1452
|
+
events: ['change', 'blur', 'submit', 'stepChange'], // Mixed event types
|
|
1453
|
+
debounceMs: 500
|
|
1454
|
+
};
|
|
1455
|
+
```
|
|
1456
|
+
|
|
1457
|
+
#### For New Implementations
|
|
1458
|
+
|
|
1459
|
+
If you're implementing webhooks for the first time, consider these patterns:
|
|
1460
|
+
|
|
1461
|
+
```typescript
|
|
1462
|
+
// Complete form lifecycle monitoring
|
|
1463
|
+
const lifecycleWebhook: WebhookConfig = {
|
|
1464
|
+
url: 'https://api.example.com/form-lifecycle',
|
|
1465
|
+
events: ['focus', 'change', 'blur', 'submit', 'validationError'],
|
|
1466
|
+
method: 'POST',
|
|
1467
|
+
headers: {
|
|
1468
|
+
'X-API-Key': 'your-api-key'
|
|
1469
|
+
},
|
|
1470
|
+
debounceMs: 500,
|
|
1471
|
+
retries: 3,
|
|
1472
|
+
timeout: 10000
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
// Wizard-specific tracking
|
|
1476
|
+
const wizardWebhook: WebhookConfig = {
|
|
1477
|
+
url: 'https://analytics.example.com/wizard-tracking',
|
|
1478
|
+
events: ['stepChange', 'submit'],
|
|
1479
|
+
method: 'POST',
|
|
1480
|
+
debounceMs: 0, // No debouncing for navigation events
|
|
1481
|
+
retries: 1
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
// Error monitoring only
|
|
1485
|
+
const errorWebhook: WebhookConfig = {
|
|
1486
|
+
url: 'https://monitoring.example.com/form-errors',
|
|
1487
|
+
events: ['validationError'],
|
|
1488
|
+
method: 'POST',
|
|
1489
|
+
retries: 5 // Ensure error tracking is reliable
|
|
1490
|
+
};
|
|
1491
|
+
```
|
|
1492
|
+
|
|
1493
|
+
#### Using Webhook Status Callbacks
|
|
1494
|
+
|
|
1495
|
+
The new webhook status callbacks provide visibility into webhook processing:
|
|
1496
|
+
|
|
1497
|
+
```typescript
|
|
1498
|
+
<ExtendedForm
|
|
1499
|
+
schema={schema}
|
|
1500
|
+
webhooks={webhooks}
|
|
1501
|
+
onWebhookStart={(webhook, event) => {
|
|
1502
|
+
// Show loading indicator
|
|
1503
|
+
setWebhookLoading(true);
|
|
1504
|
+
}}
|
|
1505
|
+
onWebhookComplete={(webhook, event, response) => {
|
|
1506
|
+
// Hide loading indicator
|
|
1507
|
+
setWebhookLoading(false);
|
|
1508
|
+
|
|
1509
|
+
// Handle success
|
|
1510
|
+
if (response.ok) {
|
|
1511
|
+
showNotification('Data saved successfully');
|
|
1512
|
+
}
|
|
1513
|
+
}}
|
|
1514
|
+
onWebhookError={(webhook, event, error) => {
|
|
1515
|
+
// Hide loading indicator
|
|
1516
|
+
setWebhookLoading(false);
|
|
1517
|
+
|
|
1518
|
+
// Handle error gracefully
|
|
1519
|
+
console.error('Webhook failed:', error);
|
|
1520
|
+
showNotification('Saving to server failed, will retry later');
|
|
1521
|
+
|
|
1522
|
+
// Store for offline sync
|
|
1523
|
+
queueForLaterSync(event);
|
|
1524
|
+
}}
|
|
1525
|
+
/>
|
|
1526
|
+
```
|
|
1527
|
+
|
|
1528
|
+
#### Important Notes
|
|
1529
|
+
|
|
1530
|
+
1. **Event Filtering**: If you specify the `events` array, only those events will trigger webhooks
|
|
1531
|
+
2. **Debouncing**: Form-level events (submit, stepChange, validationError) respect debounceMs settings
|
|
1532
|
+
3. **Payload Structure**: All events now include standard fields (timestamp, formData, eventType, source)
|
|
1533
|
+
4. **No Breaking Changes**: All existing functionality remains unchanged
|
|
1534
|
+
|
|
1207
1535
|
## Community & Support
|
|
1208
1536
|
|
|
1209
1537
|
- 📚 [Documentation](https://extended-dynamic-forms.dev)
|