@bolttech/form-engine 3.0.1-beta.5 → 3.0.1-beta.7

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 CHANGED
@@ -2,112 +2,282 @@
2
2
 
3
3
  This is an adapter to be used with the bolttech form engine. Compatible with Next.js 13 and 14 and react 18.
4
4
 
5
- ## React `<FormGroupContextProvider />`
5
+ 1. [Sample](#sample)
6
+ 2. [mappers](#mappers)
7
+ 3. [Form Group Context](#form-group-context)
6
8
 
7
- Provider of the context surrounding the child components, providing the state and functions to manage the group of forms.
9
+ - 3.1 [FormGroupContextProvider](#formgroupcontextprovider)
10
+ - 3.2 [useFormGroupContext](#useformgroupcontext)
8
11
 
9
- ### Props
12
+ 4. [Form](#form)
10
13
 
11
- | Prop | Type | Description |
12
- | --------- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
13
- | mappers | TMapper<ElementType>[] | Array of mappers for form element types. Allow you to map your own components to be used with the form |
14
- | debugMode | boolean | Optional flag to enable debug mode (default: `false`). |
14
+ - 4.1 [Submit Form](#submit-form)
15
15
 
16
- ### Implementation
17
-
18
- - Creates a reference to the form group instance.
19
- - Defines functions for adding, getting, and removing forms from the group.
20
- - Defines a function to print the form group instance.
21
- - Defines a function to submit multiple forms via the index.
22
- - Defines the value of the context with the created functions and states.
23
- - Returns the context provider involving the child components.
16
+ 5. [schema](#schema)
17
+ 6. [schema components](#schema-components)
24
18
 
25
- ### Example
19
+ - 6.1 [formatters](#formatters)
20
+ - 6.2 [masks] (#masks)
21
+ - 6.3 [nameToSubmit](#nametosubmit)
22
+ - 6.4 [props](#props)
23
+ - 6.5 [validations](#validations)
24
+ - 6.6 [api](#api)
25
+ - 6.7 [resetValues](#resetvalues)
26
+ - 6.8 [visibilityConditions](#visibilityconditions)
26
27
 
27
- The following example shows a provider that will provide forms with input and Dropdown component
28
+ 7. [templating](#templating)
28
29
 
29
- #### With lazy import
30
+ 8. [hooks](#hooks)
30
31
 
31
- ```typescript
32
- import { ElementType, SyntheticEvent } from 'react';
33
- import { TValueChangeEvent, TMapper } from '@bolttech/form-engine-core';
32
+ - 8.1 [useForm](#useform)
33
+ - 8.2 [useFormGroup](#useformgroup)
34
34
 
35
- const defaultChangeEvent: TValueChangeEvent = (event: unknown) => {
36
- return (event as SyntheticEvent<HTMLInputElement>).currentTarget.value;
37
- };
35
+ 9. [AsFormField](#asformfield)
36
+ 10. [AsFormFieldBuilder](#asformfieldbuilder)
38
37
 
39
- const pathImports = {
40
- input: lazy(() =>
41
- import('@bolttech/atoms-input').then((module) => ({
42
- default: module.Input,
43
- }))
44
- ),
45
- };
38
+ <a id="sample"></a>
39
+ ## **Sample**
46
40
 
41
+ ```typescript
47
42
  const mappers: TMapper<ElementType>[] = [
48
43
  {
49
- asynccomponent: pathImports.input,
50
44
  componentName: 'input',
45
+ component: Input,
51
46
  events: {
52
47
  getValue: 'onChange',
53
48
  setValue: 'value',
54
49
  setErrorMessage: 'errorMessage',
55
50
  onBlur: 'onBlur',
56
51
  onFocus: 'onFocus',
57
- onKeyUp: 'onKeyUp',
58
- onKeyDown: 'onKeyUpCapture',
59
52
  },
60
- valueChangeEvent: defaultChangeEvent,
53
+ valueChangeEvent: (event: unknown) => {
54
+ return (event as ChangeEvent<HTMLInputElement>).target.value;
55
+ },
56
+ },
57
+ {
58
+ componentName: 'container',
59
+ asyncComponent: lazy(() =>
60
+ import('Components/Container').then((module) => ({
61
+ default: module.Container,
62
+ }))
63
+ ),
64
+ },
65
+ ];
66
+
67
+ const schema = {
68
+ index: 'form1',
69
+ components: [
70
+ {
71
+ component: 'container',
72
+ name: 'container1',
73
+ children: [
74
+ {
75
+ component: 'input',
76
+ name: 'input1',
77
+ props: {
78
+ label: 'input1 label',
79
+ },
80
+ },
81
+ ],
82
+ },
83
+ ],
84
+ };
85
+
86
+ const ComponentProvider = ({ children }: PropsWithChildren) => {
87
+ return <FormGroupContextProvider mappers={mappers}>{children}</FormGroupContextProvider>;
88
+ };
89
+
90
+ const ComponentWithForm = ({ schema }: { schema: IFormSchema }) => {
91
+ const { printFormGroupInstance } = useFormGroupContext();
92
+
93
+ const handleData = (data: TFormData<unknown>) => {
94
+ console.log(data);
95
+ };
96
+
97
+ return (
98
+ <>
99
+ <button onClick={printFormGroupInstance}>print form instance</button>
100
+ <Form index={schema.index} schema={schema} onData={handleData} />
101
+ </>
102
+ );
103
+ };
104
+
105
+ const Sample = () => {
106
+ return (
107
+ <ComponentProvider>
108
+ <ComponentWithForm schema={schema} />
109
+ </ComponentProvider>
110
+ );
111
+ };
112
+ ```
113
+ <a id="mappers"></a>
114
+
115
+ ## **Mappers**
116
+
117
+ Mappers are the configuration that needs to be provided in order to use components onto schemas or a special component that render form fields
118
+
119
+ The mapper configuration goes as it follows:
120
+
121
+ | Prop | Type | Description |
122
+ | ---------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------ |
123
+ | componentName | string | name to be used onto schema to identify the component to be rendered on a field |
124
+ | events | TComponentPropsMapping | events mapping that will reference the component prop with the respective form-engine prop that will handle it's content |
125
+ | valueChangeEvent | TValueChangeEvent | component handle function to define how the value is extracted from the 'onChange' event of the component |
126
+ | component | T | component type that describes a normal imported component to be used on the field rendering |
127
+ | asynccomponent | T | component type that describes an async (lazy) imported component to be used on the field rendering |
128
+
129
+ `events` are optional and will reference component props that will provide it's dynamic behaviour:
130
+
131
+ | Prop | Type | Description |
132
+ | --------------- | ------ | ------------------------------------------------------------------------------------------- |
133
+ | getValue | string | component property that will contain the value |
134
+ | setValue | string | component property that handles onChange events triggered by the component |
135
+ | onBlur | string | component property that handles onBlur events triggered by the component |
136
+ | onClick | string | component property that handles onClick events triggered by the component |
137
+ | onFocus | string | component property that handles onFocus events triggered by the component |
138
+ | onKeyUp | string | component property that handles onKeyUp events triggered by the component |
139
+ | onKeyDown | string | component property that handles onKeyDown events triggered by the component |
140
+ | setErrorMessage | string | component property that recieves the errors of the field as string |
141
+ | setErrorState | string | component property that recieves the error status of the field as boolean (not implemented) |
142
+
143
+ ### component and asynccomponent
144
+
145
+ You can choose the way your component is imported, you can directly import the component that will be bundled on your project when built,
146
+ or with React's `lazy` or any other method to be used with React's `Suspense` to load it asyncronously and allow code splitting.
147
+
148
+ Example:
149
+
150
+ ```typescript
151
+ import { Input } from '@bolttech/atoms-input';
152
+
153
+ const mappers = [
154
+ {
155
+ component: Input,
156
+ name: 'inputcomponent',
157
+ },
158
+ {
159
+ component: lazy(() =>
160
+ import('@bolttech/atoms-input').then((module) => ({
161
+ default: module.Input,
162
+ }))
163
+ ),
164
+ name: 'inputasynccomponent',
61
165
  },
62
166
  ];
63
167
  ```
64
168
 
65
- #### Without lazy import
169
+ ### from v2 to v3
170
+
171
+ Previously, mappers was two lists: `mappings` and `propsMappings`, now it's a single list, a simple example how to refactor a mapper:
66
172
 
67
173
  ```typescript
68
- import { ElementType, SyntheticEvent } from 'react';
69
- import { TValueChangeEvent, TMapper } from '@bolttech/form-engine-core';
70
- import { OptionType } from '@bolttech/atoms-select';
71
- import { Dropdown } from '@bolttech/molecules-dropdown';
174
+ //v2
175
+ const mappings = {
176
+ input: { component: Input },
177
+ };
178
+ const propsMappings = {
179
+ input: {
180
+ getValue: 'onChangeCallback',
181
+ setValue: 'data',
182
+ setErrorMessage: 'errorMessageArray',
183
+ setErrorState: 'isErrored',
184
+ onBlur: 'onBlurCallback',
185
+ onFocus: 'onFocusCallback',
186
+ onKeyUp: 'onKeyUpCallback',
187
+ onKeyDown: 'onKeyDownCallback',
188
+ },
189
+ };
72
190
 
73
- const mappers: TMapper<ElementType>[] = [
191
+ //v3
192
+ const mappers = [
74
193
  {
75
- component: Dropdown,
76
- componentName: 'dropdown',
194
+ component: Input,
195
+ componentName: 'input',
196
+ events: {
197
+ getValue: 'onChangeCallback',
198
+ setValue: 'data',
199
+ setErrorMessage: 'errorMessageArray',
200
+ setErrorState: 'isErrored',
201
+ onBlur: 'onBlurCallback',
202
+ onFocus: 'onFocusCallback',
203
+ onKeyUp: 'onKeyUpCallback',
204
+ onKeyDown: 'onKeyDownCallback',
205
+ },
206
+ valueChangeEvent: (event: unknown) => {
207
+ return (event as SyntheticEvent<HTMLInputElement>).currentTarget.value;
208
+ },
209
+ },
210
+ ];
211
+ ```
212
+
213
+ for bolltech components, there are `valueChangeEvent` default functions provided by form-engine:
214
+
215
+ - defaultChangeEvent - `atoms/input` | `atoms/textarea`
216
+ - checkedChangeEvent - `atoms/checkbox`
217
+ - valueChangeEvent - `atoms/input` | `atoms/textarea`
218
+ - datepickerChangeEvent - `molecules/datepicker`
219
+ - dropdownChangeEvent - `molecules/dropdown`
220
+
221
+ Example:
222
+
223
+ ```typescript
224
+ import { Input } from '@bolttech/atoms-input';
225
+
226
+ import { defaultChangeEvent } from '@bolttech/form-engine';
227
+
228
+ const mapper = [
229
+ {
230
+ component: Input,
231
+ componentName: 'input',
77
232
  events: {
78
233
  getValue: 'onChange',
79
234
  setValue: 'value',
80
235
  setErrorMessage: 'errorMessage',
236
+ onBlur: 'onBlur',
237
+ onFocus: 'onFocus',
238
+ onKeyUp: 'onKeyUp',
239
+ onKeyDown: 'onKeyUpCapture',
81
240
  },
82
- valueChangeEvent: (event: unknown, opts) => {
83
- if (typeof event === 'object' && event !== null && 'value' in event && 'id' in event) {
84
- return {
85
- _value: event?.id,
86
- _metadata: event,
87
- };
88
- } else if (typeof event === 'string' && typeof opts === 'object' && 'props' in opts && typeof opts.props === 'object' && 'optionList' in opts.props && Array.isArray(opts.props.optionList)) {
89
- const option = (opts.props.optionList as OptionType[]).find((el) => el?.value === event);
90
- if (option) return { _value: option.id, _metadata: option };
91
- } else return { _value: event, _metadata: event };
92
- },
241
+ valueChangeEvent: defaultChangeEvent,
93
242
  },
94
243
  ];
95
244
  ```
96
245
 
97
- The codes above are created using the `valueChangeEvent` method, which is a new way of controlling how the field value will be manipulated when setValue happens. In other words: Optional function to handle value changes.
246
+ <a id="form-group-context"></a>
247
+
248
+ ## **Form Group Context**
249
+
250
+ <a id="formgroupcontextprovider"></a>
251
+
252
+ ## **FormGroupContextProvider**
253
+
254
+ Provider of the context surrounding the child components, providing the state and functions to manage the group of forms.
255
+
256
+ ### Props
257
+
258
+ | Prop | Type | Description |
259
+ | --------- | ---------------------- | ------------------------------------------------------------------------------------------------------ |
260
+ | mappers | TMapper<ElementType>[] | Array of mappers for form element types. Allow you to map your own components to be used with the form |
261
+ | debugMode | boolean | Optional flag to enable debug mode (default: `false`). |
262
+ | config | TSchemaFormConfig | Optional configuration object to set api and events debounce time |
263
+
264
+ ### Implementation
265
+
266
+ - Creates a reference to the form group instance.
267
+ - Defines functions for adding, getting, and removing forms from the group.
268
+ - Defines a function to print the form group instance.
269
+ - Defines a function to submit multiple forms via the index.
270
+ - Defines the value of the context with the created functions and states.
271
+ - Returns the context provider involving the child components.
98
272
 
99
273
  #### Calling provider
100
274
 
101
275
  ```typescript
102
276
  import React from 'react';
103
277
  import { FormGroupContextProvider } from '@bolttech/form-engine'; // Import the previously created provider
278
+ import { mappers } from 'project/mappers'; // mappers defined on the previous topic
104
279
 
105
- type Props = {
106
- children: React.ReactNode;
107
- };
108
-
109
- const App = ({ children }: Props): React.ReactElement => {
110
- const mappers = []; // Define your type mappers here
280
+ const App = ({ children }: React.PropsWithChildren): React.ReactElement => {
111
281
  const debugMode = true; // Enable debug mode
112
282
 
113
283
  return (
@@ -157,19 +327,17 @@ const propsMapping = {
157
327
  onKeyDown: 'onKeyDownCallback',
158
328
  },
159
329
  };
160
-
161
- const App = () => {
162
- return <FormProvider mapper={Mappings} propsMapping={propsMapping} />;
163
- };
164
330
  ```
165
331
 
166
332
  [DEPRECIATED] This will make form search for those names in all your components that do not have split mapping.
167
333
 
168
- ## React `useFormGroupContext()` hook
334
+ <a id="useformgroupcontext"></a>
335
+
336
+ ## **useFormGroupContext**
169
337
 
170
338
  Hook that facilitates the use of the FormGroupContext context in functional components.
171
339
 
172
- - Checks if the context is set and throws an error if not.
340
+ - Checks if the context is set or creates an isolated context that will only be accessible on the component that is defined.
173
341
  - Returns the context.
174
342
 
175
343
  ### What comes from
@@ -238,8 +406,9 @@ const FormComponent = (): React.ReactElement => {
238
406
 
239
407
  export default FormComponent;
240
408
  ```
409
+ <a id="form"></a>
241
410
 
242
- ## React `<Form />`
411
+ ## **Form**
243
412
 
244
413
  After configuring the provider, `<Form />` components lets you render a form
245
414
 
@@ -256,16 +425,16 @@ After configuring the provider, `<Form />` components lets you render a form
256
425
  | Schema | TSchema | Form Schema |
257
426
  | [DEPRECIATED] autoComplete | string | HTML autocomplete |
258
427
  | className | string | Allow to style form |
259
- | onSubmit | callback(HTMLFormElement,TFormValues) | Will be called when there is a submit action in the form |
260
- | 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 |
261
- | 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 |
262
- | 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 |
428
+ | onSubmit | callback(TFormValues) | Will be called when there is a submit action in the form |
429
+ | onData | callback(TFormData) | Will be called when any field data change. The arguments will let you know which field changed and the field configuration |
430
+ | onBlur | callback(TFieldEvent) | Will be called when any field blured. The arguments will let you know which field blured and the field configuration |
431
+ | onFocus | callback(TFieldEvent) | Will be called when any field focused change. The arguments will let you know which field focused and the field configuration |
263
432
  | onMount | callback(TFormValues,TComponent, TField) | Will be called when some field mounted. Its called with the field that information that mounted. |
264
433
  | [DEPRECIATED] onStep | callback(TFormValues) | Called when a form step changed |
265
434
  | [DEPRECIATED] onLog | callback(TLoggingEvent) | Called on each log, if the logging is enabled |
266
435
  | [DEPRECIATED] onScopeChange | onScopeChange?(scope: TScope, namespace: string, key: string): void; | Called everything scope change with the changing information (namespace and key) and the changed scope |
267
- | onClick | onClick(TFormValues, TField) | Callback function that runs on each component click |
268
- | onApiResponse | onApiResponse(values: TFormValues, component?: TComponent, field?: TField) | Callback function that runs on each component after api call. |
436
+ | onClick | onClick(TFieldEvent) | Callback function that runs on each component click |
437
+ | onApiResponse | onApiResponse(TFieldEvent) | Callback function that runs on each component after api call. |
269
438
  | [DEPRECIATED] onFieldRehydrate | onFieldRehydrate?(values: TFormValues, component: TComponent, field: TField): void | This callback is called whenever some form field was rehydrated |
270
439
  | [DEPRECIATED] renderLoading | renderLoading?(): ReactElement; | Component to render while the schema has not rendered |
271
440
  | [DEPRECIATED] onFormMount | onFormMount?(values: TFormValues): void; | Called when the form finished mounted |
@@ -280,137 +449,674 @@ A simple example of rendering a basic form
280
449
  ```javascript
281
450
  <Form schema={schema} />
282
451
  ```
452
+ <a id="submit-form"></a>
283
453
 
284
- ## React `useForm()`
454
+ ## **Submit Form**
285
455
 
286
- 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.
456
+ In order to submit a form you need to have a button with the prop type `submit` and set a callback function on `onSubmit` Form prop
287
457
 
288
- ## React `useFormGroup()` [DEPRECIATED]
458
+ ### Example:
289
459
 
290
- Similar to `useForm`
460
+ ```typescript
461
+ const schema = {
462
+ index: 'form1',
463
+ components: [
464
+ {
465
+ name: 'firstName',
466
+ component: 'input',
467
+ props: {
468
+ label: 'first name',
469
+ },
470
+ },
471
+ {
472
+ name: 'lastName',
473
+ component: 'input',
474
+ props: {
475
+ label: 'lase name',
476
+ },
477
+ },
478
+ {
479
+ name: 'submitButton',
480
+ component: 'button',
481
+ props: {
482
+ type: 'submit',
483
+ text: 'submit form',
484
+ },
485
+ },
486
+ ],
487
+ };
291
488
 
292
- ## React `asFormField()`
489
+ const Comp = () => {
490
+ const handleSubmit = (payload: TFormValues<unknown>) => {
491
+ const values = payload.values;
492
+
493
+ userService
494
+ .addNewUser(values)
495
+ .then((res) => {
496
+ router.push(`/plans?hash=${res.hash}`);
497
+ })
498
+ .catch((e) => {
499
+ logService.push(e);
500
+ router.push(`/errorPage`);
501
+ });
502
+ };
293
503
 
294
- Allows you to add separate components from a standard form engine form schema. Transforming it into a field on a standard form.
504
+ return <Form schema={schema} onSubmit={handleSubmit} />;
505
+ };
506
+ ```
295
507
 
296
- ### Props
508
+ This is a sample on how to handle a form submission, you can adapt to any use case, and also set the `onSubmit` callback function on the `useForm` hook, but when you
509
+ have a `Form` component, it's preferable to set the `onSubmit` callback function onto the `Form` prop, the hook is preferrable to `asFormFieldBuilder` fields that doesn't
510
+ have a `Form` component and event callback functions needs to be setted
511
+
512
+ <a id="schema"></a>
513
+
514
+ ## **schema**
297
515
 
298
- You can use the following arguments declare a component as a form field
299
-
300
- | Prop | Type | Description |
301
- | ------------ | ------------------------ | -------------------------------------------------------------------------- |
302
- | Comp | React Function Component | The component to be used as form field. |
303
- | propsMapping | TComponentPropsMapping | Link for the TComponentPropsMapping likely props mapper from default form. |
304
-
305
- And this will give you the following properties in addition to the native ones of your component
306
-
307
- | Prop | Type | Description |
308
- | -------------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
309
- | validations | TSchemaValidation | Setup validations to be used in the field. |
310
- | masks | TSchemaMasks | Allows you to display the value of the masked component by events |
311
- | clearFields | TSchemaClearFields | Will clear target fields in case they do not pass with the specified validations |
312
- | api | TSchemaApi | Allows you to make api calls using events emitted by the component. |
313
- | filter | TSchemaValidation | Filters the component value based on a validation. |
314
- | formatters | TSchemaFormatters | Allows you to format the value that the field will receive for each event issuance |
315
- | visibilityConditions | TSchemaVisibilityConditions | Allows you to specify the conditions a given field will be visible what will run when this field meets the specified life-cycle |
316
- | formId | string | Form id that you need to create and integrate the field. |
317
- | visibility | boolean | It's a prop that you can use to hide the component and control this state outside the component. |
318
- | name | string | Name of the component similar to name prop name on TComponent type. This name will be used later to correlate the field with the value, and you will be able to read it. |
319
- | value | any | The value of the field that you can control outside the component. |
320
- | disabled | boolean | The disabled of the field that you can control outside the component. |
321
- | optionList | any[] | The optionList of the field that you can control outside the component. This optionList will be used to manipulated a list of elements. For example, Dropdown optionList. |
322
- | onSelected | void | The onSelected of the field you can control outside the component by calling a custom function. onSelected makes the data available (TField) for manipulation. It is called when you mount and rehydrate the component at the first moment. Also, it is called every time the component changes (onChange). |
516
+ Schema is the structure of the form, it will contain the logic to be rendered and configurations of the fields to apply dynamic logic
517
+
518
+ | Prop | Type | Description |
519
+ | -------------- | ----------------------- | ----------------------------------------------------------------------- |
520
+ | index | string | unique form id to handle multiple form on form group |
521
+ | action? | string | WIP: HTML form native action property to handle native form submissions |
522
+ | method? | string | WIP: HTML form native method property to handle native form submissions |
523
+ | config? | TSchemaFormConfig | Optional configuration object to set api and events debounce time |
524
+ | initialValues? | Record<string, unknown> | initial values to be loaded on the form |
525
+ | iVars? | Record<string, unknown> | dynamic values that can be changed externally to be used onto the form |
526
+ | components? | IComponentSchema[] | components to be rendered defined on the mappers |
527
+
528
+ this root configuration can be defined onto the `Form` component except the `components`
323
529
 
324
530
  ### Example
325
531
 
326
- In the following example `asFormField` hooks are used to create `Component` field into a stateful form `test`.
532
+ ```typescript
533
+ const schema: IFormSchema = {
534
+ index: 'form1',
535
+ components: [
536
+ {
537
+ component: 'input',
538
+ name: 'input1',
539
+ props: {
540
+ label: 'input1',
541
+ },
542
+ },
543
+ {
544
+ component: 'input',
545
+ name: 'input2',
546
+ props: {
547
+ label: 'input2',
548
+ },
549
+ },
550
+ ],
551
+ };
552
+ ```
553
+
554
+ <a id="schema-components"></a>
327
555
 
328
- ```javascript
556
+ ## **schema components**
557
+
558
+ Schema components contains the information of the component that will be rendered and the configurations he will exectute
559
+
560
+ | Prop | Type | Description |
561
+ | --------------------- | -------------------- | ------------------------------------------------------------------------------------------- |
562
+ | component | string | component name defined on schema to render the correspondent component |
563
+ | props? | TProps | props of the component ex: label |
564
+ | name | string | unique id to identify the field onto form-engine |
565
+ | nameToSubmit? | string | dot notation to submit a custom path to the component value ex: 'person.profile.firstName' |
566
+ | validations? | TValidations | validations configuration described below |
567
+ | api? | TApiEvent | api configuration described below |
568
+ | visibilityConditions? | TVisibility[] | visibilityConditions configuration described below |
569
+ | resetValues? | TResetValueMethods[] | resetValues configuration described below |
570
+ | formatters? | TFormatters | formatters configuration described below |
571
+ | masks? | TMasks | masks configuration described below |
572
+ | children? | IComponentSchema[] | nested components to be rendered (if the current parent component accepts child components) |
573
+
574
+ ### Example
575
+
576
+ ```typescript
329
577
  {
330
- const FormComponent =
331
- asFormField <
332
- React.ComponentProps <
333
- typeof Component >>
334
- {
335
- Comp: Component,
336
- propsMapping: propsMapping.component,
337
- };
338
-
339
- <FormGroupContextProvider mapper={Mappings}>
340
- <FormComponent
341
- formId="test"
342
- label="Field example"
343
- name="ss"
344
- formatters={
345
- splitter: [
346
- {
347
- position: 2,
348
- value: '/',
349
- },
350
- {
351
- position: 5,
352
- value: '/',
353
- },
354
- ]
355
- }
356
- validations={{
357
- methods: {
358
- callback: (data) => {
359
- return data === '10/10/1000';
360
- }
361
- },
362
- eventMessages: { ON_FIELD_BLUR: [ 'callback' ] },
363
- messages: { callback: 'ERRRO' }
364
- }}
365
- />
366
- </FormGroupContextProvider>;
578
+ component: 'input',
579
+ name: 'input1',
580
+ props: {
581
+ label: 'input1',
582
+ },
583
+ nameToSubmit: 'person.profile.firstName',
584
+ validations: {
585
+ methods: {
586
+ required: true,
587
+ },
588
+ eventMessages: {
589
+ ON_FIELD_CHANGE: ['required'],
590
+ },
591
+ messages: {
592
+ required: 'Value is required',
593
+ },
594
+ },
595
+ api: {
596
+ defaultConfig: {
597
+ config: {
598
+ method: 'GET',
599
+ url: 'https://api.chucknorris.io/jokes/random',
600
+ resultPath: 'value',
601
+ },
602
+ events: ['ON_FIELD_MOUNT'],
603
+ },
604
+ },
605
+ visibilityConditions: [
606
+ {
607
+ validations: {
608
+ isNumber: true,
609
+ },
610
+ events: ['ON_FIELD_CHANGE'],
611
+ fields: ['input2'],
612
+ },
613
+ ],
614
+ resetValues: [{
615
+ validations: {
616
+ isNumber: true,
617
+ },
618
+ events: ['ON_FIELD_CHANGE'],
619
+ fields: ['input2'],
620
+ resettledValue: ['resettledValue from input1'],
621
+ }]
622
+ }
623
+ ```
624
+ <a id="formatters"></a>
625
+
626
+ ## **formatters**
627
+
628
+ formatters are methods that will format the input inserted on any field, they will format a value, regardless the event type,
629
+
630
+ ### Avaliable formatters
631
+
632
+ Check the TSDocs from `TFormatters` on form-engine-core
633
+
634
+ <a id="masks"></a>
635
+
636
+ ## **masks**
637
+
638
+ masks are methods that will format the input inserted on any field, they will show to the user a formatted value, but the submission value will be the original input of the user
639
+
640
+ ### Avaliable masks
641
+
642
+ Check the TSDocs from `TMasks` on form-engine-core
643
+
644
+ ### From v2 to v3
645
+
646
+ ```typescript
647
+ //v2
648
+ const formatters = {
649
+ ON_FIELD_MOUNT: {
650
+ upperCase: true,
651
+ },
652
+ ON_FIELD_CHANGE: {
653
+ upperCase: true,
654
+ },
655
+ };
656
+
657
+ //v3
658
+ const formatters = {
659
+ upperCase: true,
660
+ };
661
+ ```
662
+ <a id="nametosubmit"></a>
663
+
664
+ ## **nameToSubmit**
665
+
666
+ nameToSubmit is a component property that will set the submit value to a custom dot notation path or key
667
+
668
+ ### Example
669
+
670
+ ```typescript
671
+ const component = {
672
+ component: 'input'
673
+ name: 'input1'
674
+ nameToSubmit: 'person.profile.firstName'
675
+ }
676
+ ```
677
+
678
+ when calling the `onSubmit` or `onData` the field section of this field will output:
679
+
680
+ ```json
681
+ {
682
+ "person": {
683
+ "profile": {
684
+ "firstName": "inserted value from input1"
685
+ }
686
+ }
367
687
  }
368
688
  ```
689
+ <a id="props"></a>
690
+
691
+ ## **props**
692
+
693
+ props let you add any prop the component you are using from the mappers that exists on the component
694
+
695
+ ### Example
696
+
697
+ ```typescript
698
+ const component = {
699
+ component: 'input'
700
+ name: 'input1'
701
+ props: {
702
+ label: 'input1 label'
703
+ }
704
+ }
705
+ ```
706
+
707
+ If the mapper of `input` has the `label` prop avaliable, it will be set with `input1 label` value
708
+
709
+ <a id="validations"></a>
710
+
711
+ ## **validations**
712
+
713
+ validations let you set rules in order to validate a field, then show errorMessages based on an event occured
714
+
715
+ | Prop | Type | Description |
716
+ | -------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------- |
717
+ | methods | TSchemaValidation | validations rules to be used to validate the field |
718
+ | eventMessages? | Partial<Record<TEvents, Partial<keyof TValidationMethods>[]>> | object with the event occured, and the messages to display on the occured event |
719
+ | messages? | TErrorMessages | object with the validations and the respective error message to display |
720
+
721
+ ### Avaliable validations
722
+
723
+ Check the TSDocs from `TValidationMethods` on form-engine-core
724
+
725
+ ### From v2 to v3
726
+
727
+ ```typescript
728
+ //v2
729
+ const component: {
730
+ component: 'input';
731
+ name: 'input1';
732
+ validations: {
733
+ ON_FIELD_BLUR: {
734
+ required: true;
735
+ };
736
+ ON_FIELD_CHANGE: {
737
+ regex: '^[0-9]{2}/[0-9]{2}$';
738
+ };
739
+ errorMessages: {
740
+ required: 'field required';
741
+ regex: 'invalid format';
742
+ };
743
+ };
744
+ };
745
+
746
+ //v3
747
+ const validations: {
748
+ methods: {
749
+ required: true;
750
+ regex: '^[0-9]{2}/[0-9]{2}$';
751
+ };
752
+ eventMessages: {
753
+ ON_FIELD_BLUR: ['required'];
754
+ ON_FIELD_CHANGE: ['regex'];
755
+ };
756
+ messages: {
757
+ required: 'field required';
758
+ regex: 'invalid format';
759
+ };
760
+ };
761
+ ```
762
+
763
+ ### named validations
764
+
765
+ if you want to make a name validation, instead of using a `TValidationMethods`, you can write a custom name with `TValidationMethods` inside, ex:
766
+
767
+ ```typescript
768
+ const validations: {
769
+ methods: {
770
+ custom1: {
771
+ regex: '^[0-5]{4}/[0-3]{6}$';
772
+ };
773
+ custom2: {
774
+ regex: '^[0-9]{2}/[0-9]{2}$';
775
+ };
776
+ };
777
+ eventMessages: {
778
+ ON_FIELD_BLUR: ['custom1'];
779
+ ON_FIELD_CHANGE: ['custom2'];
780
+ };
781
+ messages: {
782
+ custom1: 'field required';
783
+ custom2: 'invalid format';
784
+ };
785
+ };
786
+ ```
787
+
788
+ This is useful if you want to make more than one rule of the same `TValidationMethods`,
789
+
790
+ ### default error message
791
+
792
+ If you don't want to specify each error message for each method, you can use `default`on `messages` property, this message will appear regardless the validation method
793
+ and only one method that will fail will display the message, you still need to set on `eventMessages` which events and methods will trigger an error message.
794
+
795
+ <a id="api"></a>
796
+
797
+ ## **api**
798
+
799
+ Api let's you make a request and use the response values onto fields
800
+
801
+ The configuration is as it follows:
802
+
803
+ | Prop | Type | Description |
804
+ | -------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
805
+ | defaultConfig? | TEvent<TApiConfig> | this is the default config, preferred if you only have 1 API request on a field |
806
+ | configs? | Record<string, TEvent<TApiConfig>> | this is a named config, preferred if you have more than 1 API request on a field, you set a key and an API config |
807
+
808
+ Each config you opt to use, needs to be filled with an API configuration, the configuration is as it follows:
809
+
810
+ | Prop | Type | Description |
811
+ | ------------------------ | --------------------------------- | ------------------------------------------------------------------------------------- |
812
+ | method | 'GET' or 'POST' | HTTP method (only GET or POST) |
813
+ | url | string | Request url ex: http://mockapi.org |
814
+ | body? | Record<string, unknown> | Request body (only POST requests) |
815
+ | headers? | OutgoingHttpHeaders | Avaliable HTTP headers |
816
+ | resultPath? | string | response dot notation path to the value needed from the response |
817
+ | fallbackValue? | unknown | default value to return if the API returns error |
818
+ | preConditions? | TSchemaValidation | validations to occur before the request is made (check validations section) |
819
+ | blockRequestWhenInvalid? | boolean | flag to only request the api config if the field is valid |
820
+ | transform? | { callback:(payload) => unknown } | custom function to be passed as callback to transform the request in any other format |
821
+
822
+ ### From v2 to v3
823
+
824
+ Instead of setting scope on the api config, you use a `config` with a key name that you want to use as scope, or you can just use `defaultConfig` and set the same
825
+ structure as you pass to the `config` without a key name
826
+
827
+ ```typescript
828
+ //v2
829
+ const api = {
830
+ ON_FIELD_BLUR: [
831
+ {
832
+ method: 'GET',
833
+ url: 'https://api.chucknorris.io/jokes/random',
834
+ scope: 'chuck',
835
+ },
836
+ ],
837
+ ON_FIELD_CHANGE: [
838
+ {
839
+ method: 'GET',
840
+ url: 'https://api.chucknorris.io/jokes/random',
841
+ scope: 'chuck',
842
+ },
843
+ ],
844
+ };
845
+
846
+ //v3
847
+ const api = {
848
+ config: {
849
+ chuck: {
850
+ config: {
851
+ method: 'GET',
852
+ url: 'https://api.chucknorris.io/jokes/random',
853
+ },
854
+ events: ['ON_FIELD_BLUR', 'ON_FIELD_CHANGE'],
855
+ },
856
+ },
857
+ };
858
+ ```
859
+
860
+ the API result is commonly used with `templating`, check [templating](#markdown-templating) section
861
+
862
+ <a id="resetvalues"></a>
369
863
 
370
- ## React `AsFormFieldBuilder()`
864
+ ## **resetValues**
371
865
 
372
- Extends the asFormField implementation with the addition of the prop formId and mapper and doesn't required the Form component to render a form field
866
+ resetValues lets you change input values with the same rules as validations
373
867
 
374
- | Prop | Type | Description |
375
- |formIndex | string | index of the form to identity the form |
376
- |mapper | TMapper<ElementType> | same mapper used on the mapper context |
868
+ | Prop | Type | Description |
869
+ | -------------- | -------------------- | ---------------------------------------------------------------------------------- |
870
+ | validations | TSchemaValidation | validations rules to be validated to reset the value to the configuration provided |
871
+ | fields | string[] or string | field or fields that will recieve the resettled value |
872
+ | events | Partial<TEvents>[] | events that will trigger the validation |
873
+ | resettledValue | unknown[] or unknown | value or values to be set on the specified fields |
874
+
875
+ if the event occurs and all the validations returns `true` the resettled value will trigger and the `fields` specified will be filled with the `resettledValue` values
876
+
877
+ ### from v2 to v3
878
+
879
+ `clearFields` is changed to `resetValues`
880
+
881
+ ```typescript
882
+ //v2
883
+ const clearFields = {
884
+ ON_FIELD_CLICK: [
885
+ {
886
+ fields: ['input1', 'input2', 'dropdown'],
887
+ clearedValue: ['', 'Value has change', 'all'],
888
+ },
889
+ ],
890
+ ON_FIELD_BLUR: [
891
+ {
892
+ fields: ['input1', 'input2', 'dropdown'],
893
+ clearedValue: ['', 'Value has change', 'all'],
894
+ },
895
+ ],
896
+ };
897
+
898
+ //v3
899
+ const resetValues = [
900
+ {
901
+ fields: ['input1', 'input2', 'dropdown'],
902
+ resettledValue: ['', 'Value has change', 'all'],
903
+ events: ['ON_FIELD_CLICK', 'ON_FIELD_BLUR'],
904
+ },
905
+ ];
906
+ ```
907
+ <a id="visibilityconditions"></a>
908
+
909
+ ## **visibilityConditions**
910
+
911
+ visibilityConditions will show or hide fields based on rules, the structure is similar as the resetValues, but instead, you will set the fields to be shown or hidden
912
+
913
+ | Prop | Type | Description |
914
+ | --------------- | ------------------ | ---------------------------------------------------------------------------------- |
915
+ | showOnlyIfTrue? | boolean | shows the fields specified is condition is true |
916
+ | validations | TSchemaValidation | validations rules to be validated to reset the value to the configuration provided |
917
+ | fields | string[] or string | field or fields that will be shown or hidden |
918
+ | events | Partial<TEvents>[] | events that will trigger the validation |
919
+
920
+ ### From v2 to v3
921
+
922
+ ```typescript
923
+ //v2
924
+ const visibilityConditions = {
925
+ ON_FIELD_MOUNT: [
926
+ {
927
+ validations: {
928
+ value: true,
929
+ },
930
+ fieldNames: ['roofUpdatedYear'],
931
+ },
932
+ ],
933
+ ON_FIELD_CHANGE: [
934
+ {
935
+ validations: {
936
+ value: true,
937
+ },
938
+ fieldNames: ['roofUpdatedYear'],
939
+ },
940
+ ],
941
+ };
942
+
943
+ //v3
944
+ const visibilityConditions = [
945
+ {
946
+ validations: {
947
+ value: true,
948
+ },
949
+ fields: 'roofUpdatedYear',
950
+ events: ['ON_FIELD_MOUNT', 'ON_FIELD_CHANGE'],
951
+ },
952
+ ];
953
+ ```
954
+
955
+ <a id="templating"></a>
956
+
957
+ ## **templating**
958
+
959
+ templating let's you use field properties or iVars as values of other properties
960
+
961
+ Templates separates on two scopes:
962
+
963
+ | Scope | Description |
964
+ | ------ | --------------------------- |
965
+ | fields | any property from a field |
966
+ | iVars | any iVar passed to the form |
967
+
968
+ ### From v2 to v3
969
+
970
+ getting an iVar in v2:
971
+ `${globals.ivarsample}`
972
+
973
+ getting an iVar in v3:
974
+ `${iVars.ivarsample}`
975
+
976
+ **api scope now doesn't exist and the api response will be taken from the field**
977
+
978
+ getting api response from field `field1` in v2:
979
+ `${api.chuck.value}`
980
+
981
+ getting api response from field `field1` in v3:
982
+ `${fields.field1.api.chuck.response.value}`
983
+
984
+ ### Examples:
985
+
986
+ Templates start with the `scope` term, are defined with `${}` syntax and use dot notation to get the property you want,
987
+
988
+ Examples:
989
+
990
+ `${fields.field1.value}` this will get the current value of a field
991
+
992
+ `${fields.field1.api.default.response||[]}` this will get the response of a field api or an empty array
993
+
994
+ `${fields.field1.props.label}` this will get the label prop defined on the component schema prop
995
+
996
+ `${fields.field1.props.label||fields.field1.props.description}` this will get the `label` OR `description` defined on the component schema props
997
+
998
+ `${fields.field1.props.label} and ${fields.field1.props.description}` this will concatenate `label` AND `description` defined on the component schema props in a string
999
+
1000
+ <a id="hooks"></a>
1001
+
1002
+ ## **hooks**
1003
+
1004
+ <a id="useform"></a>
1005
+
1006
+ ## **useForm**
1007
+
1008
+ `useForm` is used to register callback functions instead of passing them to the `Form` props,
1009
+
1010
+ ```javascript
1011
+ useForm({ id: 'form1', onData: (data) => console.log(data) });
1012
+
1013
+ return <Form index={'form1'} schema={schema}>
1014
+ ```
1015
+
1016
+ Each time a data event occurs, the callback function passed on `onData` executes, allowing to run additional code outside the form-engine lifecycle
1017
+
1018
+ Also, there's an additional property to be passed on this hook in order to event record occurs on certain scenarios
377
1019
 
378
1020
  ```javascript
379
- <FormGroupContextProvider mapper={Mappings}>
380
- <AsFormFieldBuilder
1021
+ useForm({ id: 'form1', onData: (data) => console.log(data) },[visible]);
1022
+
1023
+ return visible && <Form index={'form1'} schema={schema}>
1024
+ ```
1025
+
1026
+ If you happen to have a conditional form render inside the react jsx implementation, you need to add it to the dependency array, or, if you can pass
1027
+ the respective callback to the form props like this:
1028
+
1029
+ ```javascript
1030
+ <Form index={'form1'} schema={schema} onData={(data) => console.log(data)} />
1031
+ ```
1032
+
1033
+ The avalialbe callback methods are:
1034
+
1035
+ | method | payload | description |
1036
+ | ------------- | ---------------------- | -------------------------------------------------------------------- |
1037
+ | onChange | TFieldEvent | basic event returning this type if this event occurs, (check TSDocs) |
1038
+ | onBlur | TFieldEvent | same as above |
1039
+ | onFocus | TFieldEvent | same as above |
1040
+ | onKeyDown | TFieldEvent | same as above |
1041
+ | onKeyUp | TFieldEvent | same as above |
1042
+ | onMount | TFieldEvent | same as above |
1043
+ | onApiResponse | TFieldEvent | same as above |
1044
+ | onClick | TFieldEvent | same as above |
1045
+ | onFormMount | TFormValues<T> | event occuring on form mount |
1046
+ | onData | TFormData<T> | event occuring when a value is changing via input or logic |
1047
+ | onSubmit | TFormValues<T> | event occuring when pressing the submit button defined on the form |
1048
+ | onValid | TFormValidationPayload | event occuring when validation status changes on the form |
1049
+
1050
+ <a id="useformgroup"></a>
1051
+
1052
+ ## **useFormGroup**
1053
+
1054
+ ```javascript
1055
+ useFormGroup({ ids: ['form1', 'form2'], onData: (payload) => console.log(payload) }, [deps]);
1056
+ ```
1057
+
1058
+ As `useForm`, `useFormGroup` serves the same purpose, but the difference is that it handles multiple forms and has limited callback functions to set:
1059
+
1060
+ | method | payload | description |
1061
+ | ------- | ------------------------------- | ---------------------------------------------------------- |
1062
+ | onData | TFormGroupOnDataEventPayload<T> | event occuring when a value is changing via input or logic |
1063
+ | onValid | TFormGroupOnValidEventPayload | event occuring when validation status changes on the form |
1064
+
1065
+ <a id="asformfield"></a>
1066
+
1067
+ ## **asFormField**
1068
+
1069
+ Currently on development process, allows to build a schema with react components instead of a json schema
1070
+
1071
+ <a id="asformfieldbuilder"></a>
1072
+
1073
+ ## **AsFormFieldBuilder**
1074
+
1075
+ This component allows to create form fields with the functionality of form-engine without a form schema or a `Form` component,
1076
+ they will be identified by it's `id` on the formGroup context and they will interact with each other by the same id.
1077
+
1078
+ Also, they don't require component mapping, so the component mapping can be done on this component by passing the mapper by it's props:
1079
+
1080
+ Ex:
1081
+
1082
+ ```javascript
1083
+ <AsFormFieldBuilder
381
1084
  mapper={{
382
1085
  component: Input,
383
- componentName: 'input',
384
1086
  events: {
385
1087
  getValue: 'onChange',
386
1088
  setValue: 'value',
387
1089
  setErrorMessage: 'errorMessage',
388
1090
  onBlur: 'onBlur',
389
1091
  onFocus: 'onFocus',
390
- onKeyUp: 'onKeyUp',
391
- onKeyDown: 'onKeyUpCapture',
392
1092
  },
393
1093
  valueChangeEvent: (event: unknown) => {
394
- return (event as SyntheticEvent<HTMLInputElement>).currentTarget
395
- .value;
396
- },
1094
+ return (event as ChangeEvent<HTMLInputElement>).target.value;
1095
+ }
397
1096
  }}
398
- formIndex="form1"
399
- name={`input1`}
400
1097
  props={{
401
- label: '${"insert something"||${input1.value}}',
402
- helperMessage: `\${input1.api.default.response}`,
1098
+ label: 'Input label',
403
1099
  }}
404
- api={{
405
- defaultConfig: {
406
- config: {
407
- method: 'GET',
408
- url: 'https://api.chucknorris.io/jokes/random',
409
- resultPath: 'value',
410
- },
411
- events: ['ON_FIELD_MOUNT'],
1100
+ formIndex={'form1'}
1101
+ name={'asFormField'}
1102
+ validations={{
1103
+ methods: {
1104
+ required: true,
1105
+ },
1106
+ eventMessages: {
1107
+ ON_FIELD_CHANGE: ['required'],
1108
+ },
1109
+ messages: {
1110
+ required: 'its required',
412
1111
  },
413
1112
  }}
414
- />
415
- </FormGroupContextProvider>
1113
+ ></AsFormFieldBuilder>
416
1114
  ```
1115
+
1116
+ Other than schema component properties like `validations`, `api`, etc.., it has additional properties to define the component, the associated form id and the name
1117
+
1118
+ | method | type | description |
1119
+ | --------- | ---------- | --------------------------------------------------- |
1120
+ | mapper | TMapper<T> | mapper configuration to define the component |
1121
+ | name | string | field name to be identified on the form |
1122
+ | formIndex | string | index of the form to be identified on the formGroup |