@bolttech/form-engine 0.1.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +1172 -0
  3. package/index.js +2 -0
  4. package/index.js.map +1 -0
  5. package/package.json +50 -0
  6. package/src/adapters/react/Field.d.ts +4 -0
  7. package/src/adapters/react/Form.d.ts +5 -0
  8. package/src/adapters/react/Submit.d.ts +3 -0
  9. package/src/adapters/react/asFormField.d.ts +14 -0
  10. package/src/adapters/react/context.d.ts +5 -0
  11. package/src/adapters/react/index.d.ts +6 -0
  12. package/src/adapters/react/types.d.ts +185 -0
  13. package/src/adapters/react/useForceUpdate.d.ts +3 -0
  14. package/src/adapters/react/useForm.d.ts +51 -0
  15. package/src/adapters/react/useFormGroup.d.ts +15 -0
  16. package/src/core/apis/formatters.d.ts +16 -0
  17. package/src/core/apis/index.d.ts +4 -0
  18. package/src/core/apis/masks.d.ts +3 -0
  19. package/src/core/apis/validations.d.ts +11 -0
  20. package/src/core/apis/varOps.d.ts +4 -0
  21. package/src/core/constants/events.d.ts +23 -0
  22. package/src/core/constants/index.d.ts +5 -0
  23. package/src/core/constants/observer.d.ts +7 -0
  24. package/src/core/events/ObserverError.d.ts +7 -0
  25. package/src/core/events/events.types.d.ts +27 -0
  26. package/src/core/events/index.d.ts +2 -0
  27. package/src/core/handlers/common/templating.d.ts +2 -0
  28. package/src/core/handlers/field/api.d.ts +4 -0
  29. package/src/core/handlers/field/blur.d.ts +2 -0
  30. package/src/core/handlers/field/change.d.ts +2 -0
  31. package/src/core/handlers/field/clearFields.d.ts +4 -0
  32. package/src/core/handlers/field/data.d.ts +3 -0
  33. package/src/core/handlers/field/filter.d.ts +2 -0
  34. package/src/core/handlers/field/focus.d.ts +2 -0
  35. package/src/core/handlers/field/formatters.d.ts +4 -0
  36. package/src/core/handlers/field/htmlEventParser.d.ts +6 -0
  37. package/src/core/handlers/field/masks.d.ts +4 -0
  38. package/src/core/handlers/field/mount.d.ts +2 -0
  39. package/src/core/handlers/field/validations.d.ts +4 -0
  40. package/src/core/handlers/field/visibilityConditions.d.ts +4 -0
  41. package/src/core/handlers/flows.d.ts +36 -0
  42. package/src/core/handlers/form/hooks.d.ts +3 -0
  43. package/src/core/handlers/form/steps.d.ts +2 -0
  44. package/src/core/handlers/form/templating.d.ts +2 -0
  45. package/src/core/handlers/form/validate.d.ts +2 -0
  46. package/src/core/handlers/form/visibilityConditions.d.ts +4 -0
  47. package/src/core/index.d.ts +3 -0
  48. package/src/core/managers/Base.d.ts +19 -0
  49. package/src/core/managers/Factory.d.ts +52 -0
  50. package/src/core/managers/Field.d.ts +31 -0
  51. package/src/core/managers/Form.d.ts +32 -0
  52. package/src/core/managers/Scope.d.ts +12 -0
  53. package/src/core/managers/index.d.ts +1 -0
  54. package/src/core/types/index.d.ts +621 -0
  55. package/src/core/utils/credit-card.d.ts +16 -0
  56. package/src/core/utils/index.d.ts +4 -0
  57. package/src/core/utils/object.d.ts +24 -0
  58. package/src/core/utils/string.d.ts +3 -0
  59. package/src/index.d.ts +3 -0
  60. package/types.js +2 -0
  61. package/types.js.map +1 -0
