react-form-manage 1.0.8-beta.9 → 1.0.8

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 (94) hide show
  1. package/CHANGELOG.md +173 -4
  2. package/README.md +8 -4
  3. package/dist/components/Form/FormCleanUp.js +3 -3
  4. package/dist/components/Form/FormItem.d.ts +10 -4
  5. package/dist/components/Form/FormItem.js +52 -14
  6. package/dist/components/Form/FormList.d.ts +2 -2
  7. package/dist/components/Form/FormList.js +2 -2
  8. package/dist/constants/form.d.ts +1 -1
  9. package/dist/hooks/useFormItemControl.d.ts +8 -3
  10. package/dist/hooks/useFormItemControl.js +64 -28
  11. package/dist/hooks/useFormListControl.d.ts +2 -1
  12. package/dist/hooks/useFormListControl.js +85 -19
  13. package/dist/index.cjs.d.ts +1 -0
  14. package/dist/index.d.ts +4 -3
  15. package/dist/index.esm.d.ts +1 -0
  16. package/dist/index.js +4 -2
  17. package/dist/providers/Form.d.ts +15 -2
  18. package/dist/providers/Form.js +197 -22
  19. package/dist/stores/formStore.d.ts +44 -4
  20. package/dist/stores/formStore.js +42 -7
  21. package/dist/test/CommonTest.d.ts +3 -0
  22. package/dist/test/CommonTest.js +49 -0
  23. package/dist/test/TestDialog.d.ts +3 -0
  24. package/dist/test/TestDialog.js +21 -0
  25. package/dist/test/TestListener.d.ts +3 -0
  26. package/dist/test/TestListener.js +17 -0
  27. package/dist/test/TestNotFormWrapper.d.ts +3 -0
  28. package/dist/test/TestNotFormWrapper.js +15 -0
  29. package/dist/test/TestSelect.d.ts +6 -0
  30. package/dist/test/TestSelect.js +24 -0
  31. package/dist/test/TestWatchNormalize.d.ts +3 -0
  32. package/dist/test/TestWatchNormalize.js +23 -0
  33. package/dist/test/TestWrapperFormItem.d.ts +3 -0
  34. package/dist/test/TestWrapperFormItem.js +13 -0
  35. package/dist/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.d.ts +21 -0
  36. package/dist/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.js +61 -0
  37. package/dist/test/testSetValue/TestCase1_PlainObjectToPrimitives.d.ts +16 -0
  38. package/dist/test/testSetValue/TestCase1_PlainObjectToPrimitives.js +18 -0
  39. package/dist/test/testSetValue/TestCase2_PlainObjectToFormList.d.ts +21 -0
  40. package/dist/test/testSetValue/TestCase2_PlainObjectToFormList.js +33 -0
  41. package/dist/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.d.ts +21 -0
  42. package/dist/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.js +26 -0
  43. package/dist/test/testSetValue/TestCase4_PlainObjectRemovedFields.d.ts +20 -0
  44. package/dist/test/testSetValue/TestCase4_PlainObjectRemovedFields.js +32 -0
  45. package/dist/test/testSetValue/TestCase5_FormListRemovedItems.d.ts +22 -0
  46. package/dist/test/testSetValue/TestCase5_FormListRemovedItems.js +29 -0
  47. package/dist/test/testSetValue/TestCase6_NestedFormListRemoved.d.ts +28 -0
  48. package/dist/test/testSetValue/TestCase6_NestedFormListRemoved.js +36 -0
  49. package/dist/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.d.ts +17 -0
  50. package/dist/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.js +33 -0
  51. package/dist/test/testSetValue/TestCase8_SetFieldValues_NestedObject.d.ts +27 -0
  52. package/dist/test/testSetValue/TestCase8_SetFieldValues_NestedObject.js +57 -0
  53. package/dist/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.d.ts +25 -0
  54. package/dist/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.js +46 -0
  55. package/dist/test/testSetValue/index.d.ts +2 -0
  56. package/dist/test/testSetValue/index.js +28 -0
  57. package/dist/types/index.d.ts +1 -1
  58. package/dist/types/public.d.ts +1 -1
  59. package/dist/utils/obj.util.d.ts +29 -1
  60. package/dist/utils/obj.util.js +59 -5
  61. package/package.json +2 -1
  62. package/src/App.tsx +38 -163
  63. package/src/DEEP_TRIGGER_LOGIC.md +573 -0
  64. package/src/components/Form/FormCleanUp.tsx +4 -8
  65. package/src/components/Form/FormItem.tsx +174 -57
  66. package/src/components/Form/FormList.tsx +17 -4
  67. package/src/constants/form.ts +1 -1
  68. package/src/hooks/useFormItemControl.ts +78 -32
  69. package/src/hooks/useFormListControl.ts +133 -43
  70. package/src/index.ts +25 -13
  71. package/src/main.tsx +6 -1
  72. package/src/providers/Form.tsx +451 -23
  73. package/src/stores/formStore.ts +363 -283
  74. package/src/test/CommonTest.tsx +177 -0
  75. package/src/test/TestDialog.tsx +52 -0
  76. package/src/test/TestListener.tsx +21 -0
  77. package/src/test/TestNotFormWrapper.tsx +43 -0
  78. package/src/test/TestSelect.tsx +38 -0
  79. package/src/test/TestWatchNormalize.tsx +32 -0
  80. package/src/test/TestWrapperFormItem.tsx +34 -0
  81. package/src/test/testSetValue/TestCase10_SetFieldValues_ComplexNested.tsx +203 -0
  82. package/src/test/testSetValue/TestCase1_PlainObjectToPrimitives.tsx +72 -0
  83. package/src/test/testSetValue/TestCase2_PlainObjectToFormList.tsx +114 -0
  84. package/src/test/testSetValue/TestCase3_ArrayNonListenerToPrimitives.tsx +99 -0
  85. package/src/test/testSetValue/TestCase4_PlainObjectRemovedFields.tsx +112 -0
  86. package/src/test/testSetValue/TestCase5_FormListRemovedItems.tsx +119 -0
  87. package/src/test/testSetValue/TestCase6_NestedFormListRemoved.tsx +185 -0
  88. package/src/test/testSetValue/TestCase7_SetFieldValues_MixedStructure.tsx +110 -0
  89. package/src/test/testSetValue/TestCase8_SetFieldValues_NestedObject.tsx +162 -0
  90. package/src/test/testSetValue/TestCase9_SetFieldValues_MultipleArrays.tsx +169 -0
  91. package/src/test/testSetValue/index.tsx +100 -0
  92. package/src/types/index.ts +1 -1
  93. package/src/types/public.ts +1 -1
  94. package/src/utils/obj.util.ts +153 -13
