@pautena/react-design-system 0.1.4 → 0.3.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/dist/cjs/index.js +13 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/value-displays/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/value-datetime/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/value-datetime/value-datetime.d.ts +18 -0
- package/dist/cjs/types/generators/generators.mock.d.ts +9 -5
- package/dist/cjs/types/generators/generators.model.d.ts +25 -1
- package/dist/cjs/types/generators/model-router/index.d.ts +1 -0
- package/dist/cjs/types/generators/model-router/screens/add-screen.d.ts +1 -1
- package/dist/cjs/types/generators/model-router/screens/list-screen.d.ts +1 -1
- package/dist/cjs/types/generators/model-router/screens/screens.types.d.ts +20 -0
- package/dist/cjs/types/generators/model-router/screens/update-screen.d.ts +1 -1
- package/dist/cjs/types/index.d.ts +1 -0
- package/dist/esm/index.js +13 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/value-displays/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/value-datetime/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/value-datetime/value-datetime.d.ts +18 -0
- package/dist/esm/types/generators/generators.mock.d.ts +9 -5
- package/dist/esm/types/generators/generators.model.d.ts +25 -1
- package/dist/esm/types/generators/model-router/index.d.ts +1 -0
- package/dist/esm/types/generators/model-router/screens/add-screen.d.ts +1 -1
- package/dist/esm/types/generators/model-router/screens/list-screen.d.ts +1 -1
- package/dist/esm/types/generators/model-router/screens/screens.types.d.ts +20 -0
- package/dist/esm/types/generators/model-router/screens/update-screen.d.ts +1 -1
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/index.d.ts +98 -4
- package/package.json +6 -2
- package/src/components/header/header.test.tsx +2 -12
- package/src/components/tab/tab-card/tab-card.tsx +2 -2
- package/src/components/table-list/table-list.test.tsx +8 -1
- package/src/components/table-list/table-list.tsx +3 -3
- package/src/components/value-displays/index.ts +1 -0
- package/src/components/value-displays/value-datetime/index.ts +1 -0
- package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +21 -0
- package/src/components/value-displays/value-datetime/value-datetime.test.tsx +23 -0
- package/src/components/value-displays/value-datetime/value-datetime.tsx +40 -0
- package/src/components/value-displays/value-text/{value-test.test.tsx → value-text.test.tsx} +0 -0
- package/src/generators/generators.mock.ts +56 -17
- package/src/generators/generators.model.ts +39 -1
- package/src/generators/model-form/model-form.stories.tsx +2 -2
- package/src/generators/model-form/model-form.test.tsx +39 -22
- package/src/generators/model-form/model-form.tsx +220 -33
- package/src/generators/model-router/index.ts +1 -0
- package/src/generators/model-router/model-router.test.tsx +264 -57
- package/src/generators/model-router/model-router.tsx +4 -3
- package/src/generators/model-router/screens/add-screen.tsx +2 -1
- package/src/generators/model-router/screens/list-screen.tsx +41 -26
- package/src/generators/model-router/screens/screens.types.ts +25 -0
- package/src/generators/model-router/screens/update-screen.tsx +2 -1
- package/src/generators/model-router/stories/list-screen.stories.tsx +51 -0
- package/src/generators/model-router/stories/model-router.stories.tsx +66 -3
- package/src/generators/object-details/object-details.tsx +5 -4
- package/src/index.ts +1 -0
- package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.stories.tsx +1 -1
- package/src/layouts/app-bar-with-drawer-layout/app-bar-with-drawer-layout.tsx +1 -1
- package/src/storybook.tsx +10 -0
- package/src/tests/actions.ts +43 -0
- package/src/tests/assertions.ts +70 -1
- package/src/tests/index.ts +1 -0
- package/src/tests/testing-library.tsx +5 -1
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { Model, ModelField } from "./generators.model";
|
|
1
|
+
import { BasicModelInstance, Model, ModelField } from "./generators.model";
|
|
2
2
|
import { faker } from "@faker-js/faker";
|
|
3
|
-
import {
|
|
3
|
+
import { newArrayWithSize } from "../utils";
|
|
4
4
|
|
|
5
|
+
export const BirthDateFormat = "dd/MM/yyyy";
|
|
6
|
+
export const ReturnTimeFormat = "HH:mm";
|
|
7
|
+
export const TradeDateFormat = "dd/MM/yyyy HH:mm";
|
|
5
8
|
export const mockModel: Model = {
|
|
6
9
|
fields: [
|
|
7
10
|
{
|
|
@@ -46,7 +49,8 @@ export const mockModel: Model = {
|
|
|
46
49
|
},
|
|
47
50
|
{
|
|
48
51
|
id: "gender",
|
|
49
|
-
type: "
|
|
52
|
+
type: "enum",
|
|
53
|
+
value: faker.definitions.name?.gender || [],
|
|
50
54
|
description: "User gender",
|
|
51
55
|
name: "Gender",
|
|
52
56
|
xs: 12,
|
|
@@ -65,23 +69,27 @@ export const mockModel: Model = {
|
|
|
65
69
|
},
|
|
66
70
|
{
|
|
67
71
|
id: "birthDate",
|
|
68
|
-
type: "
|
|
72
|
+
type: "date",
|
|
73
|
+
format: BirthDateFormat,
|
|
74
|
+
default: new Date(2014, 8, 18),
|
|
69
75
|
description: "When he was born",
|
|
70
76
|
name: "Birth Date",
|
|
71
77
|
xs: 12,
|
|
72
78
|
sm: 6,
|
|
73
79
|
md: 6,
|
|
74
|
-
listable:
|
|
80
|
+
listable: false,
|
|
75
81
|
},
|
|
76
82
|
{
|
|
77
83
|
id: "car",
|
|
78
84
|
type: "group",
|
|
79
85
|
description: "Information about the user car",
|
|
80
86
|
name: "User car",
|
|
87
|
+
xs: 12,
|
|
81
88
|
value: [
|
|
82
89
|
{
|
|
83
90
|
id: "model",
|
|
84
|
-
type: "
|
|
91
|
+
type: "enum",
|
|
92
|
+
value: faker.definitions.vehicle?.model || [],
|
|
85
93
|
description: "Lorem ipsum",
|
|
86
94
|
name: "Model",
|
|
87
95
|
xs: 12,
|
|
@@ -89,7 +97,8 @@ export const mockModel: Model = {
|
|
|
89
97
|
},
|
|
90
98
|
{
|
|
91
99
|
id: "manufacturer",
|
|
92
|
-
type: "
|
|
100
|
+
type: "enum",
|
|
101
|
+
value: faker.definitions.vehicle?.manufacturer || [],
|
|
93
102
|
description: "Lorem ipsum",
|
|
94
103
|
name: "Manufacturer",
|
|
95
104
|
xs: 12,
|
|
@@ -105,7 +114,8 @@ export const mockModel: Model = {
|
|
|
105
114
|
},
|
|
106
115
|
{
|
|
107
116
|
id: "type",
|
|
108
|
-
type: "
|
|
117
|
+
type: "multienum",
|
|
118
|
+
value: faker.definitions.vehicle?.type || [],
|
|
109
119
|
description: "Lorem ipsum",
|
|
110
120
|
name: "Type",
|
|
111
121
|
xs: 12,
|
|
@@ -127,6 +137,16 @@ export const mockModel: Model = {
|
|
|
127
137
|
xs: 12,
|
|
128
138
|
sm: 4,
|
|
129
139
|
},
|
|
140
|
+
{
|
|
141
|
+
id: "returnTime",
|
|
142
|
+
type: "time",
|
|
143
|
+
format: ReturnTimeFormat,
|
|
144
|
+
default: new Date(1970, 0, 1, 9, 0),
|
|
145
|
+
description: "Lorem ipsum",
|
|
146
|
+
name: "Return time",
|
|
147
|
+
xs: 12,
|
|
148
|
+
sm: 4,
|
|
149
|
+
},
|
|
130
150
|
],
|
|
131
151
|
},
|
|
132
152
|
{
|
|
@@ -156,11 +176,13 @@ export const mockModel: Model = {
|
|
|
156
176
|
},
|
|
157
177
|
{
|
|
158
178
|
id: "tradeDate",
|
|
159
|
-
type: "
|
|
179
|
+
type: "datetime",
|
|
180
|
+
format: TradeDateFormat,
|
|
181
|
+
default: new Date(2022, 8, 12, 9, 0),
|
|
160
182
|
description: "Lorem ipsum",
|
|
161
183
|
name: "Trade date",
|
|
162
184
|
xs: 12,
|
|
163
|
-
sm:
|
|
185
|
+
sm: 5,
|
|
164
186
|
},
|
|
165
187
|
],
|
|
166
188
|
};
|
|
@@ -172,19 +194,20 @@ export interface MockInstance {
|
|
|
172
194
|
lastName: string;
|
|
173
195
|
gender: string;
|
|
174
196
|
age: number;
|
|
175
|
-
birthDate:
|
|
197
|
+
birthDate: Date;
|
|
176
198
|
car: {
|
|
177
199
|
model: string;
|
|
178
200
|
manufacturer: string;
|
|
179
201
|
color: string;
|
|
180
|
-
type: string;
|
|
202
|
+
type: string[];
|
|
181
203
|
vin: string;
|
|
182
204
|
vrm: string;
|
|
205
|
+
returnTime: Date;
|
|
183
206
|
};
|
|
184
207
|
quantity: number;
|
|
185
208
|
available: boolean;
|
|
186
209
|
currency: string;
|
|
187
|
-
tradeDate:
|
|
210
|
+
tradeDate: Date;
|
|
188
211
|
}
|
|
189
212
|
|
|
190
213
|
const mockFieldValue = {
|
|
@@ -194,20 +217,36 @@ const mockFieldValue = {
|
|
|
194
217
|
lastName: faker.name.lastName,
|
|
195
218
|
gender: faker.name.gender,
|
|
196
219
|
age: () => faker.datatype.number({ min: 20, max: 60 }),
|
|
197
|
-
birthDate: () =>
|
|
220
|
+
birthDate: () => {
|
|
221
|
+
const date = faker.date.recent();
|
|
222
|
+
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
223
|
+
},
|
|
224
|
+
returnTime: () => {
|
|
225
|
+
const date = faker.date.recent();
|
|
226
|
+
const time = new Date();
|
|
227
|
+
time.setHours(date.getHours());
|
|
228
|
+
time.setMinutes(date.getMinutes());
|
|
229
|
+
time.setSeconds(0);
|
|
230
|
+
time.setMilliseconds(0);
|
|
231
|
+
return time;
|
|
232
|
+
},
|
|
198
233
|
model: faker.vehicle.model,
|
|
199
234
|
manufacturer: faker.vehicle.manufacturer,
|
|
200
235
|
color: faker.vehicle.color,
|
|
201
|
-
type:
|
|
236
|
+
type: () => {
|
|
237
|
+
const array = newArrayWithSize(faker.datatype.number({ min: 2, max: 5 }), 0);
|
|
238
|
+
const result = array.map(() => faker.vehicle.type());
|
|
239
|
+
return [...new Set(result)];
|
|
240
|
+
},
|
|
202
241
|
vin: faker.vehicle.vin,
|
|
203
242
|
vrm: faker.vehicle.vrm,
|
|
204
243
|
quantity: () => faker.datatype.number({ min: 1, max: 9 }),
|
|
205
244
|
available: faker.datatype.boolean,
|
|
206
245
|
currency: () => "MXN",
|
|
207
|
-
tradeDate:
|
|
246
|
+
tradeDate: faker.date.recent,
|
|
208
247
|
};
|
|
209
248
|
|
|
210
|
-
export const createModelInstance = <T extends
|
|
249
|
+
export const createModelInstance = <T extends BasicModelInstance>(model: Model, seed = 100): T => {
|
|
211
250
|
faker.seed(seed);
|
|
212
251
|
const obj = {};
|
|
213
252
|
|
|
@@ -25,7 +25,43 @@ type BooleanField = {
|
|
|
25
25
|
type: "boolean";
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
type
|
|
28
|
+
type EnumField = {
|
|
29
|
+
type: "enum";
|
|
30
|
+
value: string[];
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
type MultiEnumField = {
|
|
34
|
+
type: "multienum";
|
|
35
|
+
value: string[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
type DateField = {
|
|
39
|
+
type: "date";
|
|
40
|
+
format: string;
|
|
41
|
+
default: any;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
type TimeField = {
|
|
45
|
+
type: "time";
|
|
46
|
+
format: string;
|
|
47
|
+
default: any;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
type DatetimeField = {
|
|
51
|
+
type: "datetime";
|
|
52
|
+
format: string;
|
|
53
|
+
default: any;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
type SingleFields =
|
|
57
|
+
| StringField
|
|
58
|
+
| NumberField
|
|
59
|
+
| BooleanField
|
|
60
|
+
| EnumField
|
|
61
|
+
| MultiEnumField
|
|
62
|
+
| DateField
|
|
63
|
+
| TimeField
|
|
64
|
+
| DatetimeField;
|
|
29
65
|
|
|
30
66
|
export type GroupField = {
|
|
31
67
|
type: "group";
|
|
@@ -44,3 +80,5 @@ export interface BasicModelInstance {
|
|
|
44
80
|
id: string;
|
|
45
81
|
[key: string]: any;
|
|
46
82
|
}
|
|
83
|
+
|
|
84
|
+
export type ModelFieldTypes = "string" | "number" | "boolean" | "enum" | "multienum";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ComponentMeta } from "@storybook/react";
|
|
2
2
|
import { ModelForm } from "./model-form";
|
|
3
|
-
import { createTemplate, withPadding } from "../../storybook";
|
|
3
|
+
import { createTemplate, withPadding, withLocalizationProvider } from "../../storybook";
|
|
4
4
|
import { createModelInstance, mockModel } from "../generators.mock";
|
|
5
5
|
|
|
6
6
|
const initialValues = createModelInstance(mockModel);
|
|
@@ -8,7 +8,7 @@ const initialValues = createModelInstance(mockModel);
|
|
|
8
8
|
export default {
|
|
9
9
|
title: "Generators/ModelForm",
|
|
10
10
|
component: ModelForm,
|
|
11
|
-
decorators: [withPadding(2)],
|
|
11
|
+
decorators: [withPadding(2), withLocalizationProvider],
|
|
12
12
|
parameters: {
|
|
13
13
|
layout: "fullscreen",
|
|
14
14
|
},
|
|
@@ -5,8 +5,19 @@ import {
|
|
|
5
5
|
expectModelFieldInputValue,
|
|
6
6
|
render,
|
|
7
7
|
screen,
|
|
8
|
+
selectOption,
|
|
9
|
+
selectOptions,
|
|
10
|
+
pickDatetime,
|
|
11
|
+
expectToHaveBeenCalledOnceWithMockInstance,
|
|
8
12
|
} from "../../tests";
|
|
9
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
BirthDateFormat,
|
|
15
|
+
createModelInstance,
|
|
16
|
+
MockInstance,
|
|
17
|
+
mockModel,
|
|
18
|
+
ReturnTimeFormat,
|
|
19
|
+
TradeDateFormat,
|
|
20
|
+
} from "../generators.mock";
|
|
10
21
|
import userEvent from "@testing-library/user-event";
|
|
11
22
|
|
|
12
23
|
describe("ModelForm", () => {
|
|
@@ -47,54 +58,60 @@ describe("ModelForm", () => {
|
|
|
47
58
|
|
|
48
59
|
it("would call onSubmit if I fullfill all inputs and press the submit button", async () => {
|
|
49
60
|
const { onSubmit } = renderComponent();
|
|
61
|
+
const birthDate = new Date(2047, 11, 26);
|
|
62
|
+
const returnTime = new Date(1970, 0, 1, 10, 12);
|
|
63
|
+
const tradeDate = new Date(2047, 11, 26, 12, 50);
|
|
50
64
|
|
|
51
65
|
await userEvent.type(screen.getByRole("textbox", { name: "Id" }), "Id-1");
|
|
52
66
|
await userEvent.type(screen.getByRole("textbox", { name: /first name/i }), "Karianne");
|
|
53
67
|
await userEvent.type(screen.getByRole("textbox", { name: /middle name/i }), "Noah");
|
|
54
68
|
await userEvent.type(screen.getByRole("textbox", { name: /last name/i }), "Gorczany");
|
|
55
|
-
await
|
|
69
|
+
await selectOption(screen.getByRole("button", { name: /gender/i }), "Cis Man");
|
|
56
70
|
await userEvent.type(screen.getByRole("spinbutton", { name: /age/i }), "37");
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
);
|
|
61
|
-
await userEvent.type(screen.getByRole("textbox", { name: /model/i }), "Spyder");
|
|
62
|
-
await userEvent.type(screen.getByRole("textbox", { name: /manufacturer/i }), "Bugatti");
|
|
71
|
+
pickDatetime(screen.getByRole("textbox", { name: /birth date/i }), birthDate, BirthDateFormat);
|
|
72
|
+
await selectOption(screen.getByRole("button", { name: /model/i }), "Spyder");
|
|
73
|
+
await selectOption(screen.getByRole("button", { name: /manufacturer/i }), "Bugatti");
|
|
63
74
|
await userEvent.type(screen.getByRole("textbox", { name: /color/i }), "red");
|
|
64
|
-
await
|
|
75
|
+
await selectOptions(screen.getByRole("button", { name: /type/i }), [
|
|
76
|
+
"Coupe",
|
|
77
|
+
"Hatchback",
|
|
78
|
+
"Minivan",
|
|
79
|
+
]);
|
|
65
80
|
await userEvent.type(screen.getByRole("textbox", { name: /vin/i }), "46N6UE4VJ2XL28828");
|
|
66
81
|
await userEvent.type(screen.getByRole("textbox", { name: /vrm/i }), "NE51AFH");
|
|
82
|
+
pickDatetime(
|
|
83
|
+
screen.getByRole("textbox", { name: /return time/i }),
|
|
84
|
+
returnTime,
|
|
85
|
+
ReturnTimeFormat,
|
|
86
|
+
);
|
|
67
87
|
await userEvent.type(screen.getByRole("spinbutton", { name: /q/i }), "9");
|
|
68
|
-
await userEvent.
|
|
88
|
+
await userEvent.click(screen.getByRole("checkbox", { name: /available/i }));
|
|
69
89
|
await userEvent.type(screen.getByRole("textbox", { name: /currency/i }), "mxn");
|
|
70
|
-
|
|
71
|
-
screen.getByRole("textbox", { name: /trade date/i }),
|
|
72
|
-
"Thu Jul 21 2022 22:44:10",
|
|
73
|
-
);
|
|
90
|
+
pickDatetime(screen.getByRole("textbox", { name: /trade date/i }), tradeDate, TradeDateFormat);
|
|
74
91
|
|
|
75
92
|
await userEvent.click(screen.getByRole("button", { name: /save/i }));
|
|
76
93
|
|
|
77
|
-
|
|
78
|
-
expect(onSubmit).toHaveBeenCalledWith({
|
|
94
|
+
expectToHaveBeenCalledOnceWithMockInstance(onSubmit, {
|
|
79
95
|
id: "Id-1",
|
|
80
96
|
firstName: "Karianne",
|
|
81
97
|
middleName: "Noah",
|
|
82
98
|
lastName: "Gorczany",
|
|
83
99
|
gender: "Cis Man",
|
|
84
|
-
age:
|
|
85
|
-
birthDate
|
|
100
|
+
age: 37,
|
|
101
|
+
birthDate,
|
|
86
102
|
car: {
|
|
87
103
|
model: "Spyder",
|
|
88
104
|
manufacturer: "Bugatti",
|
|
89
105
|
color: "red",
|
|
90
|
-
type: "
|
|
106
|
+
type: ["Coupe", "Hatchback", "Minivan"],
|
|
91
107
|
vin: "46N6UE4VJ2XL28828",
|
|
92
108
|
vrm: "NE51AFH",
|
|
109
|
+
returnTime,
|
|
93
110
|
},
|
|
94
|
-
quantity:
|
|
95
|
-
available:
|
|
111
|
+
quantity: 9,
|
|
112
|
+
available: true,
|
|
96
113
|
currency: "mxn",
|
|
97
|
-
tradeDate
|
|
114
|
+
tradeDate,
|
|
98
115
|
});
|
|
99
116
|
});
|
|
100
117
|
});
|
|
@@ -1,8 +1,69 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
Box,
|
|
3
|
+
Button,
|
|
4
|
+
Checkbox,
|
|
5
|
+
FormControl,
|
|
6
|
+
FormControlLabel,
|
|
7
|
+
Grid,
|
|
8
|
+
InputLabel,
|
|
9
|
+
ListItemText,
|
|
10
|
+
MenuItem,
|
|
11
|
+
Paper,
|
|
12
|
+
Select,
|
|
13
|
+
SelectChangeEvent,
|
|
14
|
+
TextField,
|
|
15
|
+
Typography,
|
|
16
|
+
} from "@mui/material";
|
|
17
|
+
import { DesktopDatePicker, TimePicker, DateTimePicker } from "@mui/x-date-pickers";
|
|
18
|
+
import React, { ChangeEvent, FormEvent, ReactElement, useMemo } from "react";
|
|
3
19
|
import { useState } from "react";
|
|
4
20
|
import { useGetDefaultThemeColor } from "../../utils/theme";
|
|
5
|
-
import { Model, ModelField, BasicModelInstance } from "../generators.model";
|
|
21
|
+
import { Model, ModelField, BasicModelInstance, ModelFieldTypes } from "../generators.model";
|
|
22
|
+
|
|
23
|
+
const InitialStateZeroValue: Record<string, any> = {
|
|
24
|
+
string: undefined,
|
|
25
|
+
number: undefined,
|
|
26
|
+
boolean: false,
|
|
27
|
+
enum: "",
|
|
28
|
+
multienum: [],
|
|
29
|
+
date: "01/01/1970",
|
|
30
|
+
group: {},
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const getFieldInitialState = <T extends BasicModelInstance>(
|
|
34
|
+
field: ModelField,
|
|
35
|
+
initialValues: T | undefined,
|
|
36
|
+
) => {
|
|
37
|
+
return initialValues ? initialValues[field.id] : InitialStateZeroValue[field.type];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const getValuesInitialState = <T extends BasicModelInstance>(
|
|
41
|
+
model: Model,
|
|
42
|
+
initialValues: T | undefined,
|
|
43
|
+
): T => {
|
|
44
|
+
const obj = {} as any;
|
|
45
|
+
|
|
46
|
+
model.fields.forEach((field) => {
|
|
47
|
+
let value: any;
|
|
48
|
+
if (field.type === "group") {
|
|
49
|
+
value = {};
|
|
50
|
+
field.value.forEach((groupField) => {
|
|
51
|
+
value[groupField.id] = getFieldInitialState(
|
|
52
|
+
groupField,
|
|
53
|
+
initialValues && initialValues[field.id],
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
} else if (field.type === "date" || field.type === "time") {
|
|
57
|
+
value = (initialValues && initialValues[field.id]) || field.default;
|
|
58
|
+
} else {
|
|
59
|
+
value = getFieldInitialState(field, initialValues);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
obj[field.id] = value;
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
return obj;
|
|
66
|
+
};
|
|
6
67
|
|
|
7
68
|
export interface ModelFormProps<T extends BasicModelInstance> {
|
|
8
69
|
model: Model;
|
|
@@ -17,28 +78,63 @@ export const ModelForm = <T extends BasicModelInstance>({
|
|
|
17
78
|
onSubmit,
|
|
18
79
|
initialValues,
|
|
19
80
|
}: ModelFormProps<T>) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
e.target;
|
|
81
|
+
const valuesInitialState = useMemo(
|
|
82
|
+
() => getValuesInitialState<T>(model, initialValues),
|
|
83
|
+
[model, initialValues],
|
|
84
|
+
);
|
|
85
|
+
const [values, setValues] = useState<T>(valuesInitialState);
|
|
26
86
|
|
|
87
|
+
const setKeyValue = (name: string, key: string | undefined, value: any) => {
|
|
27
88
|
setValues((v) => {
|
|
28
89
|
const n: Record<string, object> = {};
|
|
29
90
|
if (key) {
|
|
30
91
|
n[key] = {
|
|
31
92
|
...v[key],
|
|
32
|
-
[
|
|
93
|
+
[name]: value,
|
|
33
94
|
};
|
|
34
95
|
} else {
|
|
35
|
-
n[
|
|
96
|
+
n[name] = value;
|
|
36
97
|
}
|
|
37
98
|
|
|
38
99
|
return { ...v, ...n };
|
|
39
100
|
});
|
|
40
101
|
};
|
|
41
102
|
|
|
103
|
+
const handleCheckboxChange = (e: ChangeEvent<any>, key: string | undefined) => {
|
|
104
|
+
e.preventDefault();
|
|
105
|
+
setKeyValue(e.target.name, key, e.target.checked);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const handleSelectChange = (e: SelectChangeEvent<any>, key: string | undefined) => {
|
|
109
|
+
e.preventDefault();
|
|
110
|
+
setKeyValue(e.target.name, key, e.target.value);
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
const handleMultiSelectChange = (e: SelectChangeEvent<any>, key: string | undefined) => {
|
|
114
|
+
e.preventDefault();
|
|
115
|
+
const { value } = e.target;
|
|
116
|
+
const newValue = typeof value === "string" ? value.split(",") : value;
|
|
117
|
+
setKeyValue(e.target.name, key, newValue);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const handleInputChange = (
|
|
121
|
+
e: ChangeEvent<any>,
|
|
122
|
+
key: string | undefined,
|
|
123
|
+
type: ModelFieldTypes,
|
|
124
|
+
) => {
|
|
125
|
+
e.preventDefault();
|
|
126
|
+
|
|
127
|
+
let value = e.target.value;
|
|
128
|
+
if (type === "number") {
|
|
129
|
+
value = parseInt(e.target.value);
|
|
130
|
+
}
|
|
131
|
+
setKeyValue(e.target.name, key, value);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const handleDateChange = (value: any, key: string | undefined, id: string) => {
|
|
135
|
+
setKeyValue(id, key, value);
|
|
136
|
+
};
|
|
137
|
+
|
|
42
138
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
|
43
139
|
e.preventDefault();
|
|
44
140
|
onSubmit(values);
|
|
@@ -48,28 +144,113 @@ export const ModelForm = <T extends BasicModelInstance>({
|
|
|
48
144
|
const defaultColor = useGetDefaultThemeColor({ lightWeight: 200, darkWeight: 800 });
|
|
49
145
|
|
|
50
146
|
const { id, type, name, description, xs, sm, md, lg, xl } = field;
|
|
147
|
+
|
|
148
|
+
let fieldInput: ReactElement;
|
|
149
|
+
const value = key ? values[key][id] : values[id];
|
|
51
150
|
if (type === "group") {
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
</Paper>
|
|
67
|
-
</Grid>
|
|
151
|
+
fieldInput = (
|
|
152
|
+
<Paper>
|
|
153
|
+
<Box bgcolor={defaultColor} px={2} py={1} mb={2}>
|
|
154
|
+
<Typography variant="h6" role="heading" aria-level={1}>
|
|
155
|
+
{name}
|
|
156
|
+
</Typography>
|
|
157
|
+
<Typography variant="body2" role="heading" aria-level={2}>
|
|
158
|
+
{description}
|
|
159
|
+
</Typography>
|
|
160
|
+
</Box>
|
|
161
|
+
<Grid container spacing={2} sx={{ p: 2 }}>
|
|
162
|
+
{field.value.map((f) => renderField(f, id))}
|
|
163
|
+
</Grid>
|
|
164
|
+
</Paper>
|
|
68
165
|
);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
166
|
+
} else if (type === "boolean") {
|
|
167
|
+
fieldInput = (
|
|
168
|
+
<Box sx={{ height: 1, display: "flex", alignItems: "center" }}>
|
|
169
|
+
<FormControlLabel
|
|
170
|
+
control={
|
|
171
|
+
<Checkbox name={id} onChange={(e) => handleCheckboxChange(e, key)} checked={value} />
|
|
172
|
+
}
|
|
173
|
+
label={name}
|
|
174
|
+
/>
|
|
175
|
+
</Box>
|
|
176
|
+
);
|
|
177
|
+
} else if (type === "enum") {
|
|
178
|
+
fieldInput = (
|
|
179
|
+
<FormControl fullWidth>
|
|
180
|
+
<InputLabel id={`${id}-select-label`}>{name}</InputLabel>
|
|
181
|
+
<Select
|
|
182
|
+
labelId={`${id}-select-label`}
|
|
183
|
+
id={`${id}-select`}
|
|
184
|
+
value={value}
|
|
185
|
+
label={name}
|
|
186
|
+
name={id}
|
|
187
|
+
onChange={(e) => handleSelectChange(e, key)}
|
|
188
|
+
required
|
|
189
|
+
>
|
|
190
|
+
{field.value.map((fieldValue) => (
|
|
191
|
+
<MenuItem key={fieldValue} value={fieldValue}>
|
|
192
|
+
{fieldValue}
|
|
193
|
+
</MenuItem>
|
|
194
|
+
))}
|
|
195
|
+
</Select>
|
|
196
|
+
</FormControl>
|
|
197
|
+
);
|
|
198
|
+
} else if (type === "multienum") {
|
|
199
|
+
fieldInput = (
|
|
200
|
+
<FormControl fullWidth>
|
|
201
|
+
<InputLabel id={`${id}-select-label`}>{name}</InputLabel>
|
|
202
|
+
<Select
|
|
203
|
+
labelId={`${id}-select-label`}
|
|
204
|
+
id={`${id}-select`}
|
|
205
|
+
value={value || []}
|
|
206
|
+
renderValue={(selected) => selected.join(", ")}
|
|
207
|
+
label={name}
|
|
208
|
+
name={id}
|
|
209
|
+
onChange={(e) => handleMultiSelectChange(e, key)}
|
|
210
|
+
required
|
|
211
|
+
multiple
|
|
212
|
+
>
|
|
213
|
+
{field.value.map((fieldValue) => (
|
|
214
|
+
<MenuItem key={fieldValue} value={fieldValue}>
|
|
215
|
+
<Checkbox checked={(value || []).includes(fieldValue)} />
|
|
216
|
+
<ListItemText primary={fieldValue} />
|
|
217
|
+
</MenuItem>
|
|
218
|
+
))}
|
|
219
|
+
</Select>
|
|
220
|
+
</FormControl>
|
|
221
|
+
);
|
|
222
|
+
} else if (type === "date") {
|
|
223
|
+
fieldInput = (
|
|
224
|
+
<DesktopDatePicker
|
|
225
|
+
label={name}
|
|
226
|
+
inputFormat={field.format}
|
|
227
|
+
value={value}
|
|
228
|
+
onChange={(value) => handleDateChange(value, key, id)}
|
|
229
|
+
renderInput={(params: any) => <TextField {...params} />}
|
|
230
|
+
/>
|
|
231
|
+
);
|
|
232
|
+
} else if (type === "time") {
|
|
233
|
+
fieldInput = (
|
|
234
|
+
<TimePicker
|
|
235
|
+
label={name}
|
|
236
|
+
inputFormat={field.format}
|
|
237
|
+
value={value}
|
|
238
|
+
onChange={(value) => handleDateChange(value, key, id)}
|
|
239
|
+
renderInput={(params: any) => <TextField {...params} />}
|
|
240
|
+
/>
|
|
241
|
+
);
|
|
242
|
+
} else if (type === "datetime") {
|
|
243
|
+
fieldInput = (
|
|
244
|
+
<DateTimePicker
|
|
245
|
+
label={name}
|
|
246
|
+
inputFormat={field.format}
|
|
247
|
+
value={value}
|
|
248
|
+
onChange={(value) => handleDateChange(value, key, id)}
|
|
249
|
+
renderInput={(params: any) => <TextField {...params} />}
|
|
250
|
+
/>
|
|
251
|
+
);
|
|
252
|
+
} else {
|
|
253
|
+
fieldInput = (
|
|
73
254
|
<TextField
|
|
74
255
|
required
|
|
75
256
|
type={type}
|
|
@@ -77,9 +258,15 @@ export const ModelForm = <T extends BasicModelInstance>({
|
|
|
77
258
|
name={id}
|
|
78
259
|
variant="outlined"
|
|
79
260
|
fullWidth
|
|
80
|
-
value={
|
|
81
|
-
onChange={(e) => handleInputChange(e, key)}
|
|
261
|
+
value={value}
|
|
262
|
+
onChange={(e) => handleInputChange(e, key, type)}
|
|
82
263
|
/>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return (
|
|
268
|
+
<Grid item key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl}>
|
|
269
|
+
{fieldInput}
|
|
83
270
|
</Grid>
|
|
84
271
|
);
|
|
85
272
|
};
|