src/components/molecules/Form/index.tsx   A
last analyzed

Complexity

Total Complexity 6
Complexity/F 0

Size

Lines of Code 166
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 6
eloc 101
mnd 6
bc 6
fnc 0
dl 0
loc 166
ccs 47
cts 47
cp 1
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1 1
import "./styles.css";
2
3 1
import React from "react";
4
5
import { FormComponent } from "./types";
6
7 1
import { buildBoxComponent } from "../../../utils";
8
9 1
import Button from "../../atoms/Button";
10 1
import CheckBox from "../../atoms/CheckBox";
11 1
import Counter from "../../atoms/Counter";
12 1
import Input from "../../atoms/Input";
13
14
/**
15
 * A
16
 *
17
 * @since 0.8.0
18
 *
19
 * @param {string} title Form title
20
 * @param {Record<string,  { header?: string; type?: "boolean" | "text" | "numeric" }>} fields Form fields array. Every field must be an object with optionally 2 properties:
21
 *     - `header` - the text showed upon the field component)
22
 *     - `type` - the field type (will determine a specific type when returned, and also the UI component associated)
23
 *
24
 * allowed types are:
25
 *     - `boolean` (rendered as a {@link CheckBox})
26
 *     - `numeric` (rendered as a {@link Counter})
27
 *
28
 * A field object can be empty, in this case default values will be used (`input` type with empty header)
29
 * @param {(values: Record<string, string>) => void} onSubmit callback triggered on Form submit
30
 * @param {string} submitLabel custom submit button label
31
 * @param {string} className `common modular-ui prop` - custom className (to better customize it)
32
 * @param {boolean} unstyled `common modular-ui prop` - Style/unstyle component (to better customize it)
33
 * @param {string} id `common modular-ui prop` - `data-id` parameter (for testing purpose, to easily find the component into the DOM)
34
 * @param {boolean} dark `common modular-ui prop` - Enable/disable dark mode
35
 * @param {boolean} hide `common modular-ui prop` - Hide/show component
36
 * @param {boolean} shadow `common modular-ui prop` - Enable/disable shadow behind component (to better customize it)
37
 *
38
 * @example <caption>Example Form usage</caption>
39
 * import { render } from "react-dom";
40
 * import { Field } from '@cianciarusocataldo/modular-ui';
41
 *
42
 * render(<Form fields={{ "Field 0": { header:"Field 0 header" } }} onSubmit={()=>alert('Submitted !')} />, document.getElementById("root"));
43
 *
44
 * @see https://cianciarusocataldo.github.io/modular-ui/components/molecules/Form
45
 *
46
 * @author Cataldo Cianciaruso <https://github.com/CianciarusoCataldo>
47
 *
48
 * @copyright 2022 Cataldo Cianciaruso
49
 */
50 1
const Form: FormComponent = ({
51 9
  title,
52 9
  fields,
53 9
  onSubmit,
54 9
  submitLabel,
55 9
  label,
56 9
  ...commonProps
57
}) => {
58 9
  const dropdownFields: Record<string, string | boolean | number> = fields
59
    ? Object.keys(fields).reduce(
60 21
        (o, key) => ({
61
          ...o,
62
          [key]:
63
            fields[key].type === "boolean"
64
              ? false
65
              : fields[key].type === "numeric"
66
              ? 0
67
              : "",
68
        }),
69
        {}
70
      )
71
    : {};
72
73 9
  const [values, setValues] =
74 9
    React.useState<Record<string, string | boolean | number>>(dropdownFields);
75
76 9
  return buildBoxComponent({
77 9
    callBack: () => ({
78
      name: "modular-form",
79
      Component: [
80
        <p key="form_title" className="title">
81
          {title}
82
        </p>,
83
        ...Object.keys(dropdownFields).map((field, index) => {
84 21
          const name = field;
85 21
          const { type, header } = fields[field];
86
87 21
          let FieldElement: (props: any) => JSX.Element = Input;
88
89 21
          let fieldProps: Record<string, any> = {
90
            label: <span className="header">{header}</span>,
91
            value: String(values[field]),
92
            id: `form-field-${index}`,
93
            onChange: (newValue: string) => {
94 3
              let tmpValues = { ...values };
95 3
              let value = String(newValue);
96 3
              if (value.length < 1) {
97 1
                tmpValues[name] = "";
98
              } else {
99 2
                tmpValues[name] = value;
100
              }
101
102 3
              setValues(tmpValues);
103
            },
104
            className: "form-input",
105
          };
106
107 21
          if (type) {
108 13
            switch (type) {
109
              case "boolean":
110
                {
111 8
                  FieldElement = CheckBox;
112 8
                  fieldProps = {
113
                    ...fieldProps,
114
                    className: "",
115
                    value: values[field] as boolean,
116
                    onChange: (value: boolean) => {
117 1
                      let tmpValues = { ...values };
118 1
                      tmpValues[field] = value;
119 1
                      setValues(tmpValues);
120
                    },
121
                  };
122
                }
123 8
                break;
124
              case "numeric":
125
                {
126 5
                  FieldElement = Counter;
127 5
                  fieldProps = {
128
                    ...fieldProps,
129
                    value: values[field] as number,
130
                    onChange: (value: number) => {
131 1
                      let tmpValues = { ...values };
132 1
                      tmpValues[field] = value;
133 1
                      setValues(tmpValues);
134
                    },
135
                  };
136
                }
137 5
                break;
138
            }
139
          }
140
141 21
          return (
142
            <div className="field" key={`form_field_${index}`}>
143
              {<FieldElement {...fieldProps} />}
144
            </div>
145
          );
146
        }),
147
        <div key="form_submit_button" className="submit-button">
148
          <Button
149
            dark={!commonProps.dark}
150
            id="form-submit-button"
151
            onClick={() => {
152 2
              onSubmit && onSubmit(values);
153
            }}
154
          >
155
            {submitLabel}
156
          </Button>
157
        </div>,
158
      ],
159
      commonProps,
160
    }),
161
    label,
162
  });
163
};
164
165
export default Form;
166