@@ -0,0 +1,177 @@
1
+ import { Button, Checkbox, Input } from "antd";
2
+ import { motion } from "framer-motion";
3
+ import FormItem from "../components/Form/FormItem";
4
+ import FormList from "../components/Form/FormList";
5
+ import InputWrapper from "../components/Form/InputWrapper";
6
+ import Form from "../providers/Form";
7
+
8
+ type Props = {};
9
+
10
+ function CommonTest({}: Props) {
11
+ const [form] = Form.useForm("form1");
12
+
13
+ const watchCheckBox = Form.useWatch("checkControlledAfterInit", "form1");
14
+
15
+ return (
16
+ <Form
17
+ initialValues={{
18
+ TestData: "",
19
+ numericCode: "",
20
+ // arr: [{ el: "Item 1" }, { el: "Item 2" }],
21
+ }}
22
+ onFinish={(values) => {
23
+ console.log(values);
24
+ }}
25
+ formName={"form1"}
26
+ // hidden
27
+ >
28
+ <FormItem
29
+ name={"username"}
30
+ rules={[
31
+ {
32
+ required: true,
33
+ message: "Test",
34
+ },
35
+ ]}
36
+ initialValue={"283746"}
37
+ // hidden
38
+ >
39
+ <InputWrapper>
40
+ <Input />
41
+ </InputWrapper>
42
+ </FormItem>
43
+
44
+ {/* Numberic test */}
45
+ <FormItem
46
+ name={"numericCode"}
47
+ rules={[
48
+ { required: true, message: "Vui lòng nhập mã" },
49
+ {
50
+ pattern: /^\d+$/,
51
+ message: "Chỉ cho phép chuỗi số",
52
+ },
53
+ ]}
54
+ >
55
+ <InputWrapper>
56
+ <Input placeholder="Mã chỉ gồm số" style={{ width: 200 }} />
57
+ </InputWrapper>
58
+ </FormItem>
59
+
60
+ {/* Motion Test */}
61
+ <motion.div
62
+ initial={{ opacity: 0 }}
63
+ animate={{ opacity: 1 }}
64
+ exit={{ opacity: 0 }}
65
+ transition={{ duration: 1 }}
66
+ >
67
+ <FormItem name="motionTest" controlAfterInit initialValue={"1234134"}>
68
+ <InputWrapper>
69
+ <Input placeholder="Motion Test" style={{ width: 200 }} />
70
+ </InputWrapper>
71
+ </FormItem>
72
+ </motion.div>
73
+ <FormList
74
+ initialValues={[
75
+ {
76
+ el: "sdfsdf",
77
+ d: { child: "Test Child" },
78
+ },
79
+ ]}
80
+ name="arr"
81
+ >
82
+ {(fields, { add, remove, move }) => (
83
+ <div>
84
+ {fields.map((field, index) => (
85
+ <div key={field.key} style={{ marginBottom: 8 }}>
86
+ <FormItem name={`${field.name}.el`} initialValue={"Chém gió"}>
87
+ <InputWrapper>
88
+ <Input placeholder="Item value" style={{ width: 200 }} />
89
+ </InputWrapper>
90
+ </FormItem>
91
+ <FormItem
92
+ name={`${field.name}.d.child`}
93
+ initialValue={"Con của item"}
94
+ >
95
+ <InputWrapper>
96
+ <Input placeholder="Item value" style={{ width: 200 }} />
97
+ </InputWrapper>
98
+ </FormItem>
99
+ <Button
100
+ onClick={() => remove({ index })}
101
+ style={{ marginLeft: 8 }}
102
+ >
103
+ Remove
104
+ </Button>
105
+ {index > 0 && (
106
+ <Button
107
+ onClick={() => move({ from: index, to: index - 1 })}
108
+ style={{ marginLeft: 8 }}
109
+ >
110
+ Move Up
111
+ </Button>
112
+ )}
113
+ {index < fields.length - 1 && (
114
+ <Button
115
+ onClick={() => move({ from: index, to: index + 1 })}
116
+ style={{ marginLeft: 8 }}
117
+ >
118
+ Move Down
119
+ </Button>
120
+ )}
121
+ </div>
122
+ ))}
123
+ <Button onClick={() => add(fields.length)}>Add Item</Button>
124
+ </div>
125
+ )}
126
+ </FormList>
127
+ <Button
128
+ onClick={() => {
129
+ form?.setFieldValue("arr", [
130
+ { el: "Set Item 1" },
131
+ { el: "Set Item 2" },
132
+ { el: "Set Item 3" },
133
+ ]);
134
+ }}
135
+ >
136
+ Test set array list value
137
+ </Button>
138
+ <motion.div
139
+ initial={{ opacity: 0 }}
140
+ animate={{ opacity: 1 }}
141
+ exit={{ opacity: 0 }}
142
+ transition={{ duration: 1.5 }}
143
+ >
144
+ <Form.Item
145
+ valuePropName="checked"
146
+ getValueFromEvent={(_, checked) => checked}
147
+ name="checkControlledAfterInit"
148
+ controlAfterInit={true}
149
+ initialValue={true}
150
+ hidden
151
+ >
152
+ <Checkbox />
153
+ </Form.Item>
154
+ </motion.div>
155
+ {/* <TestFormWatch /> */}
156
+ <Button
157
+ onClick={() => {
158
+ const current = form?.getFieldValue("checkControlledAfterInit");
159
+ console.log("Toggle controlled after init: ", current);
160
+ form?.setFieldValue("checkControlledAfterInit", !current);
161
+ }}
162
+ >
163
+ Toggle
164
+ </Button>
165
+ <Button htmlType="submit">Submit</Button>
166
+ <Button
167
+ onClick={() => {
168
+ form?.resetFields?.();
169
+ }}
170
+ >
171
+ Reset
172
+ </Button>
173
+ </Form>
174
+ );
175
+ }
176
+
177
+ export default CommonTest;
@@ -0,0 +1,52 @@
1
+ import {
2
+ Button,
3
+ Dialog,
4
+ DialogContent,
5
+ DialogContentText,
6
+ DialogTitle,
7
+ TextField,
8
+ } from "@mui/material";
9
+ import { Box } from "@mui/system";
10
+ import { useToggle } from "minh-custom-hooks-release";
11
+ import FormItem from "../components/Form/FormItem";
12
+ import TestSelect from "./TestSelect";
13
+
14
+ type Props = {};
15
+
16
+ function TestDialog({}: Props) {
17
+ const { state: open, toggle } = useToggle();
18
+ return (
19
+ <Box>
20
+ <Dialog open={open} onClose={toggle}>
21
+ <DialogTitle>Test Dialog</DialogTitle>
22
+ <DialogContent>
23
+ <DialogContentText>This is a test dialog.</DialogContentText>
24
+ <FormItem
25
+ rules={[
26
+ {
27
+ handler(value) {
28
+ console.log("Validating in dialog: ", value);
29
+ return Boolean(value);
30
+ },
31
+ message: "Testt",
32
+ },
33
+ ]}
34
+ controlAfterInit
35
+ initialValue={null}
36
+ name="testSelectInsideDialog"
37
+ >
38
+ <TestSelect />
39
+ </FormItem>
40
+ <FormItem controlAfterInit initialValue={""} name="anotherField">
41
+ <TextField />
42
+ </FormItem>
43
+ </DialogContent>
44
+ </Dialog>
45
+ <Button variant="contained" onClick={toggle}>
46
+ Open Test Dialog
47
+ </Button>
48
+ </Box>
49
+ );
50
+ }
51
+
52
+ export default TestDialog;
@@ -0,0 +1,21 @@
1
+ import { cloneDeep } from "lodash";
2
+ import { useEffect } from "react";
3
+ import { useShallow } from "zustand/react/shallow";
4
+ import { useFormStore } from "../stores/formStore";
5
+
6
+ type Props = {};
7
+
8
+ function TestListener({}: Props) {
9
+ const { listeners } = useFormStore(
10
+ useShallow((state) => ({
11
+ listeners: state.listeners,
12
+ })),
13
+ );
14
+
15
+ useEffect(() => {
16
+ console.log("Listeners updated: ", cloneDeep(listeners));
17
+ }, [listeners]);
18
+ return <div>TestListener</div>;
19
+ }
20
+
21
+ export default TestListener;
@@ -0,0 +1,43 @@
1
+ import { Button, Input } from "antd";
2
+
3
+ type Props = {};
4
+
5
+ function TestNotFormWrapper({}: Props) {
6
+ return (
7
+ <div
8
+ onSubmit={(e) => {
9
+ e.preventDefault();
10
+ console.log("Div Submit");
11
+ }}
12
+ >
13
+ <form
14
+ id="not-form"
15
+ onSubmit={(e) => {
16
+ e.preventDefault();
17
+ console.log("Form Submit");
18
+ }}
19
+ >
20
+ <div
21
+ id="child-form"
22
+ onSubmit={(e) => {
23
+ e.preventDefault();
24
+
25
+ console.log("Child Form Submit");
26
+ }}
27
+ >
28
+ <Input />
29
+ <Button htmlType="submit" form="child-form">
30
+ Child Form Submit
31
+ </Button>
32
+ </div>
33
+ <Input />
34
+
35
+ <Button htmlType="submit" form="not-form">
36
+ Submit
37
+ </Button>
38
+ </form>
39
+ </div>
40
+ );
41
+ }
42
+
43
+ export default TestNotFormWrapper;
@@ -0,0 +1,38 @@
1
+ import { Autocomplete, TextField } from "@mui/material";
2
+
3
+ type Props = {
4
+ value?: any;
5
+ onChange?: (value: any) => void;
6
+ };
7
+
8
+ function TestSelect({ value = null, onChange }: Props) {
9
+ return (
10
+ <Autocomplete
11
+ value={value}
12
+ onChange={(_, newValue) => {
13
+ onChange?.(newValue);
14
+ }}
15
+ renderInput={(params) => <TextField {...params} />}
16
+ options={[
17
+ {
18
+ value: "option1",
19
+ label: "Option 1",
20
+ },
21
+ {
22
+ value: "option2",
23
+ label: "Option 2",
24
+ },
25
+ ]}
26
+ getOptionKey={(o) => {
27
+ // console.log("Option get key: ", o);
28
+ return o?.value;
29
+ }}
30
+ getOptionLabel={(o) => {
31
+ // console.log("Option get label: ", o);
32
+ return o?.label;
33
+ }}
34
+ />
35
+ );
36
+ }
37
+
38
+ export default TestSelect;
@@ -0,0 +1,32 @@
1
+ import { Input } from "antd";
2
+ import React from "react";
3
+ import Form from "../providers/Form";
4
+
5
+ type Props = {};
6
+
7
+ function TestWatchNormalize({}: Props) {
8
+ const normalizeData = Form.useWatchNormalized({
9
+ name: "normalizeItem",
10
+ formNameOrFormInstance: "testNormalize",
11
+ normalizeFn: (value) => {
12
+ if (typeof value === "string") {
13
+ return value.toUpperCase();
14
+ }
15
+ return value;
16
+ },
17
+ });
18
+
19
+ React.useEffect(() => {
20
+ console.log("Normalized Data: ", normalizeData);
21
+ }, [normalizeData]);
22
+ return (
23
+ <Form formName="testNormalize">
24
+ Test Watch Normalize
25
+ <Form.Item name="normalizeItem">
26
+ <Input />
27
+ </Form.Item>
28
+ </Form>
29
+ );
30
+ }
31
+
32
+ export default TestWatchNormalize;
@@ -0,0 +1,34 @@
1
+ import { Button, Col, Input, Row } from "antd";
2
+ import { useToggle } from "minh-custom-hooks-release";
3
+ import Form from "../providers/Form";
4
+
5
+ type Props = {};
6
+
7
+ function TestWrapperFormItem({}: Props) {
8
+ const { state, toggle } = useToggle(false);
9
+ return (
10
+ <Form
11
+ onFinish={(values, all) => {
12
+ console.log(values, all);
13
+ }}
14
+ formName="testWrapper"
15
+ >
16
+ <Row>
17
+ <Form.Item
18
+ hidden={!state}
19
+ name="test"
20
+ collectOnHidden={false}
21
+ initialValue={"test"}
22
+ Component={Col}
23
+ xs={12}
24
+ >
25
+ <Input />
26
+ </Form.Item>
27
+ </Row>
28
+ <Button htmlType="submit">Submit</Button>
29
+ <Button onClick={toggle}>{state ? "Off" : "On"}</Button>
30
+ </Form>
31
+ );
32
+ }
33
+
34
+ export default TestWrapperFormItem;
@@ -0,0 +1,203 @@
1
+ import { Box } from "@mui/material";
2
+ import { Button, Input, Typography } from "antd";
3
+ import FormList from "../../components/Form/FormList";
4
+ import Form from "../../providers/Form";
5
+
6
+ type Props = {};
7
+
8
+ /**
9
+ * Test Case 10: setFieldValues với Complex Nested Structure
10
+ *
11
+ * Cấu trúc phức tạp:
12
+ * - config.appName (string)
13
+ * - config.version (string)
14
+ * - config.features (array) - deepTrigger
15
+ * - users (FormList array) - deepTrigger
16
+ * - users.0.name (string)
17
+ * - users.0.roles (array) - nested array trong FormList
18
+ *
19
+ * Test: setFieldValues với structure phức tạp nhiều cấp
20
+ *
21
+ * Kỳ vọng:
22
+ * - Primitives trigger onChange
23
+ * - Arrays ở mọi level đều gọi handleDeepTriggerSet
24
+ * - Nested arrays trong FormList được xử lý đúng
25
+ */
26
+ function TestCase10_SetFieldValues_ComplexNested({}: Props) {
27
+ const [form] = Form.useForm("testCase10");
28
+
29
+ const handleTestSetFieldValues = () => {
30
+ form?.setFieldValues({
31
+ config: {
32
+ appName: "My App",
33
+ version: "1.0.0",
34
+ features: ["auth", "api", "ui"],
35
+ },
36
+ users: [
37
+ { name: "John", roles: ["admin", "editor"] },
38
+ { name: "Jane", roles: ["viewer"] },
39
+ ],
40
+ });
41
+ console.log("✅ Called setFieldValues with complex nested structure");
42
+ };
43
+
44
+ const handleTestUpdateStructure = () => {
45
+ form?.setFieldValues({
46
+ config: {
47
+ appName: "Updated App",
48
+ version: "2.0.0",
49
+ features: ["auth", "api"], // removed "ui"
50
+ },
51
+ users: [
52
+ { name: "John", roles: ["admin"] }, // removed editor role + removed Jane user
53
+ ],
54
+ });
55
+ console.log("✅ Updated structure (cleanup nested arrays)");
56
+ };
57
+
58
+ const handleClear = () => {
59
+ form?.setFieldValues({
60
+ config: {
61
+ appName: "",
62
+ version: "",
63
+ features: [],
64
+ },
65
+ users: [],
66
+ });
67
+ console.log("✅ Cleared all fields");
68
+ };
69
+
70
+ return (
71
+ <Box sx={{ p: 2 }}>
72
+ <Typography.Title level={4}>
73
+ Test Case 10: setFieldValues - Complex Nested
74
+ </Typography.Title>
75
+ <Typography.Paragraph>
76
+ Test setFieldValues với structure phức tạp: nested objects + arrays +
77
+ FormList
78
+ </Typography.Paragraph>
79
+
80
+ <Form formName="testCase10">
81
+ {/* Config section */}
82
+ <Box sx={{ border: "2px solid #1890ff", p: 2, mb: 2, borderRadius: 1 }}>
83
+ <Typography.Text strong style={{ fontSize: 16 }}>
84
+ Config
85
+ </Typography.Text>
86
+
87
+ <Form.Item name="config.appName" label="App Name">
88
+ <Input placeholder="Enter app name" />
89
+ </Form.Item>
90
+
91
+ <Form.Item name="config.version" label="Version">
92
+ <Input placeholder="Enter version" />
93
+ </Form.Item>
94
+
95
+ <Box sx={{ mt: 1 }}>
96
+ <Typography.Text>Features (Array):</Typography.Text>
97
+ <Form.Item name="config.features.0">
98
+ <Input placeholder="Feature 1" />
99
+ </Form.Item>
100
+ <Form.Item name="config.features.1">
101
+ <Input placeholder="Feature 2" />
102
+ </Form.Item>
103
+ <Form.Item name="config.features.2">
104
+ <Input placeholder="Feature 3" />
105
+ </Form.Item>
106
+ </Box>
107
+ </Box>
108
+
109
+ {/* Users FormList with nested roles array */}
110
+ <Box sx={{ border: "2px solid #52c41a", p: 2, mb: 2, borderRadius: 1 }}>
111
+ <Typography.Text strong style={{ fontSize: 16 }}>
112
+ Users (FormList)
113
+ </Typography.Text>
114
+
115
+ <FormList name="users">
116
+ {(fields, { add, remove }) => (
117
+ <Box>
118
+ {fields.map((field, index) => (
119
+ <Box
120
+ key={field.key}
121
+ sx={{
122
+ border: "1px dashed #52c41a",
123
+ p: 1,
124
+ mb: 1,
125
+ borderRadius: 1,
126
+ }}
127
+ >
128
+ <Typography.Text>User {index + 1}</Typography.Text>
129
+
130
+ <Form.Item name={`users.${index}.name`} label="Name">
131
+ <Input placeholder="User name" />
132
+ </Form.Item>
133
+
134
+ <Box
135
+ sx={{ ml: 2, bgcolor: "#f6ffed", p: 1, borderRadius: 1 }}
136
+ >
137
+ <Typography.Text>Roles (Nested Array):</Typography.Text>
138
+ <Form.Item name={`users.${index}.roles.0`}>
139
+ <Input placeholder="Role 1" />
140
+ </Form.Item>
141
+ <Form.Item name={`users.${index}.roles.1`}>
142
+ <Input placeholder="Role 2" />
143
+ </Form.Item>
144
+ </Box>
145
+
146
+ <Button
147
+ danger
148
+ size="small"
149
+ onClick={() => remove({ key: field.key })}
150
+ >
151
+ Remove User
152
+ </Button>
153
+ </Box>
154
+ ))}
155
+ <Button type="dashed" onClick={() => add()}>
156
+ Add User
157
+ </Button>
158
+ </Box>
159
+ )}
160
+ </FormList>
161
+ </Box>
162
+
163
+ <Box sx={{ mt: 2, display: "flex", gap: 2, flexDirection: "column" }}>
164
+ <Button type="primary" onClick={handleTestSetFieldValues}>
165
+ 1. Set Complex Structure
166
+ </Button>
167
+ <Button onClick={handleTestUpdateStructure}>
168
+ 2. Update with Cleanup
169
+ </Button>
170
+ <Button danger onClick={handleClear}>
171
+ 3. Clear All
172
+ </Button>
173
+ </Box>
174
+
175
+ <Box
176
+ sx={{
177
+ mt: 2,
178
+ p: 2,
179
+ bgcolor: "#e6fffb",
180
+ border: "1px solid #13c2c2",
181
+ borderRadius: 1,
182
+ }}
183
+ >
184
+ <Typography.Text strong>Expected Behavior:</Typography.Text>
185
+ <Typography.Paragraph>
186
+ <strong>setFieldValues logic:</strong>
187
+ <br />- Traverse object, dừng tại mỗi array
188
+ <br />- Primitives: trigger onChange
189
+ <br />- Arrays: gọi handleDeepTriggerSet
190
+ <br />
191
+ <br />
192
+ <strong>Update with Cleanup:</strong>
193
+ <br />- config.features.2 → undefined (removed)
194
+ <br />- users.1 → undefined (Jane removed)
195
+ <br />- users.0.roles.1 → undefined (editor removed)
196
+ </Typography.Paragraph>
197
+ </Box>
198
+ </Form>
199
+ </Box>
200
+ );
201
+ }
202
+
203
+ export default TestCase10_SetFieldValues_ComplexNested;
@@ -0,0 +1,72 @@
1
+ import { Box } from "@mui/material";
2
+ import { Button, Input, Typography } from "antd";
3
+ import Form from "../../providers/Form";
4
+
5
+ type Props = {};
6
+
7
+ /**
8
+ * Test Case 1: Plain Object → Primitive Listeners
9
+ *
10
+ * Cấu trúc:
11
+ * - user.name (string)
12
+ * - user.age (number)
13
+ *
14
+ * Test: setFieldValue("user", {name: "John", age: 30}, {deepTrigger: true})
15
+ * Kỳ vọng:
16
+ * - Trigger "user" listener (nếu có)
17
+ * - Trigger "user.name" listener
18
+ * - Trigger "user.age" listener
19
+ */
20
+ function TestCase1_PlainObjectToPrimitives({}: Props) {
21
+ const [form] = Form.useForm("testCase1");
22
+
23
+ const handleTestDeepTrigger = () => {
24
+ form?.setFieldValue(
25
+ "user",
26
+ { name: "John Doe", age: 30 },
27
+ { deepTrigger: true },
28
+ );
29
+ console.log("✅ Called setFieldValue with deepTrigger=true");
30
+ };
31
+
32
+ const handleTestNormalSet = () => {
33
+ form?.setFieldValue("user", { name: "Jane Doe", age: 25 });
34
+ console.log("✅ Called setFieldValue without deepTrigger");
35
+ };
36
+
37
+ return (
38
+ <Box sx={{ p: 2 }}>
39
+ <Typography.Title level={4}>
40
+ Test Case 1: Plain Object → Primitive Listeners
41
+ </Typography.Title>
42
+ <Typography.Paragraph>
43
+ Test setFieldValue("user", {"{name, age}"}) với deepTrigger=true
44
+ </Typography.Paragraph>
45
+
46
+ <Form formName="testCase1">
47
+ {/* User object listener - should be triggered */}
48
+ <Form.Item name="user">
49
+ <Input placeholder="User Object (JSON)" disabled />
50
+ </Form.Item>
51
+
52
+ {/* Nested primitive listeners */}
53
+ <Form.Item name="user.name" label="Name">
54
+ <Input placeholder="Enter name" />
55
+ </Form.Item>
56
+
57
+ <Form.Item name="user.age" label="Age">
58
+ <Input type="number" placeholder="Enter age" />
59
+ </Form.Item>
60
+
61
+ <Box sx={{ mt: 2, display: "flex", gap: 2 }}>
62
+ <Button type="primary" onClick={handleTestDeepTrigger}>
63
+ Test Deep Trigger
64
+ </Button>
65
+ <Button onClick={handleTestNormalSet}>Test Normal Set</Button>
66
+ </Box>
67
+ </Form>
68
+ </Box>
69
+ );
70
+ }
71
+
72
+ export default TestCase1_PlainObjectToPrimitives;