@nvs-dynamic-form/react-core 1.2.3 → 1.2.4
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/lib/index.tsx +2 -0
- package/lib/nvsDynamicForm/_stories.tsx +93 -0
- package/lib/nvsDynamicForm/_style.css +5 -0
- package/lib/nvsDynamicForm/_template.tsx +143 -0
- package/lib/nvsDynamicForm/_type.tsx +21 -0
- package/lib/nvsDynamicForm/index.tsx +2 -0
- package/lib/types/dynamic-object.type.tsx +3 -0
- package/lib/types/form-field.type.tsx +23 -0
- package/lib/types/index.tsx +4 -0
- package/lib/types/screen-size.type.tsx +7 -0
- package/lib/types/submit-button-options.type.tsx +5 -0
- package/package.json +2 -1
package/lib/index.tsx
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { INvsDynamicForm, NvsDynamicForm } from "./";
|
|
2
|
+
import * as Yup from "yup";
|
|
3
|
+
|
|
4
|
+
import { Field } from "formik";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { FieldBase } from "../types";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
component: NvsDynamicForm,
|
|
10
|
+
title: "Nvs Dynamic Form",
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const ButtonComponent = ({ children }: { children: string }) => {
|
|
14
|
+
return (
|
|
15
|
+
<button style={{ width: "100%" }} type="submit">
|
|
16
|
+
{children}
|
|
17
|
+
</button>
|
|
18
|
+
);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const TextboxElement = (opt: TextboxField) => {
|
|
22
|
+
return (
|
|
23
|
+
<Field
|
|
24
|
+
style={{ width: "100%", boxSizing: "border-box" }}
|
|
25
|
+
id={opt.id}
|
|
26
|
+
name={opt.id}
|
|
27
|
+
placeholder={opt.label}
|
|
28
|
+
/>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
class TextboxField extends FieldBase<string> {
|
|
33
|
+
override readonly fieldType? = "textbox";
|
|
34
|
+
type?: "email" | "number" | "password" | "tel" | "text" | "url";
|
|
35
|
+
placeholder?: string;
|
|
36
|
+
|
|
37
|
+
constructor(options: TextboxField) {
|
|
38
|
+
super(options, "");
|
|
39
|
+
this.type = options.type ?? "text";
|
|
40
|
+
this.placeholder = options.placeholder ?? "";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export const Default: { args: INvsDynamicForm } = {
|
|
45
|
+
args: {
|
|
46
|
+
onSubmit: (values) => {
|
|
47
|
+
alert(JSON.stringify(values));
|
|
48
|
+
},
|
|
49
|
+
submitButtonIsFullWidth: false,
|
|
50
|
+
submitButtonLabel: "Save",
|
|
51
|
+
submitButtonVisible: true,
|
|
52
|
+
submitButtonPosition: "right",
|
|
53
|
+
submitButton: {
|
|
54
|
+
component: ButtonComponent,
|
|
55
|
+
defaultOptions: {
|
|
56
|
+
label: "Save",
|
|
57
|
+
isFullWidth: true,
|
|
58
|
+
position: "right",
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
formElements: {
|
|
62
|
+
textbox: {
|
|
63
|
+
component: TextboxElement,
|
|
64
|
+
class: TextboxField,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
fields: [
|
|
68
|
+
new TextboxField({
|
|
69
|
+
id: "firstName",
|
|
70
|
+
label: "First Name",
|
|
71
|
+
defaultValue: "ismet",
|
|
72
|
+
screenSize: {
|
|
73
|
+
desktop: 6,
|
|
74
|
+
},
|
|
75
|
+
validate: Yup.string().required(),
|
|
76
|
+
}),
|
|
77
|
+
new TextboxField({
|
|
78
|
+
id: "lastName",
|
|
79
|
+
label: "Last Name",
|
|
80
|
+
screenSize: {
|
|
81
|
+
desktop: 6,
|
|
82
|
+
},
|
|
83
|
+
validate: Yup.string().required(),
|
|
84
|
+
}),
|
|
85
|
+
new TextboxField({
|
|
86
|
+
id: "emailAddress",
|
|
87
|
+
label: "E-mail Address",
|
|
88
|
+
screenSize: 12,
|
|
89
|
+
type: "email",
|
|
90
|
+
}),
|
|
91
|
+
],
|
|
92
|
+
},
|
|
93
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import "nvs-flexgrid";
|
|
2
|
+
import "./_style.css";
|
|
3
|
+
|
|
4
|
+
import * as Yup from "yup";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
DynamicObject,
|
|
8
|
+
FieldBase,
|
|
9
|
+
IScreenSize,
|
|
10
|
+
ScreenSizeType,
|
|
11
|
+
} from "../types";
|
|
12
|
+
import { Form, Formik, FormikErrors, FormikTouched } from "formik";
|
|
13
|
+
import React, { useEffect, useState } from "react";
|
|
14
|
+
|
|
15
|
+
import { INvsDynamicForm } from "./_type";
|
|
16
|
+
|
|
17
|
+
export const NvsDynamicForm = ({
|
|
18
|
+
onSubmit,
|
|
19
|
+
formElements = {},
|
|
20
|
+
fields = [],
|
|
21
|
+
formClass,
|
|
22
|
+
submitButton,
|
|
23
|
+
submitButtonVisible = true,
|
|
24
|
+
submitButtonLabel = submitButton.defaultOptions.label,
|
|
25
|
+
submitButtonIsFullWidth = submitButton.defaultOptions.isFullWidth,
|
|
26
|
+
submitButtonPosition = submitButton.defaultOptions.position,
|
|
27
|
+
}: INvsDynamicForm) => {
|
|
28
|
+
const getDefaultValues = (): DynamicObject => {
|
|
29
|
+
return fields.reduce((acc: DynamicObject, field: FieldBase<any>) => {
|
|
30
|
+
acc[field.id] = field.defaultValue;
|
|
31
|
+
return acc;
|
|
32
|
+
}, {});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const getValidateSchema = () => {
|
|
36
|
+
const validationSchema = fields.reduce(
|
|
37
|
+
(acc: { [key: string]: Yup.AnySchema }, field) => {
|
|
38
|
+
if (field?.validate) {
|
|
39
|
+
acc[field.id] = field.validate;
|
|
40
|
+
}
|
|
41
|
+
return acc;
|
|
42
|
+
},
|
|
43
|
+
{},
|
|
44
|
+
);
|
|
45
|
+
return Yup.object(validationSchema);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const [defaultValues, setDefaultValues] = useState(getDefaultValues());
|
|
49
|
+
const [validateSchema, setValidateSchema] = useState(getValidateSchema());
|
|
50
|
+
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
setDefaultValues(getDefaultValues());
|
|
53
|
+
setValidateSchema(getValidateSchema());
|
|
54
|
+
}, [fields]);
|
|
55
|
+
|
|
56
|
+
const createFieldItemClass = (
|
|
57
|
+
screenSize: ScreenSizeType | IScreenSize,
|
|
58
|
+
): Array<string> => {
|
|
59
|
+
const className: Array<string> = [];
|
|
60
|
+
if (typeof screenSize == "number") className.push("nvs-col-" + screenSize);
|
|
61
|
+
else {
|
|
62
|
+
className.push("nvs-col-md-" + screenSize?.desktop);
|
|
63
|
+
if (screenSize?.tablet) className.push("nvs-col-sm-" + screenSize.tablet);
|
|
64
|
+
if (screenSize?.mobile) className.push("nvs-col-xs-" + screenSize.mobile);
|
|
65
|
+
}
|
|
66
|
+
return className;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const createFormElement = (field: FieldBase<any>) => {
|
|
70
|
+
const Field = formElements[field.fieldType!]?.component;
|
|
71
|
+
return Field ? <Field {...field} /> : <></>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const createFormElements = (
|
|
75
|
+
errors: FormikErrors<DynamicObject>,
|
|
76
|
+
touched: FormikTouched<DynamicObject>,
|
|
77
|
+
) => {
|
|
78
|
+
return fields.map((field: FieldBase<any>) => (
|
|
79
|
+
<div
|
|
80
|
+
key={field.id}
|
|
81
|
+
className={createFieldItemClass(field.screenSize ?? 12).join(" ")}
|
|
82
|
+
>
|
|
83
|
+
{createFormElement({
|
|
84
|
+
...field,
|
|
85
|
+
error: errors[field.id],
|
|
86
|
+
touched: touched[field.id],
|
|
87
|
+
})}
|
|
88
|
+
</div>
|
|
89
|
+
));
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getSubmitButtonComponent = () => {
|
|
93
|
+
const SubmitButton = submitButton.component;
|
|
94
|
+
return <SubmitButton>{submitButtonLabel}</SubmitButton>;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const getButtonPositionClass = (position: "left" | "right" | "center") => {
|
|
98
|
+
const classes = {
|
|
99
|
+
left: "nvs-jc-start",
|
|
100
|
+
right: "nvs-jc-end",
|
|
101
|
+
center: "nvs-jc-center",
|
|
102
|
+
};
|
|
103
|
+
return classes[position];
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const createSubmitButton = () => {
|
|
107
|
+
const buttonClasses = ["df-button"];
|
|
108
|
+
|
|
109
|
+
submitButtonIsFullWidth && buttonClasses.push("nvs-col-12");
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<div
|
|
113
|
+
className={`nvs-row ${getButtonPositionClass(submitButtonPosition)}`}
|
|
114
|
+
>
|
|
115
|
+
<div className={buttonClasses.join(" ")}>
|
|
116
|
+
{getSubmitButtonComponent()}
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const createForm = (
|
|
123
|
+
errors: FormikErrors<DynamicObject>,
|
|
124
|
+
touched: FormikTouched<DynamicObject>,
|
|
125
|
+
) => (
|
|
126
|
+
<Form className={`nvs-container-fluid${formClass ? ` ${formClass}` : ""}`}>
|
|
127
|
+
<div className="nvs-row">{createFormElements(errors, touched)}</div>
|
|
128
|
+
{submitButtonVisible && createSubmitButton()}
|
|
129
|
+
</Form>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<Formik
|
|
134
|
+
initialValues={defaultValues}
|
|
135
|
+
validationSchema={validateSchema}
|
|
136
|
+
onSubmit={async (values) => {
|
|
137
|
+
onSubmit && (await onSubmit(values));
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
{({ errors, touched }) => createForm(errors, touched)}
|
|
141
|
+
</Formik>
|
|
142
|
+
);
|
|
143
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { FieldBase, SubmitButtonOptions } from "../types";
|
|
2
|
+
|
|
3
|
+
export interface INvsDynamicForm {
|
|
4
|
+
onSubmit?: ((values: unknown) => void) | ((values: unknown) => Promise<void>);
|
|
5
|
+
submitButton: {
|
|
6
|
+
component: React.FC<any>;
|
|
7
|
+
defaultOptions: SubmitButtonOptions;
|
|
8
|
+
};
|
|
9
|
+
formElements: {
|
|
10
|
+
[key: string]: {
|
|
11
|
+
component: React.FC<any>;
|
|
12
|
+
class: typeof FieldBase<any>;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
fields: Array<FieldBase<unknown>>;
|
|
16
|
+
formClass?: string;
|
|
17
|
+
submitButtonVisible?: boolean;
|
|
18
|
+
submitButtonLabel?: string;
|
|
19
|
+
submitButtonPosition?: "left" | "center" | "right";
|
|
20
|
+
submitButtonIsFullWidth?: boolean;
|
|
21
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as Yup from "yup";
|
|
2
|
+
|
|
3
|
+
import { IScreenSize, ScreenSizeType } from "./screen-size.type";
|
|
4
|
+
|
|
5
|
+
export abstract class FieldBase<ValueType> {
|
|
6
|
+
id!: string;
|
|
7
|
+
label!: string;
|
|
8
|
+
fieldType?: string;
|
|
9
|
+
defaultValue?: ValueType;
|
|
10
|
+
screenSize?: ScreenSizeType | IScreenSize;
|
|
11
|
+
validate?: Yup.AnySchema;
|
|
12
|
+
error?: string;
|
|
13
|
+
touched?: boolean;
|
|
14
|
+
|
|
15
|
+
constructor(options: FieldBase<ValueType>, fieldDefaultValue?: ValueType) {
|
|
16
|
+
this.defaultValue = options.defaultValue ?? fieldDefaultValue;
|
|
17
|
+
this.id = options.id;
|
|
18
|
+
this.label = options.label;
|
|
19
|
+
this.fieldType = options.fieldType;
|
|
20
|
+
this.screenSize = options.screenSize ?? 12;
|
|
21
|
+
this.validate = options.validate;
|
|
22
|
+
}
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nvs-dynamic-form/react-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
4
4
|
"types": "dist/cjs/index.d.ts",
|
|
5
5
|
"main": "dist/cjs/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
7
7
|
"files": [
|
|
8
8
|
"dist",
|
|
9
|
+
"lib",
|
|
9
10
|
"packages.json"
|
|
10
11
|
],
|
|
11
12
|
"scripts": {
|