pdyform 1.2.0 → 2.0.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.
- package/README.md +28 -1
- package/example/README.md +36 -0
- package/example/react-demo/dist/assets/index-BBU9cJqy.css +1 -0
- package/example/react-demo/dist/assets/index-DeJS8UcQ.js +105 -0
- package/example/react-demo/dist/index.html +13 -0
- package/example/react-demo/index.html +12 -0
- package/example/react-demo/node_modules/.bin/browserslist +17 -0
- package/example/react-demo/node_modules/.bin/tsc +17 -0
- package/example/react-demo/node_modules/.bin/tsserver +17 -0
- package/example/react-demo/node_modules/.bin/vite +17 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-checkbox.js +300 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-checkbox.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-label.js +194 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-label.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-radio-group.js +530 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-radio-group.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-select.js +4808 -0
- package/example/react-demo/node_modules/.vite/deps/@radix-ui_react-select.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/_metadata.json +115 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-3D5PZ6F6.js +49 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-3D5PZ6F6.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-5Q2RBQLA.js +127 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-5Q2RBQLA.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-G3PMV62Z.js +36 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-G3PMV62Z.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-GX7YZ5KV.js +370 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-GX7YZ5KV.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-PUFJGYAC.js +928 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-PUFJGYAC.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-SIU35MPB.js +21 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-SIU35MPB.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-TOMGVNQP.js +1906 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-TOMGVNQP.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-YYN6DZAU.js +21628 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-YYN6DZAU.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-ZE5VSJFE.js +144 -0
- package/example/react-demo/node_modules/.vite/deps/chunk-ZE5VSJFE.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/class-variance-authority.js +51 -0
- package/example/react-demo/node_modules/.vite/deps/class-variance-authority.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/clsx.js +10 -0
- package/example/react-demo/node_modules/.vite/deps/clsx.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/lucide-react.js +29725 -0
- package/example/react-demo/node_modules/.vite/deps/lucide-react.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/package.json +3 -0
- package/example/react-demo/node_modules/.vite/deps/react-dom.js +7 -0
- package/example/react-demo/node_modules/.vite/deps/react-dom.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/react-dom_client.js +39 -0
- package/example/react-demo/node_modules/.vite/deps/react-dom_client.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/react.js +6 -0
- package/example/react-demo/node_modules/.vite/deps/react.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +913 -0
- package/example/react-demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/react_jsx-runtime.js +7 -0
- package/example/react-demo/node_modules/.vite/deps/react_jsx-runtime.js.map +7 -0
- package/example/react-demo/node_modules/.vite/deps/tailwind-merge.js +2534 -0
- package/example/react-demo/node_modules/.vite/deps/tailwind-merge.js.map +7 -0
- package/example/react-demo/package.json +23 -0
- package/example/react-demo/postcss.config.mjs +6 -0
- package/example/react-demo/src/App.tsx +64 -0
- package/example/react-demo/src/main.tsx +10 -0
- package/example/react-demo/src/styles.css +102 -0
- package/example/react-demo/tailwind.config.mjs +50 -0
- package/example/react-demo/tsconfig.json +16 -0
- package/example/react-demo/vite.config.ts +14 -0
- package/example/shared/defaultSchema.ts +68 -0
- package/example/vue-demo/dist/assets/index-BhWj3D5x.css +1 -0
- package/example/vue-demo/dist/assets/index-Bw3THsrD.js +44 -0
- package/example/vue-demo/dist/index.html +13 -0
- package/example/vue-demo/index.html +12 -0
- package/example/vue-demo/node_modules/.bin/tsc +17 -0
- package/example/vue-demo/node_modules/.bin/tsserver +17 -0
- package/example/vue-demo/node_modules/.bin/vite +17 -0
- package/example/vue-demo/node_modules/.vite/deps/_metadata.json +46 -0
- package/example/vue-demo/node_modules/.vite/deps/chunk-PZ5AY32C.js +10 -0
- package/example/vue-demo/node_modules/.vite/deps/chunk-PZ5AY32C.js.map +7 -0
- package/example/vue-demo/node_modules/.vite/deps/chunk-TCXBSQ4M.js +12877 -0
- package/example/vue-demo/node_modules/.vite/deps/chunk-TCXBSQ4M.js.map +7 -0
- package/example/vue-demo/node_modules/.vite/deps/clsx.js +22 -0
- package/example/vue-demo/node_modules/.vite/deps/clsx.js.map +7 -0
- package/example/vue-demo/node_modules/.vite/deps/lucide-vue-next.js +29720 -0
- package/example/vue-demo/node_modules/.vite/deps/lucide-vue-next.js.map +7 -0
- package/example/vue-demo/node_modules/.vite/deps/package.json +3 -0
- package/example/vue-demo/node_modules/.vite/deps/radix-vue.js +24321 -0
- package/example/vue-demo/node_modules/.vite/deps/radix-vue.js.map +7 -0
- package/example/vue-demo/node_modules/.vite/deps/tailwind-merge.js +2534 -0
- package/example/vue-demo/node_modules/.vite/deps/tailwind-merge.js.map +7 -0
- package/example/vue-demo/node_modules/.vite/deps/vue.js +348 -0
- package/example/vue-demo/node_modules/.vite/deps/vue.js.map +7 -0
- package/example/vue-demo/package.json +20 -0
- package/example/vue-demo/postcss.config.mjs +6 -0
- package/example/vue-demo/src/App.vue +61 -0
- package/example/vue-demo/src/env.d.ts +1 -0
- package/example/vue-demo/src/main.ts +5 -0
- package/example/vue-demo/src/style.css +102 -0
- package/example/vue-demo/tailwind.config.mjs +50 -0
- package/example/vue-demo/tsconfig.json +15 -0
- package/example/vue-demo/vite.config.ts +14 -0
- package/package.json +7 -2
- package/packages/core/dist/{chunk-KQR3LFND.js → chunk-GQASS6PM.js} +20 -0
- package/packages/core/dist/index.cjs +20 -0
- package/packages/core/dist/index.js +1 -1
- package/packages/core/dist/utils.cjs +20 -0
- package/packages/core/dist/utils.js +1 -1
- package/packages/core/node_modules/.vite/vitest/results.json +1 -1
- package/packages/core/src/utils.ts +21 -0
- package/packages/core/test/utils.test.ts +6 -0
- package/packages/react/dist/index.cjs +22 -13
- package/packages/react/dist/index.js +22 -13
- package/packages/react/node_modules/.bin/vite +4 -4
- package/packages/react/node_modules/.vite/vitest/results.json +1 -1
- package/packages/react/src/components/InputRenderer.tsx +23 -12
- package/packages/react/test/FormFieldRenderer.test.tsx +10 -0
- package/packages/vue/dist/index.js +1 -1
- package/packages/vue/dist/index.mjs +9 -3
- package/packages/vue/node_modules/.vite/vitest/results.json +1 -1
- package/packages/vue/src/components/InputRenderer.vue +10 -1
- package/packages/vue/test/FormFieldRenderer.test.ts +11 -0
- package/pnpm-workspace.yaml +1 -1
|
@@ -26,6 +26,12 @@ __export(index_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
27
|
|
|
28
28
|
// src/utils.ts
|
|
29
|
+
function parseNumberish(value) {
|
|
30
|
+
if (typeof value === "number") return Number.isNaN(value) ? null : value;
|
|
31
|
+
if (typeof value !== "string" || value.trim() === "") return null;
|
|
32
|
+
const parsed = Number(value);
|
|
33
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
34
|
+
}
|
|
29
35
|
function validateField(value, field) {
|
|
30
36
|
if (!field.validations) return null;
|
|
31
37
|
for (const rule of field.validations) {
|
|
@@ -36,6 +42,13 @@ function validateField(value, field) {
|
|
|
36
42
|
}
|
|
37
43
|
break;
|
|
38
44
|
case "min":
|
|
45
|
+
if (field.type === "number") {
|
|
46
|
+
const numericValue = parseNumberish(value);
|
|
47
|
+
if (numericValue !== null && numericValue < rule.value) {
|
|
48
|
+
return rule.message || `${field.label} must be at least ${rule.value}`;
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
39
52
|
if (typeof value === "number" && value < rule.value) {
|
|
40
53
|
return rule.message || `${field.label} must be at least ${rule.value}`;
|
|
41
54
|
}
|
|
@@ -44,6 +57,13 @@ function validateField(value, field) {
|
|
|
44
57
|
}
|
|
45
58
|
break;
|
|
46
59
|
case "max":
|
|
60
|
+
if (field.type === "number") {
|
|
61
|
+
const numericValue = parseNumberish(value);
|
|
62
|
+
if (numericValue !== null && numericValue > rule.value) {
|
|
63
|
+
return rule.message || `${field.label} must be at most ${rule.value}`;
|
|
64
|
+
}
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
47
67
|
if (typeof value === "number" && value > rule.value) {
|
|
48
68
|
return rule.message || `${field.label} must be at most ${rule.value}`;
|
|
49
69
|
}
|
|
@@ -24,6 +24,12 @@ __export(utils_exports, {
|
|
|
24
24
|
validateField: () => validateField
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(utils_exports);
|
|
27
|
+
function parseNumberish(value) {
|
|
28
|
+
if (typeof value === "number") return Number.isNaN(value) ? null : value;
|
|
29
|
+
if (typeof value !== "string" || value.trim() === "") return null;
|
|
30
|
+
const parsed = Number(value);
|
|
31
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
32
|
+
}
|
|
27
33
|
function validateField(value, field) {
|
|
28
34
|
if (!field.validations) return null;
|
|
29
35
|
for (const rule of field.validations) {
|
|
@@ -34,6 +40,13 @@ function validateField(value, field) {
|
|
|
34
40
|
}
|
|
35
41
|
break;
|
|
36
42
|
case "min":
|
|
43
|
+
if (field.type === "number") {
|
|
44
|
+
const numericValue = parseNumberish(value);
|
|
45
|
+
if (numericValue !== null && numericValue < rule.value) {
|
|
46
|
+
return rule.message || `${field.label} must be at least ${rule.value}`;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
37
50
|
if (typeof value === "number" && value < rule.value) {
|
|
38
51
|
return rule.message || `${field.label} must be at least ${rule.value}`;
|
|
39
52
|
}
|
|
@@ -42,6 +55,13 @@ function validateField(value, field) {
|
|
|
42
55
|
}
|
|
43
56
|
break;
|
|
44
57
|
case "max":
|
|
58
|
+
if (field.type === "number") {
|
|
59
|
+
const numericValue = parseNumberish(value);
|
|
60
|
+
if (numericValue !== null && numericValue > rule.value) {
|
|
61
|
+
return rule.message || `${field.label} must be at most ${rule.value}`;
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
45
65
|
if (typeof value === "number" && value > rule.value) {
|
|
46
66
|
return rule.message || `${field.label} must be at most ${rule.value}`;
|
|
47
67
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.6.1","results":[[":test/utils.test.ts",{"duration":
|
|
1
|
+
{"version":"1.6.1","results":[[":test/utils.test.ts",{"duration":3,"failed":false}]]}
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { FormField } from './types';
|
|
2
2
|
|
|
3
|
+
function parseNumberish(value: unknown): number | null {
|
|
4
|
+
if (typeof value === 'number') return Number.isNaN(value) ? null : value;
|
|
5
|
+
if (typeof value !== 'string' || value.trim() === '') return null;
|
|
6
|
+
const parsed = Number(value);
|
|
7
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
export function validateField(value: any, field: FormField): string | null {
|
|
4
11
|
if (!field.validations) return null;
|
|
5
12
|
|
|
@@ -11,6 +18,13 @@ export function validateField(value: any, field: FormField): string | null {
|
|
|
11
18
|
}
|
|
12
19
|
break;
|
|
13
20
|
case 'min':
|
|
21
|
+
if (field.type === 'number') {
|
|
22
|
+
const numericValue = parseNumberish(value);
|
|
23
|
+
if (numericValue !== null && numericValue < rule.value) {
|
|
24
|
+
return rule.message || `${field.label} must be at least ${rule.value}`;
|
|
25
|
+
}
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
14
28
|
if (typeof value === 'number' && value < rule.value) {
|
|
15
29
|
return rule.message || `${field.label} must be at least ${rule.value}`;
|
|
16
30
|
}
|
|
@@ -19,6 +33,13 @@ export function validateField(value: any, field: FormField): string | null {
|
|
|
19
33
|
}
|
|
20
34
|
break;
|
|
21
35
|
case 'max':
|
|
36
|
+
if (field.type === 'number') {
|
|
37
|
+
const numericValue = parseNumberish(value);
|
|
38
|
+
if (numericValue !== null && numericValue > rule.value) {
|
|
39
|
+
return rule.message || `${field.label} must be at most ${rule.value}`;
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
22
43
|
if (typeof value === 'number' && value > rule.value) {
|
|
23
44
|
return rule.message || `${field.label} must be at most ${rule.value}`;
|
|
24
45
|
}
|
|
@@ -40,6 +40,12 @@ describe('core utils - validateField', () => {
|
|
|
40
40
|
expect(validateField(10, fieldNum)).toBeNull();
|
|
41
41
|
expect(validateField(21, fieldNum)).toBe('F2 must be at most 20');
|
|
42
42
|
});
|
|
43
|
+
|
|
44
|
+
it('treats numeric strings as numbers for number fields', () => {
|
|
45
|
+
expect(validateField('9', fieldNum)).toBe('F2 must be at least 10');
|
|
46
|
+
expect(validateField('10', fieldNum)).toBeNull();
|
|
47
|
+
expect(validateField('22', fieldNum)).toBe('F2 must be at most 20');
|
|
48
|
+
});
|
|
43
49
|
});
|
|
44
50
|
|
|
45
51
|
describe('email', () => {
|
|
@@ -249,19 +249,28 @@ Label.displayName = LabelPrimitive.Root.displayName;
|
|
|
249
249
|
|
|
250
250
|
// src/components/InputRenderer.tsx
|
|
251
251
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
252
|
-
var InputRenderer = ({ field, value, onChange, onBlur, fieldId }) =>
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
252
|
+
var InputRenderer = ({ field, value, onChange, onBlur, fieldId }) => {
|
|
253
|
+
const handleChange = (nextValue) => {
|
|
254
|
+
if (field.type !== "number") {
|
|
255
|
+
onChange(nextValue);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
onChange(nextValue === "" ? "" : Number(nextValue));
|
|
259
|
+
};
|
|
260
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
261
|
+
Input,
|
|
262
|
+
{
|
|
263
|
+
id: fieldId,
|
|
264
|
+
type: field.type,
|
|
265
|
+
placeholder: field.placeholder,
|
|
266
|
+
value: value ?? "",
|
|
267
|
+
onChange: (e) => handleChange(e.target.value),
|
|
268
|
+
onBlur,
|
|
269
|
+
disabled: field.disabled,
|
|
270
|
+
name: field.name
|
|
271
|
+
}
|
|
272
|
+
);
|
|
273
|
+
};
|
|
265
274
|
var InputRenderer_default = InputRenderer;
|
|
266
275
|
|
|
267
276
|
// src/components/TextareaRenderer.tsx
|
|
@@ -194,19 +194,28 @@ Label.displayName = LabelPrimitive.Root.displayName;
|
|
|
194
194
|
|
|
195
195
|
// src/components/InputRenderer.tsx
|
|
196
196
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
197
|
-
var InputRenderer = ({ field, value, onChange, onBlur, fieldId }) =>
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
197
|
+
var InputRenderer = ({ field, value, onChange, onBlur, fieldId }) => {
|
|
198
|
+
const handleChange = (nextValue) => {
|
|
199
|
+
if (field.type !== "number") {
|
|
200
|
+
onChange(nextValue);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
onChange(nextValue === "" ? "" : Number(nextValue));
|
|
204
|
+
};
|
|
205
|
+
return /* @__PURE__ */ jsx7(
|
|
206
|
+
Input,
|
|
207
|
+
{
|
|
208
|
+
id: fieldId,
|
|
209
|
+
type: field.type,
|
|
210
|
+
placeholder: field.placeholder,
|
|
211
|
+
value: value ?? "",
|
|
212
|
+
onChange: (e) => handleChange(e.target.value),
|
|
213
|
+
onBlur,
|
|
214
|
+
disabled: field.disabled,
|
|
215
|
+
name: field.name
|
|
216
|
+
}
|
|
217
|
+
);
|
|
218
|
+
};
|
|
210
219
|
var InputRenderer_default = InputRenderer;
|
|
211
220
|
|
|
212
221
|
// src/components/TextareaRenderer.tsx
|
|
@@ -6,12 +6,12 @@ case `uname` in
|
|
|
6
6
|
esac
|
|
7
7
|
|
|
8
8
|
if [ -z "$NODE_PATH" ]; then
|
|
9
|
-
export NODE_PATH="/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@
|
|
9
|
+
export NODE_PATH="/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules/vite/bin/node_modules:/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules/vite/node_modules:/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules:/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/node_modules"
|
|
10
10
|
else
|
|
11
|
-
export NODE_PATH="/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@
|
|
11
|
+
export NODE_PATH="/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules/vite/bin/node_modules:/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules/vite/node_modules:/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules:/Users/pidan/Work/Learn/dynamic-form/node_modules/.pnpm/node_modules:$NODE_PATH"
|
|
12
12
|
fi
|
|
13
13
|
if [ -x "$basedir/node" ]; then
|
|
14
|
-
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/vite@
|
|
14
|
+
exec "$basedir/node" "$basedir/../../../../node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules/vite/bin/vite.js" "$@"
|
|
15
15
|
else
|
|
16
|
-
exec node "$basedir/../../../../node_modules/.pnpm/vite@
|
|
16
|
+
exec node "$basedir/../../../../node_modules/.pnpm/vite@7.3.1_@types+node@20.19.35_jiti@1.21.7/node_modules/vite/bin/vite.js" "$@"
|
|
17
17
|
fi
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.6.1","results":[[":test/
|
|
1
|
+
{"version":"1.6.1","results":[[":test/DynamicForm.test.tsx",{"duration":30,"failed":false}],[":test/FormFieldRenderer.test.tsx",{"duration":129,"failed":false}]]}
|
|
@@ -2,17 +2,28 @@ import React from 'react';
|
|
|
2
2
|
import type { FieldRenderContext } from './types';
|
|
3
3
|
import { Input } from './Input';
|
|
4
4
|
|
|
5
|
-
const InputRenderer: React.FC<FieldRenderContext> = ({ field, value, onChange, onBlur, fieldId }) =>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
const InputRenderer: React.FC<FieldRenderContext> = ({ field, value, onChange, onBlur, fieldId }) => {
|
|
6
|
+
const handleChange = (nextValue: string) => {
|
|
7
|
+
if (field.type !== 'number') {
|
|
8
|
+
onChange(nextValue);
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
onChange(nextValue === '' ? '' : Number(nextValue));
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<Input
|
|
17
|
+
id={fieldId}
|
|
18
|
+
type={field.type}
|
|
19
|
+
placeholder={field.placeholder}
|
|
20
|
+
value={value ?? ''}
|
|
21
|
+
onChange={(e) => handleChange(e.target.value)}
|
|
22
|
+
onBlur={onBlur}
|
|
23
|
+
disabled={field.disabled}
|
|
24
|
+
name={field.name}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
17
28
|
|
|
18
29
|
export default InputRenderer;
|
|
@@ -25,6 +25,16 @@ describe('FormFieldRenderer', () => {
|
|
|
25
25
|
expect(onChange).toHaveBeenCalledWith('new value');
|
|
26
26
|
});
|
|
27
27
|
|
|
28
|
+
it('emits number for number inputs', () => {
|
|
29
|
+
const field: FormField = { ...defaultField, type: 'number' };
|
|
30
|
+
const onChange = vi.fn();
|
|
31
|
+
render(<FormFieldRenderer field={field} value={22} onChange={onChange} />);
|
|
32
|
+
|
|
33
|
+
const input = screen.getByLabelText('Test Label') as HTMLInputElement;
|
|
34
|
+
fireEvent.change(input, { target: { value: '23' } });
|
|
35
|
+
expect(onChange).toHaveBeenCalledWith(23);
|
|
36
|
+
});
|
|
37
|
+
|
|
28
38
|
it('renders a textarea correctly', () => {
|
|
29
39
|
const field: FormField = { ...defaultField, type: 'textarea' };
|
|
30
40
|
const onChange = vi.fn();
|