@pautena/react-design-system 0.6.2 → 0.7.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 +4 -4
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/value-displays/group-value-card/group-value-card.d.ts +4 -13
- package/dist/cjs/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +1 -1
- package/dist/cjs/types/components/value-displays/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/value-base/value-displays.types.d.ts +5 -0
- package/dist/cjs/types/components/value-displays/value-boolean/value-boolean.d.ts +1 -1
- package/dist/cjs/types/components/value-displays/value-content/value-content.d.ts +6 -1
- package/dist/cjs/types/components/value-displays/value-datetime/value-datetime.d.ts +1 -1
- package/dist/cjs/types/components/value-displays/value-image/value-image.d.ts +1 -1
- package/dist/cjs/types/components/value-displays/value-item/index.d.ts +1 -0
- package/dist/cjs/types/components/value-displays/value-item/value-item.d.ts +13 -0
- package/dist/cjs/types/components/value-displays/value-rating/value-rating.d.ts +1 -1
- package/dist/cjs/types/components/value-displays/value-text/value-text.d.ts +1 -1
- package/dist/cjs/types/generators/generators.mock.d.ts +2 -0
- package/dist/cjs/types/generators/generators.model.d.ts +17 -4
- package/dist/cjs/types/generators/object-details/object-details.d.ts +2 -1
- package/dist/cjs/types/utils/breakpoints.d.ts +13 -0
- package/dist/esm/index.js +4 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/value-displays/group-value-card/group-value-card.d.ts +4 -13
- package/dist/esm/types/components/value-displays/group-value-card/group-value-card.mock.d.ts +1 -1
- package/dist/esm/types/components/value-displays/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/value-base/value-displays.types.d.ts +5 -0
- package/dist/esm/types/components/value-displays/value-boolean/value-boolean.d.ts +1 -1
- package/dist/esm/types/components/value-displays/value-content/value-content.d.ts +6 -1
- package/dist/esm/types/components/value-displays/value-datetime/value-datetime.d.ts +1 -1
- package/dist/esm/types/components/value-displays/value-image/value-image.d.ts +1 -1
- package/dist/esm/types/components/value-displays/value-item/index.d.ts +1 -0
- package/dist/esm/types/components/value-displays/value-item/value-item.d.ts +13 -0
- package/dist/esm/types/components/value-displays/value-rating/value-rating.d.ts +1 -1
- package/dist/esm/types/components/value-displays/value-text/value-text.d.ts +1 -1
- package/dist/esm/types/generators/generators.mock.d.ts +2 -0
- package/dist/esm/types/generators/generators.model.d.ts +17 -4
- package/dist/esm/types/generators/object-details/object-details.d.ts +2 -1
- package/dist/esm/types/utils/breakpoints.d.ts +13 -0
- package/dist/index.d.ts +42 -19
- package/package.json +1 -1
- package/src/components/alerts/expandable-alert/expandable-alert.stories.tsx +1 -1
- package/src/components/containers/center-container/center-container.stories.tsx +1 -1
- package/src/components/data-display/markdown/markdown.stories.tsx +0 -1
- package/src/components/dialogs/bootstrap-dialog/bootstrap-dialog.stories.tsx +1 -1
- package/src/components/drawers/drawer-item/drawer-item.stories.tsx +6 -11
- package/src/components/inputs/search-input/search-input.stories.tsx +0 -1
- package/src/components/navigation/tab/tab-card/tab-card.stories.tsx +0 -1
- package/src/components/value-displays/group-value-card/group-value-card.mock.tsx +28 -26
- package/src/components/value-displays/group-value-card/group-value-card.stories.tsx +14 -5
- package/src/components/value-displays/group-value-card/group-value-card.tsx +28 -40
- package/src/components/value-displays/index.ts +1 -0
- package/src/components/value-displays/value-base/value-displays.types.ts +6 -0
- package/src/components/value-displays/value-boolean/value-boolean.stories.tsx +8 -0
- package/src/components/value-displays/value-boolean/value-boolean.tsx +3 -2
- package/src/components/value-displays/value-content/value-content.stories.tsx +7 -2
- package/src/components/value-displays/value-content/value-content.tsx +16 -3
- package/src/components/value-displays/value-datetime/value-datetime.stories.tsx +9 -0
- package/src/components/value-displays/value-datetime/value-datetime.tsx +3 -2
- package/src/components/value-displays/value-displays.stories.mdx +1 -0
- package/src/components/value-displays/value-image/value-image.stories.tsx +8 -0
- package/src/components/value-displays/value-image/value-image.tsx +4 -2
- package/src/components/value-displays/value-item/index.ts +1 -0
- package/src/components/value-displays/value-item/value-item.stories.tsx +45 -0
- package/src/components/value-displays/value-item/value-item.test.tsx +20 -0
- package/src/components/value-displays/value-item/value-item.tsx +54 -0
- package/src/components/value-displays/value-rating/value-rating.stories.tsx +8 -0
- package/src/components/value-displays/value-rating/value-rating.tsx +4 -2
- package/src/components/value-displays/value-text/value-text.stories.tsx +8 -0
- package/src/components/value-displays/value-text/value-text.tsx +3 -2
- package/src/generators/generators.mock.ts +64 -0
- package/src/generators/generators.model.test.ts +7 -0
- package/src/generators/generators.model.ts +35 -13
- package/src/generators/model-form/model-form.test.tsx +5 -0
- package/src/generators/model-form/model-form.tsx +21 -3
- package/src/generators/model-router/model-router.test.tsx +10 -1
- package/src/generators/model-router/stories/templates.tsx +0 -1
- package/src/generators/object-details/object-details.stories.tsx +13 -3
- package/src/generators/object-details/object-details.tsx +96 -15
- package/src/providers/notification-center/notification-center.stories.tsx +1 -1
- package/src/tests/assertions.ts +10 -1
- package/src/utils/breakpoints.test.ts +42 -0
- package/src/utils/breakpoints.ts +62 -0
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
export type ModelFieldTypes =
|
|
6
6
|
| "string"
|
|
7
7
|
| "number"
|
|
8
|
+
| "string[]"
|
|
9
|
+
| "number[]"
|
|
8
10
|
| "boolean"
|
|
9
11
|
| "enum"
|
|
10
12
|
| "multienum"
|
|
@@ -31,10 +33,18 @@ type StringField = {
|
|
|
31
33
|
type: "string";
|
|
32
34
|
};
|
|
33
35
|
|
|
36
|
+
type StringArrayField = {
|
|
37
|
+
type: "string[]";
|
|
38
|
+
};
|
|
39
|
+
|
|
34
40
|
type NumberField = {
|
|
35
41
|
type: "number";
|
|
36
42
|
};
|
|
37
43
|
|
|
44
|
+
type NumberArrayField = {
|
|
45
|
+
type: "number[]";
|
|
46
|
+
};
|
|
47
|
+
|
|
38
48
|
type BooleanField = {
|
|
39
49
|
type: "boolean";
|
|
40
50
|
};
|
|
@@ -70,6 +80,8 @@ type DatetimeField = {
|
|
|
70
80
|
type SingleFields =
|
|
71
81
|
| StringField
|
|
72
82
|
| NumberField
|
|
83
|
+
| StringArrayField
|
|
84
|
+
| NumberArrayField
|
|
73
85
|
| BooleanField
|
|
74
86
|
| EnumField
|
|
75
87
|
| MultiEnumField
|
|
@@ -82,7 +94,12 @@ export type GroupField = {
|
|
|
82
94
|
value: (Base & Breakpoints & SingleFields)[];
|
|
83
95
|
} & Base;
|
|
84
96
|
|
|
85
|
-
type
|
|
97
|
+
export type ArrayGroupField = {
|
|
98
|
+
type: "group[]";
|
|
99
|
+
value: (Base & Breakpoints & SingleFields)[];
|
|
100
|
+
} & Base;
|
|
101
|
+
|
|
102
|
+
type Fields = SingleFields | GroupField | ArrayGroupField;
|
|
86
103
|
export type ModelField = Base & Breakpoints & Fields;
|
|
87
104
|
|
|
88
105
|
export type Model = {
|
|
@@ -94,9 +111,10 @@ export type Model = {
|
|
|
94
111
|
* Types used to represent an instance of a model specification
|
|
95
112
|
*/
|
|
96
113
|
export type BaseFieldType = string | number | boolean | Date;
|
|
97
|
-
export type ArrayFieldType = string[];
|
|
114
|
+
export type ArrayFieldType = string[] | number[];
|
|
98
115
|
export type SingleFieldType = BaseFieldType | ArrayFieldType;
|
|
99
116
|
export type GroupInstanceType = { [key: string]: SingleFieldType };
|
|
117
|
+
export type ArrayInstanceType = { [key: string]: SingleFieldType }[];
|
|
100
118
|
export type FieldType = SingleFieldType | GroupInstanceType;
|
|
101
119
|
|
|
102
120
|
export interface BasicModelInstance {
|
|
@@ -108,17 +126,21 @@ export interface BasicModelInstance {
|
|
|
108
126
|
* UTILITIES
|
|
109
127
|
* Some functions used in several places to help to manage models
|
|
110
128
|
*/
|
|
111
|
-
const InitialStateZeroValue: Record<ModelFieldTypes | "group", FieldType | undefined> =
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
129
|
+
const InitialStateZeroValue: Record<ModelFieldTypes | "group" | "group[]", FieldType | undefined> =
|
|
130
|
+
{
|
|
131
|
+
string: "",
|
|
132
|
+
number: 0,
|
|
133
|
+
boolean: false,
|
|
134
|
+
enum: "",
|
|
135
|
+
multienum: [],
|
|
136
|
+
date: new Date(1970, 0, 1, 0, 0),
|
|
137
|
+
time: new Date(1970, 0, 1, 0, 0),
|
|
138
|
+
datetime: new Date(1970, 0, 1, 0, 0),
|
|
139
|
+
group: {},
|
|
140
|
+
"group[]": [],
|
|
141
|
+
"string[]": [],
|
|
142
|
+
"number[]": [],
|
|
143
|
+
};
|
|
122
144
|
|
|
123
145
|
const getFieldValueOrZero = (
|
|
124
146
|
field: ModelField,
|
|
@@ -86,6 +86,8 @@ describe("ModelForm", () => {
|
|
|
86
86
|
await userEvent.click(screen.getByRole("checkbox", { name: /available/i }));
|
|
87
87
|
await userEvent.type(screen.getByRole("textbox", { name: /currency/i }), "mxn");
|
|
88
88
|
pickDatetime(screen.getByRole("textbox", { name: /trade date/i }), tradeDate, TradeDateFormat);
|
|
89
|
+
await userEvent.type(screen.getByRole("textbox", { name: /codes/i }), "foo,bar");
|
|
90
|
+
await userEvent.type(screen.getByRole("textbox", { name: /identifiers/i }), "1,2,3");
|
|
89
91
|
|
|
90
92
|
await userEvent.click(screen.getByRole("button", { name: /save/i }));
|
|
91
93
|
|
|
@@ -110,6 +112,9 @@ describe("ModelForm", () => {
|
|
|
110
112
|
available: true,
|
|
111
113
|
currency: "mxn",
|
|
112
114
|
tradeDate,
|
|
115
|
+
codes: ["foo", "bar"],
|
|
116
|
+
identifiers: ["1", "2", "3"],
|
|
117
|
+
carsHistory: [],
|
|
113
118
|
});
|
|
114
119
|
});
|
|
115
120
|
});
|
|
@@ -88,9 +88,11 @@ export const ModelForm = <T extends BasicModelInstance>({
|
|
|
88
88
|
) => {
|
|
89
89
|
e.preventDefault();
|
|
90
90
|
|
|
91
|
-
let value: string | number = e.target.value;
|
|
91
|
+
let value: string | number | string[] | number[] = e.target.value;
|
|
92
92
|
if (type === "number" && typeof value === "string") {
|
|
93
|
-
value = parseInt(e.target.value);
|
|
93
|
+
value = parseInt(e.target.value, 10);
|
|
94
|
+
} else if (type.includes("[]")) {
|
|
95
|
+
value = e.target.value.split(",");
|
|
94
96
|
}
|
|
95
97
|
setKeyValue(e.target.name, key, value);
|
|
96
98
|
};
|
|
@@ -180,7 +182,7 @@ export const ModelForm = <T extends BasicModelInstance>({
|
|
|
180
182
|
>
|
|
181
183
|
{field.value.map((fieldValue) => (
|
|
182
184
|
<MenuItem key={fieldValue} value={fieldValue}>
|
|
183
|
-
<Checkbox checked={((value as
|
|
185
|
+
<Checkbox checked={((value as any[]) || []).includes(fieldValue)} />
|
|
184
186
|
<ListItemText primary={fieldValue} />
|
|
185
187
|
</MenuItem>
|
|
186
188
|
))}
|
|
@@ -214,6 +216,22 @@ export const ModelForm = <T extends BasicModelInstance>({
|
|
|
214
216
|
onChange={(value) => handleDateChange(value, key, id)}
|
|
215
217
|
/>
|
|
216
218
|
);
|
|
219
|
+
} else if (type === "group[]") {
|
|
220
|
+
return null;
|
|
221
|
+
} else if (type.includes("[]")) {
|
|
222
|
+
fieldInput = (
|
|
223
|
+
<TextField
|
|
224
|
+
required
|
|
225
|
+
type="text"
|
|
226
|
+
label={name}
|
|
227
|
+
name={id}
|
|
228
|
+
variant="outlined"
|
|
229
|
+
helperText="Use comas to separate multiple values"
|
|
230
|
+
fullWidth
|
|
231
|
+
value={(value as any[]).join(",")}
|
|
232
|
+
onChange={(e) => handleInputChange(e, key, type)}
|
|
233
|
+
/>
|
|
234
|
+
);
|
|
217
235
|
} else {
|
|
218
236
|
fieldInput = (
|
|
219
237
|
<TextField
|
|
@@ -167,6 +167,8 @@ describe("ModelRouter", () => {
|
|
|
167
167
|
const tradeDateElement = screen.getByRole<HTMLInputElement>("textbox", {
|
|
168
168
|
name: /trade date/i,
|
|
169
169
|
});
|
|
170
|
+
const codesElement = screen.getByRole("textbox", { name: /codes/i });
|
|
171
|
+
const identifiersElement = screen.getByRole("textbox", { name: /identifiers/i });
|
|
170
172
|
|
|
171
173
|
if (clear) {
|
|
172
174
|
await userEvent.clear(idElement);
|
|
@@ -181,6 +183,8 @@ describe("ModelRouter", () => {
|
|
|
181
183
|
await userEvent.clear(currencyElement);
|
|
182
184
|
await userEvent.clear(tradeDateElement);
|
|
183
185
|
await userEvent.clear(timeReturnElement);
|
|
186
|
+
await userEvent.clear(codesElement);
|
|
187
|
+
await userEvent.clear(identifiersElement);
|
|
184
188
|
await clearCheckbox(availableElement);
|
|
185
189
|
await clearMultiSelect(typeElement);
|
|
186
190
|
}
|
|
@@ -204,6 +208,8 @@ describe("ModelRouter", () => {
|
|
|
204
208
|
}
|
|
205
209
|
await userEvent.type(currencyElement, instance.currency);
|
|
206
210
|
pickDatetime(tradeDateElement, instance.tradeDate, TradeDateFormat);
|
|
211
|
+
await userEvent.type(codesElement, instance.codes.join(","));
|
|
212
|
+
await userEvent.type(identifiersElement, instance.identifiers.join(","));
|
|
207
213
|
|
|
208
214
|
submit && (await userEvent.click(screen.getByRole("button", { name: /save/i })));
|
|
209
215
|
|
|
@@ -712,7 +718,10 @@ describe("ModelRouter", () => {
|
|
|
712
718
|
|
|
713
719
|
const newInstance = await actions.fullfillModelForm({ model, submit: true });
|
|
714
720
|
|
|
715
|
-
expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem,
|
|
721
|
+
expectToHaveBeenCalledOnceWithMockInstance(onSubmitNewItem, {
|
|
722
|
+
...newInstance,
|
|
723
|
+
carsHistory: [],
|
|
724
|
+
});
|
|
716
725
|
});
|
|
717
726
|
|
|
718
727
|
it("would show a loading indicator when the request is in progress", async () => {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { Meta } from "@storybook/react";
|
|
3
2
|
import { ObjectDetails } from "./object-details";
|
|
4
3
|
import { withPadding } from "../../storybook";
|
|
@@ -15,6 +14,17 @@ export default {
|
|
|
15
14
|
},
|
|
16
15
|
} satisfies Meta<typeof ObjectDetails>;
|
|
17
16
|
|
|
18
|
-
export const Default =
|
|
19
|
-
|
|
17
|
+
export const Default = {
|
|
18
|
+
args: {
|
|
19
|
+
model: mockModel,
|
|
20
|
+
instance: instance,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const Dense = {
|
|
25
|
+
args: {
|
|
26
|
+
model: mockModel,
|
|
27
|
+
instance: instance,
|
|
28
|
+
dense: true,
|
|
29
|
+
},
|
|
20
30
|
};
|
|
@@ -2,9 +2,8 @@ import React from "react";
|
|
|
2
2
|
import { Grid } from "@mui/material";
|
|
3
3
|
import {
|
|
4
4
|
GroupValueCard,
|
|
5
|
-
|
|
5
|
+
ValueItem,
|
|
6
6
|
ValueBoolean,
|
|
7
|
-
ValueCard,
|
|
8
7
|
ValueText,
|
|
9
8
|
ValueDatetime,
|
|
10
9
|
} from "../../components";
|
|
@@ -14,39 +13,103 @@ import {
|
|
|
14
13
|
Model,
|
|
15
14
|
BasicModelInstance,
|
|
16
15
|
GroupInstanceType,
|
|
16
|
+
ArrayGroupField,
|
|
17
|
+
ArrayInstanceType,
|
|
17
18
|
} from "../generators.model";
|
|
19
|
+
import { newBreakpointsCounter } from "~/utils/breakpoints";
|
|
20
|
+
import { DataGrid, GridColDef } from "@mui/x-data-grid";
|
|
21
|
+
import { ValueContent } from "~/components/value-displays/value-content";
|
|
22
|
+
import { ArrayFieldType } from "../generators.model";
|
|
23
|
+
|
|
24
|
+
interface SingleDetailValueFactoryOptions {
|
|
25
|
+
dense?: boolean;
|
|
26
|
+
}
|
|
18
27
|
|
|
19
28
|
const singleDetailValueFactory = <T extends BasicModelInstance>(
|
|
20
29
|
field: ModelField,
|
|
21
30
|
instance: T | GroupInstanceType,
|
|
31
|
+
{ dense }: SingleDetailValueFactoryOptions = {},
|
|
22
32
|
) => {
|
|
23
33
|
const { id, name, type } = field;
|
|
24
34
|
const value = instance[id];
|
|
25
35
|
if (type === "boolean") {
|
|
26
|
-
return <ValueBoolean label={name} value={value as boolean} />;
|
|
36
|
+
return <ValueBoolean dense={dense} label={name} value={value as boolean} />;
|
|
27
37
|
} else if (type === "date" || type === "time" || type === "datetime") {
|
|
28
|
-
return <ValueDatetime label={name} value={value as Date} format={field.format} />;
|
|
38
|
+
return <ValueDatetime dense={dense} label={name} value={value as Date} format={field.format} />;
|
|
29
39
|
}
|
|
30
|
-
return <ValueText label={name} value={value?.toString()} />;
|
|
40
|
+
return <ValueText dense={dense} label={name} value={value?.toString()} />;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
interface ObjectArrayGroupProps {
|
|
44
|
+
field: ArrayGroupField;
|
|
45
|
+
instance: ArrayInstanceType;
|
|
46
|
+
dense?: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const ObjectArrayGroup = ({
|
|
50
|
+
field: { name, description, value },
|
|
51
|
+
instance,
|
|
52
|
+
dense,
|
|
53
|
+
}: ObjectArrayGroupProps) => {
|
|
54
|
+
const columns: GridColDef[] = [{ field: "id", headerName: "ID", width: 70 }];
|
|
55
|
+
|
|
56
|
+
value.forEach((column) => {
|
|
57
|
+
columns.push({
|
|
58
|
+
field: column.id,
|
|
59
|
+
headerName: column.name,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const rows = instance.map((f, id) => ({
|
|
64
|
+
id,
|
|
65
|
+
...f,
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<GroupValueCard title={name} subtitle={description} dense={dense}>
|
|
70
|
+
<Grid item xs={12}>
|
|
71
|
+
<DataGrid
|
|
72
|
+
rows={rows}
|
|
73
|
+
columns={columns}
|
|
74
|
+
density={dense ? "compact" : "standard"}
|
|
75
|
+
disableRowSelectionOnClick
|
|
76
|
+
pageSizeOptions={[5]}
|
|
77
|
+
initialState={{
|
|
78
|
+
pagination: {
|
|
79
|
+
paginationModel: {
|
|
80
|
+
pageSize: 5,
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
}}
|
|
84
|
+
sx={{ height: 400 }}
|
|
85
|
+
/>
|
|
86
|
+
</Grid>
|
|
87
|
+
</GroupValueCard>
|
|
88
|
+
);
|
|
31
89
|
};
|
|
32
90
|
|
|
33
91
|
interface ObjectDetailGroupProps {
|
|
34
92
|
field: GroupField;
|
|
35
93
|
instance: GroupInstanceType;
|
|
94
|
+
dense?: boolean;
|
|
36
95
|
}
|
|
37
96
|
|
|
38
97
|
const ObjectDetailGroup = ({
|
|
39
98
|
field: { name, description, value },
|
|
40
99
|
instance,
|
|
100
|
+
dense,
|
|
41
101
|
}: ObjectDetailGroupProps) => {
|
|
102
|
+
const breakpointsCounter = newBreakpointsCounter();
|
|
103
|
+
|
|
42
104
|
return (
|
|
43
|
-
<GroupValueCard title={name} subtitle={description}>
|
|
105
|
+
<GroupValueCard title={name} subtitle={description} dense={dense}>
|
|
44
106
|
{value.map((field) => {
|
|
45
107
|
const { id, xs, sm, md, lg, xl } = field;
|
|
108
|
+
const bordered = breakpointsCounter.increment(field);
|
|
46
109
|
return (
|
|
47
|
-
<
|
|
48
|
-
{singleDetailValueFactory(field, instance)}
|
|
49
|
-
</
|
|
110
|
+
<ValueItem key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl} bordered={bordered}>
|
|
111
|
+
{singleDetailValueFactory(field, instance, { dense })}
|
|
112
|
+
</ValueItem>
|
|
50
113
|
);
|
|
51
114
|
})}
|
|
52
115
|
</GroupValueCard>
|
|
@@ -55,30 +118,48 @@ const ObjectDetailGroup = ({
|
|
|
55
118
|
|
|
56
119
|
export interface ObjectDetailsProps<T extends BasicModelInstance> {
|
|
57
120
|
model: Model;
|
|
121
|
+
dense?: boolean;
|
|
58
122
|
instance: T;
|
|
59
123
|
}
|
|
60
124
|
|
|
61
125
|
export const ObjectDetails = <T extends BasicModelInstance>({
|
|
62
126
|
model,
|
|
63
127
|
instance,
|
|
128
|
+
dense,
|
|
64
129
|
}: ObjectDetailsProps<T>) => {
|
|
130
|
+
const breakpointsCounter = newBreakpointsCounter();
|
|
65
131
|
return (
|
|
66
|
-
<Grid container spacing={2}>
|
|
132
|
+
<Grid container spacing={dense ? 1 : 2}>
|
|
67
133
|
{model.fields.map((field) => {
|
|
68
|
-
const { id, type, xs = 3, sm, md, lg, xl } = field;
|
|
134
|
+
const { id, type, xs = 3, sm = 0, md = 0, lg = 0, xl = 0 } = field;
|
|
69
135
|
|
|
70
136
|
if (type === "group") {
|
|
137
|
+
breakpointsCounter.increment({ xs: 12 });
|
|
138
|
+
return (
|
|
139
|
+
<Grid item key={id} xs={12}>
|
|
140
|
+
<ObjectDetailGroup
|
|
141
|
+
field={field}
|
|
142
|
+
instance={instance[id] as GroupInstanceType}
|
|
143
|
+
dense={dense}
|
|
144
|
+
/>
|
|
145
|
+
</Grid>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (type === "group[]") {
|
|
150
|
+
breakpointsCounter.increment({ xs: 12 });
|
|
71
151
|
return (
|
|
72
152
|
<Grid item key={id} xs={12}>
|
|
73
|
-
<
|
|
153
|
+
<ObjectArrayGroup field={field} instance={instance[id] as any} dense={dense} />
|
|
74
154
|
</Grid>
|
|
75
155
|
);
|
|
76
156
|
}
|
|
77
157
|
|
|
158
|
+
const bordered = breakpointsCounter.increment(field);
|
|
78
159
|
return (
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
</
|
|
160
|
+
<ValueItem key={id} xs={xs} sm={sm} md={md} lg={lg} xl={xl} bordered={bordered}>
|
|
161
|
+
{singleDetailValueFactory(field, instance, { dense })}
|
|
162
|
+
</ValueItem>
|
|
82
163
|
);
|
|
83
164
|
})}
|
|
84
165
|
</Grid>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import { Meta
|
|
2
|
+
import { Meta } from "@storybook/react";
|
|
3
3
|
import { NotificationCenterProvider } from "./notification-center.provider";
|
|
4
4
|
import { useNotificationCenter } from "./notification-center.context";
|
|
5
5
|
import { Button, Box } from "@mui/material";
|
package/src/tests/assertions.ts
CHANGED
|
@@ -32,7 +32,7 @@ export const expectModelFieldInputExist = (fields: ModelField[]) => {
|
|
|
32
32
|
expect(
|
|
33
33
|
screen.getByRole("button", { name: new RegExp(field.name.toLowerCase(), "i") }),
|
|
34
34
|
).toBeInTheDocument();
|
|
35
|
-
} else {
|
|
35
|
+
} else if (field.type !== "group[]") {
|
|
36
36
|
expect(screen.getByRole("textbox", { name: field.name })).toBeInTheDocument();
|
|
37
37
|
}
|
|
38
38
|
});
|
|
@@ -58,6 +58,12 @@ export const expectModelFieldInputValue = (
|
|
|
58
58
|
fmt: field.format,
|
|
59
59
|
addSpaces: true,
|
|
60
60
|
});
|
|
61
|
+
} else if (field.type === "group[]") {
|
|
62
|
+
// Ignore group[] cases
|
|
63
|
+
} else if (field.type.includes("[]")) {
|
|
64
|
+
expect(screen.getByRole("textbox", { name: field.name })).toHaveValue(
|
|
65
|
+
(value as any[]).join(","),
|
|
66
|
+
);
|
|
61
67
|
} else {
|
|
62
68
|
expect(screen.getByDisplayValue(value.toString())).toBeInTheDocument();
|
|
63
69
|
}
|
|
@@ -166,6 +172,9 @@ export const expectToHaveBeenCalledOnceWithMockInstance = (
|
|
|
166
172
|
calledTradeDate.getSeconds(),
|
|
167
173
|
calledTradeDate.getMilliseconds(),
|
|
168
174
|
),
|
|
175
|
+
codes: instance.codes,
|
|
176
|
+
identifiers: instance.identifiers.map((i) => i.toString()),
|
|
177
|
+
carsHistory: instance.carsHistory,
|
|
169
178
|
});
|
|
170
179
|
};
|
|
171
180
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { newBreakpointsCounter } from "./breakpoints";
|
|
2
|
+
|
|
3
|
+
describe("newBreakpointsCounter", () => {
|
|
4
|
+
//TODO Write some tests to be able to debug/develop faster
|
|
5
|
+
it("should create a new instance with the correct initial values", () => {
|
|
6
|
+
const breakpointsCounter = newBreakpointsCounter();
|
|
7
|
+
|
|
8
|
+
expect(breakpointsCounter.xs).toBe(0);
|
|
9
|
+
expect(breakpointsCounter.sm).toBe(0);
|
|
10
|
+
expect(breakpointsCounter.md).toBe(0);
|
|
11
|
+
expect(breakpointsCounter.lg).toBe(0);
|
|
12
|
+
expect(breakpointsCounter.xl).toBe(0);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe.each([["xs"], ["sm"], ["md"], ["lg"], ["xl"]])("breakpoint %s", (breakpoint: string) => {
|
|
16
|
+
it.each([
|
|
17
|
+
[false, [2, 3, 3, 4, 3]],
|
|
18
|
+
[false, [2, 3, 3, 4, 12, 3]],
|
|
19
|
+
[false, [6, 6, 6, 6, 6, 12, 3]],
|
|
20
|
+
[false, [4, 4, 4, 4, 4, 3, 8]],
|
|
21
|
+
[false, [4, 4, 4, 4, 4, 3, 8]],
|
|
22
|
+
[true, [1, 2]],
|
|
23
|
+
[true, [12, 6, 3]],
|
|
24
|
+
[true, [2, 6, 3]],
|
|
25
|
+
])(
|
|
26
|
+
"should have border=%s if apply %s increments",
|
|
27
|
+
(expectedBorder: boolean, increments: number[]) => {
|
|
28
|
+
const breakpointsCounter = newBreakpointsCounter();
|
|
29
|
+
|
|
30
|
+
let border: Record<string, boolean> = {};
|
|
31
|
+
increments.forEach((increment) => {
|
|
32
|
+
border = breakpointsCounter.increment({ [breakpoint]: increment }) as Record<
|
|
33
|
+
string,
|
|
34
|
+
boolean
|
|
35
|
+
>;
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
expect(border[breakpoint]).toBe(expectedBorder);
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ResponsiveStyleValue } from "@mui/system";
|
|
2
|
+
|
|
3
|
+
interface Breakpoints {
|
|
4
|
+
xs: number;
|
|
5
|
+
sm: number;
|
|
6
|
+
md: number;
|
|
7
|
+
lg: number;
|
|
8
|
+
xl: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface BreakpointsCounter extends Breakpoints {
|
|
12
|
+
increment(breakpoints: Partial<Breakpoints>): ResponsiveStyleValue<boolean>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const newBreakpointsCounter = (breakpoint = 12): BreakpointsCounter => {
|
|
16
|
+
const calculateBreakpointIncrement = (
|
|
17
|
+
breakpointsCounter: BreakpointsCounter,
|
|
18
|
+
key: keyof Breakpoints,
|
|
19
|
+
value: number,
|
|
20
|
+
): boolean => {
|
|
21
|
+
const initialValue = breakpointsCounter[key];
|
|
22
|
+
breakpointsCounter[key] += value;
|
|
23
|
+
if (breakpointsCounter[key] > breakpoint) {
|
|
24
|
+
breakpointsCounter[key] = value;
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (breakpointsCounter[key] == breakpoint) {
|
|
29
|
+
breakpointsCounter[key] = 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return initialValue > 0;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
xs: 0,
|
|
37
|
+
sm: 0,
|
|
38
|
+
md: 0,
|
|
39
|
+
lg: 0,
|
|
40
|
+
xl: 0,
|
|
41
|
+
increment: function ({
|
|
42
|
+
xs = 0,
|
|
43
|
+
sm = 0,
|
|
44
|
+
md = 0,
|
|
45
|
+
lg = 0,
|
|
46
|
+
xl = 0,
|
|
47
|
+
}): ResponsiveStyleValue<boolean> {
|
|
48
|
+
const smInc = sm || xs;
|
|
49
|
+
const mdInc = md || smInc;
|
|
50
|
+
const lgInc = lg || mdInc;
|
|
51
|
+
const xlInc = xl || lgInc;
|
|
52
|
+
|
|
53
|
+
return {
|
|
54
|
+
xs: calculateBreakpointIncrement(this, "xs", xs),
|
|
55
|
+
sm: calculateBreakpointIncrement(this, "sm", smInc),
|
|
56
|
+
md: calculateBreakpointIncrement(this, "md", mdInc),
|
|
57
|
+
lg: calculateBreakpointIncrement(this, "lg", lgInc),
|
|
58
|
+
xl: calculateBreakpointIncrement(this, "xl", xlInc),
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
};
|