package/README.md ADDED
@@ -0,0 +1,1172 @@
1
+ # Form Engine - Low code forms
2
+
3
+ Achieve form logic reusage with forms expressed in json format.
4
+
5
+ > version 0.3.68
6
+
7
+ ---
8
+
9
+ 1. [Basic setup](#markdown-header-basic-setup)
10
+ 2. [Step by step](#markdown-header-step-by-step)
11
+ 3. [Form Features](#markdown-header-available-features)
12
+ 3.1. [Validations - Allow form to run validations in the field](#markdown-header-validations)
13
+ 3.1.1. [Named Validations](#markdown-header-validations)
14
+ 3.1.2. [Error Messages](#markdown-header-validations)
15
+ 3.1.3. [Available Validations](#markdown-header-validations)
16
+ 3.2. [Filters - Allow only what you want in the field](#markdown-header-filters)
17
+ 3.2.1. [Available formatters](#markdown-header-formatters)
18
+ 3.3. [Formatters - Style your field value with formatters](#markdown-header-formatters)
19
+ 3.4. [Masks - Modify the field value, while maintaining the original with masks](#markdown-header-masks)
20
+ 3.4.1. [Available Masks](#markdown-header-formatters)
21
+ 3.5. [Visibility conditions - Configure when to show hide/components](#markdown-header-visibility-conditions)
22
+ 3.6. [Clear fields](#markdown-header-clear-fields)
23
+ 3.7. [Api - Make api calls on certain form events](#markdown-header-api)
24
+ 3.8. [Data binding - Allow to have dynamic data in the form, binding and subscribing to form changes](#markdown-header-data-binding)
25
+ 3.8.1. [Scopes](#markdown-header-scope)
26
+ 3.8.2. [Templates](#markdown-header-templates)
27
+ 3.8.3. [varOps](#markdown-header-varops)
28
+ 3.9. [State - Define component state](#markdown-header-state)
29
+ 3.10. [Group](#markdown-header-group)
30
+ 4. [React](#markdown-header-react-components)
31
+ 4.1. [FormProvider - Provider to configure the form with component mappings](#markdown-header-react-formprovider)
32
+ 4.2. [Form - Component to render a form based on a schema and listen to some events](#markdown-header-react-form)
33
+ 4.3. [useForm hook- Allo to connect to any form in the page](#markdown-header-react-useform)
34
+ 4.4. [asFormField HOC- Leverage form features but keep the control of your component](#markdown-header-react-asFormField-HOC)
35
+
36
+ ## **Basic setup**
37
+
38
+ Serve your forms in JSON to your frontend, and allow it to be agnostic of your forms logic.
39
+
40
+ 3 simple steps
41
+
42
+ 1. Map your components to the form (Section - Build your mappers)
43
+ 2. Build json schema
44
+ 3. Use it
45
+
46
+ **1. BUILD MAPPERS**
47
+
48
+ ```javascript
49
+ import Input from 'Components/Input';
50
+ import Other from 'Components/Other';
51
+
52
+ const Mappings = {
53
+ input: { component: Input },
54
+ other: { component: Other },
55
+ };
56
+
57
+ const formBuilderPropsMapping = {
58
+ //default prop names
59
+ __default__: {
60
+ getValue: 'onChange',
61
+ setValue: 'value',
62
+ },
63
+ //component specific prop names
64
+ other: {
65
+ getValue: 'onChangeCallback',
66
+ setValue: 'data',
67
+ setErrorMessage: 'errorMessageArray',
68
+ setErrorState: 'isErrored',
69
+ onBlur: 'onBlurCallback',
70
+ onFocus: 'onFocusCallback',
71
+ },
72
+ };
73
+
74
+ export { Mappings, formBuilderPropsMapping };
75
+ ```
76
+
77
+ **2. BUILD SCHEMA**
78
+
79
+ ```json
80
+ {
81
+ "components": [
82
+ {
83
+ "component": "",
84
+ "name": "",
85
+ "children": [
86
+ {
87
+ "component": "${componentName}",
88
+ "name": "${componentFormName}",
89
+ "props": {
90
+ "fullWidth": true
91
+ }
92
+ }
93
+ ]
94
+ }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ **USE IT (React version)**
100
+
101
+ ```javascript
102
+ import { Mappings, formBuilderPropsMapping } from './my-component-mappings';
103
+ import { getFormSchema } from './my-api-wrapper';
104
+ ...
105
+ const schema = useMemo(() => getFormSchema('myInstanceContext'), []);
106
+
107
+ ...
108
+ <Form mappings={Mappings} propsMappings={formBuilderPropsMapping} schema={schema}>
109
+ ```
110
+
111
+ Nexts steps ? Checkout what you can do in the storybook with `npm run storybook` or see the best effort readme :(
112
+
113
+ ## **Step by step**
114
+
115
+ ### Build your mappers
116
+
117
+ The form uses mappings to connect to UI components so that its easy to connect to any set of components.
118
+
119
+ You can build your own mappings file or you can use the `bolttech` and if you want extend it with your set of components.
120
+
121
+ In the mappings file you need to specify the component definition and a name to refer in the JSON's latter, and how the form will connect to component props.
122
+
123
+ See this example
124
+
125
+ ```javascript
126
+ import Input from '@bit/bolttech.components.ui.input';
127
+ import Checkbox from '@bit/bolttech.components.ui.checkbox';
128
+ import FormGroup from '@bit/bolttech.components.common.form-group';
129
+
130
+ const Mappings = {
131
+ input: { component: Input },
132
+ checkbox: { component: Checkbox },
133
+ formGroup: { component: FormGroup },
134
+ };
135
+
136
+ const formBuilderPropsMapping = {
137
+ input: {
138
+ getValue: 'onChange',
139
+ setValue: 'value',
140
+ setErrorMessage: 'errorMessage',
141
+ setErrorState: 'isErrored',
142
+ onBlur: 'onBlur',
143
+ onFocus: 'onFocus',
144
+ },
145
+ checkbox: {
146
+ getValue: 'onChange',
147
+ setValue: 'checked',
148
+ },
149
+ };
150
+
151
+ export { Mappings, formBuilderPropsMapping };
152
+ ```
153
+
154
+ Here you say to the form that you can use in your JSON the names `input`, `checkbox` and `formGroup` and you tell the form how to get the props it needs from them.
155
+
156
+ If you have lots of components with the same prop names, you can, and should use `__default__` key. This key allows to reuse prop names.
157
+
158
+ Lets say 10 components use to `value` prop name to set the component value, and `onChange` prop name to expose value. You van set your mapper the following way
159
+
160
+ ```javascript
161
+ import Input from '@bit/bolttech.components.ui.input';
162
+ import Checkbox from '@bit/bolttech.components.ui.checkbox';
163
+
164
+ const Mappings = {
165
+ input: { component: Input },
166
+ checkbox: { component: Checkbox },
167
+ };
168
+
169
+ const formBuilderPropsMapping = {
170
+ __default____: {
171
+ getValue: 'onChange',
172
+ setValue: 'value',
173
+ },
174
+ checkbox: {
175
+ getValue: 'onChange',
176
+ setValue: 'checked',
177
+ },
178
+ };
179
+
180
+ export { Mappings, formBuilderPropsMapping };
181
+ ```
182
+
183
+ ### Setup Form provider
184
+
185
+ After setting your own mappings you encapsulate your app of your form with the provider
186
+
187
+ ```javascript
188
+ <FormProvider mapper={Mappings} propsMapping={formBuilderPropsMapping}>
189
+ {children}
190
+ </FormProvider>
191
+ ```
192
+
193
+ **DONE. NOW build your forms**
194
+
195
+ ## **Form Features**
196
+
197
+ Inside the schema you can specify several actions for a field alone or that correlate and have side-effects between them.
198
+
199
+ Those actions support support multiple lifecycle and this must be on an action item basis:
200
+
201
+ - ON_FIELD_MOUNT
202
+ - ON_FIELD_CHANGE
203
+ - ON_FIELD_BLUR
204
+ - ON_FIELD_FOCUS
205
+
206
+ All the actions are typed, so you will have help here seeing which lifecycles you have available
207
+
208
+ Per action, you will be able to combine multiple lifecycle methods
209
+
210
+ All the following features can be inserted in the same location on the schema
211
+
212
+ ```json
213
+ {
214
+ "component": "input",
215
+ "name": "fieldName",
216
+ "props": {
217
+ "label": "My field"
218
+ }
219
+ //...your feature goes here
220
+ }
221
+ ```
222
+
223
+ ## **Validations**
224
+
225
+ Like the name say, this feature lets you validate the form in the several lifecycle events of the form.
226
+
227
+ ```json
228
+ {
229
+ "validations": {
230
+ "ON_FIELD_BLUR": {
231
+ "email": true
232
+ },
233
+ "ON_FIELD_CHANGE": {
234
+ "required": true
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ The above example will let form know that in each change the field must have something in it, and that on blur, the value must be a email.
241
+
242
+ ### Named validations
243
+
244
+ There are cases where you want to build your own validation, agregating several of giving it a specific name that you can refer later
245
+
246
+ ```json
247
+ {
248
+ "validations": {
249
+ "ON_FIELD_BLUR": {
250
+ "blurRequire": {
251
+ "require": true
252
+ }
253
+ },
254
+ "ON_FIELD_CHANGE": {
255
+ "email": true,
256
+ "changeRequire": {
257
+ "require": true
258
+ },
259
+ "changeRestOfValidations": {
260
+ "length": 50
261
+ //...
262
+ }
263
+ }
264
+ }
265
+ }
266
+ ```
267
+
268
+ ### Error Messages
269
+
270
+ You can also specify the error messages you want.
271
+
272
+ ```json
273
+ {
274
+ "validations": {
275
+ "ON_FIELD_BLUR": {
276
+ "require": true
277
+ },
278
+ "ON_FIELD_CHANGE": {
279
+ "email": true
280
+ }
281
+ },
282
+ "errorMessages": {
283
+ "default": "Default error message",
284
+ "email": "Invalid e-mail"
285
+ }
286
+ }
287
+ ```
288
+
289
+ This schema part, will add messages to validations error.
290
+
291
+ - Each time the field has an e-mail error it will send the "Invalid e-mail" message to the component
292
+ - If there is and field error, but no message is specified, it will send what you have in `default` key. In this example, `required` error does not have message and will send "Default error message"
293
+
294
+ **With named validations**
295
+
296
+ If you have a named validation, you can use its name in the error messages, having better granularity on it.
297
+
298
+ ```json
299
+ {
300
+ "validations": {
301
+ "ON_FIELD_BLUR": {
302
+ "blurRequire": {
303
+ "require": true
304
+ }
305
+ },
306
+ "ON_FIELD_CHANGE": {
307
+ "email": true,
308
+ "changeRequire": {
309
+ "require": true
310
+ },
311
+ "changeRestOfValidations": {
312
+ "length": 50
313
+ //...
314
+ }
315
+ }
316
+ },
317
+ "errorMessages": {
318
+ "default": "Default error message",
319
+ "email": "Invalid e-mail",
320
+ "blurRequire": "When you blur, this component is required",
321
+ "changeRequire": "You should not leave the field blank",
322
+ "changeRestOfValidations": "You are changing into an incorrect state"
323
+ }
324
+ }
325
+ ```
326
+
327
+ ### Available validations (TBD)
328
+
329
+ Refer to the types on `TSchema`
330
+
331
+ ## **Formatters**
332
+
333
+ Formatting a field means mutating the field value to a given... format.
334
+
335
+ This options will allow you to force a give field to have the format you whant while the user is performing some action on the form.
336
+
337
+ **NOTE** - When receiving the values of the form, you will have the value with the specified format, not the raw value the user entered
338
+
339
+ You have several formatters. THe following example shows splitter that is a more generic one, allowing you to split the input text
340
+
341
+ ```json
342
+ {
343
+ "formatters": {
344
+ "ON_FIELD_MOUNT": {
345
+ "splitter": [
346
+ {
347
+ "position": 2,
348
+ "value": "/"
349
+ },
350
+ {
351
+ "position": 5,
352
+ "value": "/"
353
+ }
354
+ ]
355
+ }
356
+ }
357
+ }
358
+
359
+ ```
360
+
361
+ The above example will split your word in position 2 and 5, adding there the `/`. This will give you a date format like `10/10/1987` (you would have to limit the input length. More on that on FILTERS)
362
+
363
+ ### Available Formatters (TBD)
364
+
365
+ Refer to the types on `TSchema`
366
+
367
+ ## Masks
368
+
369
+ Mask has the same functionality of formatter, but keed the original value for your program. Think of it like the password mask. You input something into your text input, mask that something with `*` but you need to read the original value. FOr Eg.
370
+
371
+ ```json
372
+ {
373
+ "masks": {
374
+ "ON_FIELD_BLUR": {
375
+ "replaceAll": "*"
376
+ },
377
+ "ON_FIELD_FOCUS": {
378
+ "cleanMask": true
379
+ }
380
+ }
381
+ }
382
+ ```
383
+
384
+ In this example, you will
385
+
386
+ - Mask a given text input from for example `123345` to `******`
387
+ - On Focus , you tell form to clean the mask with `cleanMask` directive.
388
+
389
+ ### Available Masks (TBD)
390
+
391
+ Refer to the types on `TSchema`
392
+
393
+ ## **Filters**
394
+
395
+ Filters very predictable and work like the word says, they filter a given word to a given patter/directive.
396
+
397
+ Lets say you want a field to accept only numbers and with a max length o X.
398
+
399
+ ```json
400
+ {
401
+ "filter": {
402
+ "length": 4,
403
+ "isNumber": true
404
+ }
405
+ }
406
+ ```
407
+
408
+ This example will let you do just that. Only numbers and max length of 4
409
+
410
+ ### Available filters (TBS)
411
+
412
+ Refer to the types on `TSchema`
413
+
414
+ ## Visibility conditions
415
+ ### Field level
416
+ Sometimes you want to hide other fields based on certain conditions.
417
+
418
+ That is what this feature does.
419
+
420
+ Eg: You want to hide another field, when a given field `originalField` has a given value on it.
421
+
422
+ ```json
423
+ [
424
+ {
425
+ "name": "originalField",
426
+ "component": "checkbox",
427
+ "visibilityConditions": {
428
+ "ON_FIELD_MOUNT": [
429
+ {
430
+ "validations": {
431
+ "value": "Yes"
432
+ },
433
+ "fieldName": "targetField"
434
+ }
435
+ ],
436
+ "ON_FIELD_CHANGE": [
437
+ {
438
+ "validations": {
439
+ "value": "Yes"
440
+ },
441
+ "fieldName": "targetField"
442
+ }
443
+ ]
444
+ },
445
+ "props": {
446
+ //...
447
+ }
448
+ },
449
+ {
450
+ "name": "targetField",
451
+ "component": "input",
452
+ "props": {
453
+ //...
454
+ }
455
+ }
456
+ ]
457
+ ```
458
+
459
+ This example tells form to
460
+
461
+ - On mount check if `originalField` has the value `Yes`
462
+ - If it put the `targetField` visible
463
+ - Otherwise, make it invisible
464
+
465
+ You can also for each visibility condition, apply it to multiple field names with `fieldNames` key that will accept an array.
466
+
467
+ ```json
468
+ {
469
+ "visibilityConditions": {
470
+ "ON_FIELD_MOUNT": [
471
+ {
472
+ "validations": {
473
+ "value": "Yes"
474
+ },
475
+ "fieldNames": [
476
+ "targetFieldOne",
477
+ "targetFieldTwo"
478
+ ]
479
+ }
480
+ ]
481
+ }
482
+ }
483
+ ```
484
+
485
+ **NOTE** - When the field is hidden using this feature, the form will not try to validate it and will not be accounted to the general form state
486
+
487
+ ### Form Level
488
+ You can also use those in form level.
489
+
490
+ ```javascript
491
+ <Form
492
+ iVars={{ roofUpdated: state }}
493
+ initialValues={{ roofUpdatedYear: 'diogos' }}
494
+ schema={{
495
+ visibilityConditions: {
496
+ ON_FORM_MOUNT: [
497
+ {
498
+ validations: {
499
+ value: '${global.roofUpdated}',
500
+ },
501
+ fieldName: 'roofUpdatedYear',
502
+ },
503
+ ],
504
+ ON_FIELD_CHANGE: [
505
+ {
506
+ validations: {
507
+ value: 'abc',
508
+ },
509
+ fieldName: 'roofUpdatedYear',
510
+ },
511
+ ],
512
+ },
513
+ components: [{...}],
514
+ }}
515
+ />
516
+ ```
517
+
518
+ in the above example we are applying the rule:
519
+ - in form mount we will hide the value when the `roofUpdatedYear` equals to the iVar `roofUpdated`
520
+ - in each field change we will hide the value when the `roofUpdatedYear` equals to `abc`
521
+
522
+ ## Clear Fields
523
+
524
+ Guess what... THis will clear one or more form fields :)
525
+
526
+ Uses the same mechanism of VISIBILITY CONDITIONS.
527
+
528
+ Let's say you want to clear a given field when `originalField` has a given value.
529
+
530
+ ```json
531
+ {
532
+ "clearFields": {
533
+ "ON_FIELD_CHANGE": [
534
+ {
535
+ "validations": {
536
+ "value": "Yes"
537
+ },
538
+ "field": "targetValue",
539
+ "clearedValue": false
540
+ }
541
+ ]
542
+ }
543
+ }
544
+ ```
545
+
546
+ When form fires ON_CHANGE this will have the effect of having the field `targetValue` with the value `false` if `originalField` has value `Yes`.
547
+
548
+ Just like before, you can specify multiple fields with `fields` key for the same rule.
549
+
550
+ ```json
551
+ {
552
+ "clearFields": {
553
+ "ON_FIELD_CHANGE": [
554
+ {
555
+ "validations": {
556
+ "value": "Yes"
557
+ },
558
+ "fields": [
559
+ "targetValue"
560
+ ],
561
+ "clearedValue": false
562
+ }
563
+ ]
564
+ }
565
+ }
566
+ ```
567
+
568
+ ## Api
569
+
570
+ This one will let you instruct form to call a give API at a given lifecycle method
571
+
572
+ ```json
573
+ {
574
+ "api": {
575
+ "ON_FIELD_CHANGE": [
576
+ {
577
+ "blockRequestWhenInvalid": true,
578
+ "method": "GET",
579
+ "url": "https://api.chucknorris.io/jokes/random",
580
+ "scope": "chuck"
581
+ }
582
+ ]
583
+ }
584
+ }
585
+ ```
586
+
587
+ The above example will make form to call the API specified when the field where we gave the directory changes.
588
+
589
+ ### Keys
590
+
591
+ | key | type | Description |
592
+ |-------------------------|--------------|-----------------------------------------------------------------------------------------------------------------------------------------|
593
+ | blockRequestWhenInvalid | boolean | Specify if this call should be blocked when the field is invalid (due to validations) |
594
+ | method | string | HTTP verb. Get, Post, Put or delete |
595
+ | url | string | The api url |
596
+ | scope | string | This lets you put the api result inside the form scope in the given key. THis will allow to use the call result latter on on some field |
597
+ | body | object | Body to send to the API |
598
+ | headers | object | Api headers |
599
+ | debounceTime | number | Allow you to debounce the api call by X seconds |
600
+ | preConditions | TValidations | Allow you to specify validations that should not fail in order to call the API |
601
+
602
+ ### PreConditions
603
+
604
+ You can specify the pre-conditions that need to be met, in order for the request to start.
605
+
606
+ ```json
607
+ {
608
+ "api": {
609
+ "ON_FIELD_CHANGE": [
610
+ {
611
+ "method": "GET",
612
+ "url": "https://api.chucknorris.io/jokes/random",
613
+ "scope": "chuck",
614
+ "preConditions": {
615
+ "required": true,
616
+ "value": "run"
617
+ }
618
+ }
619
+ ]
620
+ }
621
+ }
622
+ ```
623
+
624
+ In the above example, the api specified will only be called
625
+
626
+ 1. When field has changes
627
+ 2. When field has values (required validation)
628
+ 3. When the field value is "run"
629
+
630
+ ## Data binding
631
+
632
+ Form has a functionality to allow you to build your logic inside the schema via templating.
633
+
634
+ You can emit and register to data between fields. For example, in the following example field `one` will register to changes on field `two` and its label will have the field `two` value. This is accomplished with [scopes](#scope).
635
+
636
+ ```json
637
+ [
638
+ {
639
+ "name": "one",
640
+ "component": "input",
641
+ "props": {
642
+ "label": "${fields.two.value}"
643
+ }
644
+ },
645
+ {
646
+ "name": "one",
647
+ "component": "input",
648
+ "props": {
649
+ //...
650
+ }
651
+ }
652
+ ]
653
+ ```
654
+
655
+ The subscription is done using the template first keys. In this case `fields` and `two`. Telling the engine that anytime the namespace `fields` and key `two` changes it should fire a notification to anyone interested. In this case, field `one` is interested
656
+
657
+ ### Scope
658
+
659
+ For templating to work, form relies on scope. The definition of scope is just a datastructures that has multiple keys each one with their context. The following table explain the namespaces
660
+
661
+ | namespace | description |
662
+ |-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
663
+ | global | This namespace contains all the data that comes from the client implementing the Form and is injected in iVars |
664
+ | fields | Automatically generated scope. This namespace contains all the fields with everything that is done in them per field. Eg: value, errors, visible, mask etc. Refer to the types for more info |
665
+ | api | This scope is where you can store the api responses with the api scope key. |
666
+ | hooks | This one is retrieved by the hooks configured on the client |
667
+ | configs | All the configs that the client gave to the form, will be stored here |
668
+
669
+ Templating basically allows a given component to subscribe to any scope change, be notified and changed according to that. In the following example, the component named `make` is subscribed to `api` namespace on `data` key.
670
+
671
+ ```json
672
+ {
673
+ "name": "make",
674
+ "component": "dropdown",
675
+ "props": {
676
+ "id": "make",
677
+ "name": "make",
678
+ "label": "Make",
679
+ "placeholder": "",
680
+ "options": "${api.makes.data||[]}"
681
+ }
682
+ }
683
+ ```
684
+
685
+ This means that, anytime that `api.makes.data` changes (done by api action with scope = data), this component will be injected with its value on the `options` key. It also has a default value of empty array `...data||[]}`.
686
+
687
+ If you want you can even nest templating also. Next example we will access to `global` namespace on `name` key. But we will access the prop dynamically from the field named `myfield` value
688
+
689
+ ```json
690
+ {
691
+ "component": "input",
692
+ "name": "destination",
693
+ "props": {
694
+ "name": "destination",
695
+ "label": "Dynamic -> ${global.name.${fields.myfield.value||test}}"
696
+ }
697
+ }
698
+ ```
699
+
700
+ This will result in the following. Assume we have scope like
701
+
702
+ ```json
703
+ {
704
+ "global": {
705
+ "name": {
706
+ "test": "test",
707
+ "other": "other"
708
+ }
709
+ }
710
+ }
711
+ ```
712
+
713
+ It will access `global.name.test` since we have the default value as test and there is no data in fields scope. The end result would be _"Dynamic -> test"_
714
+
715
+ But right after input on the form field named `myfield`, its scope will be populated
716
+
717
+ ```json
718
+ {
719
+ "global": {
720
+ "name": {
721
+ "test": "test",
722
+ "other": "other"
723
+ }
724
+ },
725
+ "fields": {
726
+ "myfield": {
727
+ "value": "other"
728
+ //...
729
+ }
730
+ }
731
+ }
732
+ ```
733
+
734
+ In this case would access `global.name.other` and the final result would be _"Dynamic -> other"_
735
+
736
+ ### Templates
737
+
738
+ We talked about templates, but let's go a step further. THe definition of template, is a just a string that has a given prefix and suffix like `${...}`.
739
+
740
+ Whatever comes inside the delimiters will be later extracted by the engine and mapped with the current scope in order to find a replacement value.
741
+
742
+ The only limitation is that the template must be a string representing an object path. That object path will be looked for in the [scope](#scope) like `#{api.myapicall.response.data.value}`.
743
+
744
+ **Default values**
745
+
746
+ You can set template default values with `||` like `${fields.foo.value||default-value}`. This will lead to, if the scope has value in `fields.foo` set the value in template value, otherwise set the string `default-value`
747
+
748
+ **Template nesting**
749
+
750
+ You can also nest multiple templates reaching extreme situations. For example
751
+
752
+ `${fields.${gloval.fieldname||foo}.value||novalue}`
753
+
754
+ This example will give you the following possible replacements:
755
+
756
+ - If `global.fieldname` exists and has value `bar` for example, and form `bar` field contains value lets say value 2 - Output will be 2
757
+ - If `global.fieldname` exists and has value `bar` for example, and form `bar` field does not contain value - Output will be non value
758
+ - If `global.fieldname` does not exists , and form `foo` field does not contain value - Output will be non value
759
+ - If `global.fieldname` does not exists , and form `foo` field contains value lets say value 3 - Output will be 3
760
+
761
+ ### VarOps
762
+
763
+ Templates are already a great power of form-engine, but we can go further allowing operations to be specified in the schema. Those operations are all under `varOps` (variable operations).
764
+
765
+ ```json
766
+ {
767
+ "component": "input",
768
+ "name": "password",
769
+ "errorMessages": {
770
+ "required": "Password is required",
771
+ "value": "Error value must be varOps.concatenate(${fields.email.value||0},${fields.email2.value||0})"
772
+ },
773
+ "validations": {
774
+ "ON_FIELD_CHANGE": {
775
+ "required": true,
776
+ "value": "varOps.concatenate(${fields.email.value||0},${fields.email2.value||0})"
777
+ }
778
+ },
779
+ "props": {
780
+ "variants": "default_border",
781
+ "placeholder": "Please enter your password",
782
+ "label": "Password"
783
+ }
784
+ }
785
+ ```
786
+
787
+ In the example the validation value comes from a `varOps`. This example uses the `concatenate` operations exposed by the engine.
788
+
789
+ Here this field (`password`) will register with [templating](#templates) to field `email` and `email2`. Meaning, each time they change this field schema will be recomputed with what changed to replace the needed values.
790
+
791
+ When this happens, lets say `email` has value foo and `email2` value bar. The `varOp` concatenate will be called with the correct field replaced values
792
+
793
+ ```javascript
794
+ varOps.concatenate('foo', 'bar');
795
+ ```
796
+
797
+ This will map to an operation function and the function return value will be replaced by the varOps like
798
+
799
+ ```json
800
+ {
801
+ "validations": {
802
+ "ON_FIELD_CHANGE": {
803
+ "required": true,
804
+ "value": "foo_bar"
805
+ }
806
+ }
807
+ }
808
+ ```
809
+
810
+ Since we are already using [templates](#templates) to run our varOps and subscribe to changes, also the error message string subscribed to the operation result. In this example we would endue with the following messages.
811
+
812
+ ```json
813
+ {
814
+ "errorMessages": {
815
+ "required": "Password is required",
816
+ "value": "Error value must be foo_bar"
817
+ }
818
+ }
819
+ ```
820
+
821
+ PS: Don't's forget that we still have the default values provided with [templates](#templates) and the rules are the same
822
+
823
+ #### Available VarOps
824
+
825
+ - concatenate(arg1,arg2)
826
+ - add(arg1,arg2)
827
+ - subtract(arg1,arg2)
828
+
829
+ ## State
830
+
831
+ This key will allow you to set up some initial state on the field.
832
+
833
+ ### hidden
834
+
835
+ Hidden prop on state, will turn your field visible or invisible
836
+
837
+ ```json
838
+ {
839
+ "state": {
840
+ "hidden": true
841
+ }
842
+ }
843
+ ```
844
+
845
+ This will be reflected on the field scope.
846
+
847
+ ### ignoreValue
848
+
849
+ Ignore component value prop on state, will control whether the property with the formatted value will be created.
850
+
851
+ ```json
852
+ {
853
+ "state": {
854
+ "ignoreValue": true
855
+ }
856
+ }
857
+ ```
858
+
859
+ Very useful when using group ownership, to limit who will actually send the value.
860
+
861
+ ## Rehydrate
862
+
863
+ **DEPRECATED, you can accomplish it with templating (previous section)**
864
+
865
+ It lets you rehydrate a given field
866
+
867
+ ```json
868
+ {
869
+ "rehydrate": {
870
+ "ON_FIELD_CHANGE": [
871
+ {
872
+ "validations": {
873
+ "required": true
874
+ },
875
+ "fields": ["destination"]
876
+ }
877
+ ]
878
+ },
879
+ "component": "dropdown",
880
+ "name": "originalField"
881
+ }
882
+ ```
883
+
884
+ The api is pretty much like visibility conditions. The above example will rehydrate the `destination` field when field with the directive (_originalField_) meets the validations configured
885
+
886
+ ## Group
887
+
888
+ In form, we can correlate fields into a single field name. This is called the group functionality.
889
+
890
+ Say you have two checkboxes and want the selected value. You can use `group` for that
891
+
892
+ ```json
893
+ [
894
+ {
895
+ "name": "checkOne",
896
+ "group": "checkedGroup",
897
+ "component": "checkbox",
898
+ "props": {
899
+ //...
900
+ }
901
+ },
902
+ {
903
+ "name": "checkTwo",
904
+ "group": "checkedGroup",
905
+ "component": "checkbox",
906
+ "props": {
907
+ //...
908
+ }
909
+ }
910
+ ]
911
+ ```
912
+
913
+ This example will store the selected value of the checkbox in the `checkedGroup` and will then be sent to the client.
914
+
915
+ # React Components
916
+
917
+ ## React `<FormProvider />`
918
+
919
+ React context that lets you provide configuration information to your application forms
920
+
921
+ ### Props
922
+
923
+ | Prop | Type | Description |
924
+ |--------------|---------------|---------------------------------------------------------------|
925
+ | mapper | TMapper | Allow you to map your own components to be used with the form |
926
+ | propsMapping | TPropsMapping | Map your component props names with the form functionalities |
927
+
928
+ ### Example
929
+
930
+ The following example shows a provider that will provide forms with input and Dropdown component
931
+
932
+ ```javascript
933
+ import Input from 'Components/Input';
934
+ import Dropdown from 'Components/Dropdown';
935
+
936
+ const Mappings = {
937
+ inputForm: { component: Input },
938
+ dropdownForm: { component: Dropdown },
939
+ };
940
+
941
+ const propsMapping = {
942
+ inputForm: {
943
+ getValue: 'onChange',
944
+ setValue: 'value',
945
+ },
946
+ dropdownForm: {
947
+ getValue: 'onChangeCallback',
948
+ setValue: 'data',
949
+ setErrorMessage: 'errorMessageArray',
950
+ setErrorState: 'isErrored',
951
+ onBlur: 'onBlurCallback',
952
+ onFocus: 'onFocusCallback',
953
+ },
954
+ };
955
+
956
+ const App = () => {
957
+ return <FormProvider mapper={Mappings} propsMapping={propsMapping} />;
958
+ };
959
+ ```
960
+
961
+ You now can use in your [form](#react-form) the mapped components with names `inputForm` and `dropdownForm`.
962
+
963
+ Also note the data in `propsMapping`. There you can map up to five form functionalities per component
964
+
965
+ | Key | Functionality |
966
+ |-----------------|----------------------------------------------------------------------------------------------------------------------|
967
+ | getValue | The name of your component prop that will give back the selected value. |
968
+ | setValue | Prop name that receives the value |
969
+ | setErrorMessage | Component prop name to receive an error message in case this field will have error and error message is configured |
970
+ | setErrorState | Component prop name to receive a boolean indicating if the field has an error or not according to the configurations |
971
+ | onBlur | Prop name that is called when field is blured |
972
+ | onFocus | Prop name that is called when field is focused |
973
+
974
+ You can also use default prop names for functionalities like:
975
+
976
+ ```javascript
977
+ import Input from 'Components/Input';
978
+ import Dropdown from 'Components/Dropdown';
979
+
980
+ const Mappings = {
981
+ inputForm: { component: Input },
982
+ dropdownForm: { component: Dropdown },
983
+ };
984
+
985
+ const propsMapping = {
986
+ __default__: {
987
+ getValue: 'onChangeCallback',
988
+ setValue: 'data',
989
+ setErrorMessage: 'errorMessageArray',
990
+ onBlur: 'onBlurCallback',
991
+ onFocus: 'onFocusCallback',
992
+ },
993
+ };
994
+
995
+ const App = () => {
996
+ return <FormProvider mapper={Mappings} propsMapping={propsMapping} />;
997
+ };
998
+ ```
999
+
1000
+ This will make form search for those names in all your components that do not have split mapping.
1001
+
1002
+ ## React `<Form />`
1003
+
1004
+ After configuring the provider, `<Form />` components lets you render a form
1005
+
1006
+ ### Props
1007
+
1008
+ | Prop | Type | Description |
1009
+ |-----------------------|------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|
1010
+ | disable | boolean | Disable all form inputs. It will use the default htm disable attribute |
1011
+ | group | string | Form group identifier. Used be able to group several forms and then get data with useGroupForm. One will be generated as default if omitted |
1012
+ | id | string | Form identified. One will be generated as default if omitted |
1013
+ | hooks | THooks | Provide functions to run on certain life-cycle events |
1014
+ | iVars | Object | One object with internal variables to be used in form with data binding |
1015
+ | initialValues | Object | Object with form initial values that will map to a field. |
1016
+ | Schema | TSchema | Form Schema |
1017
+ | autoComplete | string | HTML autocomplete |
1018
+ | className | string | Allow to style form |
1019
+ | onSubmit | callback(HTMLFormElement,TFormValues) | Will be called when there is a submit action in the form |
1020
+ | onData | callback(TFormValues,TComponent, TField) | Will be called when any field data change. The arguments will let you know which field changed and the field configuration |
1021
+ | onBlur | callback(TFormValues,TComponent, TField) | Will be called when any field blured. The arguments will let you know which field blured and the field configuration |
1022
+ | onFocus | callback(TFormValues,TComponent, TField) | Will be called when any field focused change. The arguments will let you know which field focused and the field configuration |
1023
+ | onFieldMount | callback(TFormValues,TComponent, TField) | Will be called when some field mounted. Its called with the field that information that mounted. |
1024
+ | onStep | callback(TFormValues) | Called when a form step changed |
1025
+ | onLog | callback(TLoggingEvent) | Called on each log, if the logging is enabled |
1026
+ | onScopeChange | onScopeChange?(scope: TScope, namespace: string, key: string): void; | Called everything scope change with the changing information (namespace and key) and the changed scope |
1027
+ | |
1028
+ | onFieldRehydrate | onFieldRehydrate?(values: TFormValues, component: TComponent, field: TField): void | This callback is called whenever some form field was rehydrated |
1029
+ | renderLoading | renderLoading?(): ReactElement; | Component to render while the schema has not rendered |
1030
+ | onFormMount | onFormMount?(values: TFormValues): void; | Called when the form finished mounted |
1031
+ | formattedDataDefaults | Object | Some default data to fields when they are undefined |
1032
+ | submitOnValidOnly | boolean | Boolean indicating if form can be submitted even if it is invalid |
1033
+ | renderFieldWrapper | renderFieldWrapper(component: TComponent, children: ReactElement[]) | Function that allows to insert a wrapper in place of a component or wrapping the component |
1034
+
1035
+ ### Example
1036
+
1037
+ A simple example of rendering a basic form
1038
+
1039
+ ```javascript
1040
+ <Form schema={schema} />
1041
+ ```
1042
+
1043
+ ## React `useForm()`
1044
+
1045
+ Exposed hook that allows you to connect to any form by the formId in any part of the application, as long as you are inside the form context.
1046
+
1047
+ ### Props
1048
+
1049
+ You can use the following arguments to tho hook
1050
+
1051
+ | Prop | Type | Description |
1052
+ |----------|----------|-------------------------------------------|
1053
+ | formId | string | The id of the form you want to connect to |
1054
+ | onValid | callback | Called whenever form validation changes |
1055
+ | onData | callback | Called whenever data changes |
1056
+ | onSubmit | callback | Called whenever form is submitted |
1057
+
1058
+ And it will provide you the following
1059
+
1060
+ | Prop | Type | Description |
1061
+ |----------|----------|-------------------------------------------------------------------------------------------------|
1062
+ | configs | object | One object with all the form configurations |
1063
+ | submit | function | Function that lets you call the submit on the form. After, the onSubmit callback will be called |
1064
+ | formData | function | Lets you get the most up-to-date form date |
1065
+
1066
+ ### Example
1067
+
1068
+ In the following example `useForm` hooks are used to connect to multiple forms that are inside other components.
1069
+
1070
+ ```javascript
1071
+ const Comp = () => {
1072
+ const { submitForm: submitOne } = useForm({
1073
+ formId: 'id1',
1074
+ onData: (data) => {},
1075
+ onSubmit: () => {},
1076
+ });
1077
+ const { submitForm: submitTwo } = useForm({
1078
+ formId: 'id2',
1079
+ onData: (data) => {},
1080
+ onSubmit: () => {},
1081
+ });
1082
+
1083
+ return (
1084
+ <>
1085
+ <button onClick={(() => submitOne())}>
1086
+ <button onClick={(() => submitTwo())}>
1087
+ </>
1088
+ );
1089
+ }
1090
+
1091
+ const CompOne = () => {
1092
+ return (
1093
+ <Form id="id1" {...}/>
1094
+ );
1095
+ }
1096
+
1097
+ const CompTwo = () => {
1098
+ return (
1099
+ <Form id="id2" {...} />
1100
+ );
1101
+ }
1102
+ ```
1103
+
1104
+
1105
+ ## React `useForm()`
1106
+
1107
+ Similar to `useForm`
1108
+
1109
+ ### Props
1110
+
1111
+ You can use the following arguments to tho hook
1112
+
1113
+ | Prop | Type | Description |
1114
+ |----------|-----------------|-----------------------------------------------------------|
1115
+ | ids | array of string | The ids we want to listen to |
1116
+ | group | string | A string to identify the form group we want to connect to |
1117
+ | onValid | callback | Called whenever form validation changes |
1118
+ | onData | callback | Called whenever data changes |
1119
+ | onSubmit | callback | Called whenever form is submitted |
1120
+
1121
+ And it will provide you the following
1122
+
1123
+ | Prop | Type | Description |
1124
+ |----------|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
1125
+ | configs | object | One object with all the form configurations |
1126
+ | submit | function | Function that lets you call the submit on the form. After, the onSubmit callback will be called |
1127
+ | formData | function({aggregate}) | Lets you get the most up-to-date form date in two ways. Aggregate the forms data in a single object or split by the several forms in the group or identified by the id |
1128
+
1129
+ ### Example
1130
+
1131
+ In the following example `useForm` hooks are used to connect to multiple forms that are inside other components.
1132
+
1133
+ ```javascript
1134
+ useForm({
1135
+ onSubmit: () => {
1136
+ dispatch(
1137
+ formData({
1138
+ aggregate: true,
1139
+ }),
1140
+ );
1141
+ },
1142
+ formId: 'main-form',
1143
+ });
1144
+ const { formData } = useFormGroup({
1145
+ onSubmit: (data) => {
1146
+ console.log('SUBMIT', data);
1147
+ },
1148
+ onData: (data) => {
1149
+ console.log('-> ', data);
1150
+ },
1151
+ group: 'logical',
1152
+ });
1153
+
1154
+ return (
1155
+ <Form
1156
+ id="1"
1157
+ group="logical"
1158
+ schema={...}
1159
+ />
1160
+ <Form
1161
+ id="2"
1162
+ group="logical"
1163
+ schema={...}
1164
+ />
1165
+ <Form
1166
+ id="main-form"
1167
+ schema={...}
1168
+ />
1169
+ )
1170
+ ```
1171
+
1172
+ The above example will connect to main-form with `useForm` and to a form group (logical) with `useFormGroup`